Skip to content

Commit 62e1afa

Browse files
Merge pull request #2038 from iamfaran/fix/2021-nav-dropdown
Add Map Mode for NavComponent
2 parents 0e5aa97 + 1d86562 commit 62e1afa

File tree

4 files changed

+156
-29
lines changed

4 files changed

+156
-29
lines changed
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
import { BoolCodeControl, StringControl } from "comps/controls/codeControl";
2+
import { clickEvent, eventHandlerControl } from "comps/controls/eventHandlerControl";
3+
import { MultiCompBuilder } from "comps/generators/multi";
4+
import { dropdownControl } from "comps/controls/dropdownControl";
5+
import { mapOptionsControl } from "comps/controls/optionsControl";
6+
import { trans } from "i18n";
7+
import { navListComp } from "../navItemComp";
8+
import { controlItem } from "lowcoder-design";
9+
import { menuPropertyView } from "./MenuItemList";
10+
11+
export function createNavItemsControl() {
12+
const OptionTypes = [
13+
{ label: trans("prop.manual"), value: "manual" },
14+
{ label: trans("prop.map"), value: "map" },
15+
] as const;
16+
17+
const NavMapOption = new MultiCompBuilder(
18+
{
19+
label: StringControl,
20+
hidden: BoolCodeControl,
21+
disabled: BoolCodeControl,
22+
active: BoolCodeControl,
23+
onEvent: eventHandlerControl([clickEvent]),
24+
},
25+
(props) => props
26+
)
27+
.setPropertyViewFn((children) => (
28+
<>
29+
{children.label.propertyView({ label: trans("label"), placeholder: "{{item}}" })}
30+
{children.active.propertyView({ label: trans("navItemComp.active") })}
31+
{children.hidden.propertyView({ label: trans("hidden") })}
32+
{children.disabled.propertyView({ label: trans("disabled") })}
33+
{children.onEvent.getPropertyView()}
34+
</>
35+
))
36+
.build();
37+
38+
const TmpNavItemsControl = new MultiCompBuilder(
39+
{
40+
optionType: dropdownControl(OptionTypes, "manual"),
41+
manual: navListComp(),
42+
mapData: mapOptionsControl(NavMapOption),
43+
},
44+
(props) => {
45+
return props.optionType === "manual" ? props.manual : props.mapData;
46+
}
47+
)
48+
.setPropertyViewFn(() => {
49+
throw new Error("Method not implemented.");
50+
})
51+
.build();
52+
53+
return class NavItemsControl extends TmpNavItemsControl {
54+
exposingNode() {
55+
return this.children.optionType.getView() === "manual"
56+
? (this.children.manual as any).exposingNode()
57+
: (this.children.mapData as any).exposingNode();
58+
}
59+
60+
propertyView() {
61+
const isManual = this.children.optionType.getView() === "manual";
62+
const content = isManual
63+
? menuPropertyView(this.children.manual as any)
64+
: this.children.mapData.getPropertyView();
65+
66+
return controlItem(
67+
{ searchChild: true },
68+
<>
69+
{this.children.optionType.propertyView({ radioButton: true, type: "oneline" })}
70+
{content}
71+
</>
72+
);
73+
}
74+
};
75+
}
76+
77+

client/packages/lowcoder/src/comps/comps/navComp/navComp.tsx

Lines changed: 70 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
import { NameConfig, NameConfigHidden, withExposingConfigs } from "comps/generators/withExposing";
2+
import { MultiCompBuilder } from "comps/generators/multi";
23
import { UICompBuilder, withDefault } from "comps/generators";
34
import { Section, sectionNames } from "lowcoder-design";
45
import styled from "styled-components";
56
import { clickEvent, eventHandlerControl } from "comps/controls/eventHandlerControl";
6-
import { StringControl } from "comps/controls/codeControl";
7+
import { BoolCodeControl, StringControl } from "comps/controls/codeControl";
78
import { alignWithJustifyControl } from "comps/controls/alignControl";
89
import { navListComp } from "./navItemComp";
910
import { menuPropertyView } from "./components/MenuItemList";
@@ -22,6 +23,8 @@ import { trans } from "i18n";
2223

2324
import { useContext } from "react";
2425
import { EditorContext } from "comps/editorState";
26+
import { controlItem } from "lowcoder-design";
27+
import { createNavItemsControl } from "./components/NavItemsControl";
2528

2629
type IProps = {
2730
$justify: boolean;
@@ -63,11 +66,12 @@ const Item = styled.div<{
6366
$padding: string;
6467
$textTransform:string;
6568
$textDecoration:string;
69+
$disabled?: boolean;
6670
}>`
6771
height: 30px;
6872
line-height: 30px;
6973
padding: ${(props) => props.$padding ? props.$padding : '0 16px'};
70-
color: ${(props) => (props.$active ? props.$activeColor : props.$color)};
74+
color: ${(props) => props.$disabled ? `${props.$color}80` : (props.$active ? props.$activeColor : props.$color)};
7175
font-weight: ${(props) => (props.$textWeight ? props.$textWeight : 500)};
7276
font-family:${(props) => (props.$fontFamily ? props.$fontFamily : 'sans-serif')};
7377
font-style:${(props) => (props.$fontStyle ? props.$fontStyle : 'normal')};
@@ -77,8 +81,8 @@ const Item = styled.div<{
7781
margin:${(props) => props.$margin ? props.$margin : '0px'};
7882
7983
&:hover {
80-
color: ${(props) => props.$activeColor};
81-
cursor: pointer;
84+
color: ${(props) => props.$disabled ? (props.$active ? props.$activeColor : props.$color) : props.$activeColor};
85+
cursor: ${(props) => props.$disabled ? 'not-allowed' : 'pointer'};
8286
}
8387
8488
.anticon {
@@ -131,41 +135,74 @@ function fixOldStyleData(oldData: any) {
131135
return oldData;
132136
}
133137

138+
function fixOldItemsData(oldData: any) {
139+
if (Array.isArray(oldData)) {
140+
return {
141+
optionType: "manual",
142+
manual: oldData,
143+
};
144+
}
145+
if (oldData && !oldData.optionType && Array.isArray(oldData.manual)) {
146+
return {
147+
optionType: "manual",
148+
manual: oldData.manual,
149+
};
150+
}
151+
return oldData;
152+
}
153+
134154
const childrenMap = {
135155
logoUrl: StringControl,
136156
logoEvent: withDefault(eventHandlerControl(logoEventHandlers), [{ name: "click" }]),
137157
horizontalAlignment: alignWithJustifyControl(),
138158
style: migrateOldData(styleControl(NavigationStyle, 'style'), fixOldStyleData),
139159
animationStyle: styleControl(AnimationStyle, 'animationStyle'),
140-
items: withDefault(navListComp(), [
141-
{
142-
label: trans("menuItem") + " 1",
143-
},
144-
]),
160+
items: withDefault(migrateOldData(createNavItemsControl(), fixOldItemsData), {
161+
optionType: "manual",
162+
manual: [
163+
{
164+
label: trans("menuItem") + " 1",
165+
},
166+
],
167+
}),
145168
};
146169

147170
const NavCompBase = new UICompBuilder(childrenMap, (props) => {
148171
const data = props.items;
149172
const items = (
150173
<>
151-
{data.map((menuItem, idx) => {
152-
const { hidden, label, items, active, onEvent } = menuItem.getView();
174+
{data.map((menuItem: any, idx: number) => {
175+
const isCompItem = typeof menuItem?.getView === "function";
176+
const view = isCompItem ? menuItem.getView() : menuItem;
177+
const hidden = !!view?.hidden;
153178
if (hidden) {
154179
return null;
155180
}
156-
const subMenuItems: Array<{ key: string; label: string }> = [];
181+
182+
const label = view?.label;
183+
const active = !!view?.active;
184+
const onEvent = view?.onEvent;
185+
const disabled = !!view?.disabled;
186+
const subItems = isCompItem ? view?.items : [];
187+
188+
const subMenuItems: Array<{ key: string; label: any; disabled?: boolean }> = [];
157189
const subMenuSelectedKeys: Array<string> = [];
158-
items.forEach((subItem, originalIndex) => {
159-
if (subItem.children.hidden.getView()) {
160-
return;
161-
}
162-
const key = originalIndex + "";
163-
subItem.children.active.getView() && subMenuSelectedKeys.push(key);
164-
subMenuItems.push({
165-
key: key,
166-
label: subItem.children.label.getView(),
190+
191+
if (Array.isArray(subItems)) {
192+
subItems.forEach((subItem: any, originalIndex: number) => {
193+
if (subItem.children.hidden.getView()) {
194+
return;
195+
}
196+
const key = originalIndex + "";
197+
subItem.children.active.getView() && subMenuSelectedKeys.push(key);
198+
subMenuItems.push({
199+
key: key,
200+
label: subItem.children.label.getView(),
201+
disabled: !!subItem.children.disabled.getView(),
202+
});
167203
});
168-
});
204+
}
205+
169206
const item = (
170207
<Item
171208
key={idx}
@@ -180,18 +217,23 @@ const NavCompBase = new UICompBuilder(childrenMap, (props) => {
180217
$textTransform={props.style.textTransform}
181218
$textDecoration={props.style.textDecoration}
182219
$margin={props.style.margin}
183-
onClick={() => onEvent("click")}
220+
$disabled={disabled}
221+
onClick={() => { if (!disabled && onEvent) onEvent("click"); }}
184222
>
185223
{label}
186-
{items.length > 0 && <DownOutlined />}
224+
{Array.isArray(subItems) && subItems.length > 0 && <DownOutlined />}
187225
</Item>
188226
);
189227
if (subMenuItems.length > 0) {
190228
const subMenu = (
191229
<StyledMenu
192230
onClick={(e) => {
193-
const { onEvent: onSubEvent } = items[Number(e.key)]?.getView();
194-
onSubEvent("click");
231+
if (disabled) return;
232+
const subItem = subItems[Number(e.key)];
233+
const isSubDisabled = !!subItem?.children?.disabled?.getView?.();
234+
if (isSubDisabled) return;
235+
const onSubEvent = subItem?.getView()?.onEvent;
236+
onSubEvent && onSubEvent("click");
195237
}}
196238
selectedKeys={subMenuSelectedKeys}
197239
items={subMenuItems}
@@ -201,6 +243,7 @@ const NavCompBase = new UICompBuilder(childrenMap, (props) => {
201243
<Dropdown
202244
key={idx}
203245
popupRender={() => subMenu}
246+
disabled={disabled}
204247
>
205248
{item}
206249
</Dropdown>
@@ -237,7 +280,7 @@ const NavCompBase = new UICompBuilder(childrenMap, (props) => {
237280
return (
238281
<>
239282
<Section name={sectionNames.basic}>
240-
{menuPropertyView(children.items)}
283+
{children.items.propertyView()}
241284
</Section>
242285

243286
{(useContext(EditorContext).editorModeStatus === "logic" || useContext(EditorContext).editorModeStatus === "both") && (

client/packages/lowcoder/src/comps/comps/navComp/navItemComp.tsx

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { clickEvent, eventHandlerControl } from "comps/controls/eventHandlerCont
33
import { list } from "comps/generators/list";
44
import { parseChildrenFromValueAndChildrenMap, ToViewReturn } from "comps/generators/multi";
55
import { withDefault } from "comps/generators/simpleGenerators";
6-
import { hiddenPropertyView } from "comps/utils/propertyUtils";
6+
import { disabledPropertyView, hiddenPropertyView } from "comps/utils/propertyUtils";
77
import { trans } from "i18n";
88
import _ from "lodash";
99
import { fromRecord, MultiBaseComp, Node, RecordNode, RecordNodeToValue } from "lowcoder-core";
@@ -14,6 +14,7 @@ const events = [clickEvent];
1414
const childrenMap = {
1515
label: StringControl,
1616
hidden: BoolCodeControl,
17+
disabled: BoolCodeControl,
1718
active: BoolCodeControl,
1819
onEvent: withDefault(eventHandlerControl(events), [
1920
{
@@ -29,6 +30,7 @@ const childrenMap = {
2930
type ChildrenType = {
3031
label: InstanceType<typeof StringControl>;
3132
hidden: InstanceType<typeof BoolCodeControl>;
33+
disabled: InstanceType<typeof BoolCodeControl>;
3234
active: InstanceType<typeof BoolCodeControl>;
3335
onEvent: InstanceType<ReturnType<typeof eventHandlerControl>>;
3436
items: InstanceType<ReturnType<typeof navListComp>>;
@@ -45,6 +47,7 @@ export class NavItemComp extends MultiBaseComp<ChildrenType> {
4547
{this.children.label.propertyView({ label: trans("label") })}
4648
{hiddenPropertyView(this.children)}
4749
{this.children.active.propertyView({ label: trans("navItemComp.active") })}
50+
{disabledPropertyView(this.children)}
4851
{this.children.onEvent.propertyView({ inline: true })}
4952
</>
5053
);
@@ -69,6 +72,7 @@ export class NavItemComp extends MultiBaseComp<ChildrenType> {
6972
return fromRecord({
7073
label: this.children.label.exposingNode(),
7174
hidden: this.children.hidden.exposingNode(),
75+
disabled: this.children.disabled.exposingNode(),
7276
active: this.children.active.exposingNode(),
7377
items: this.children.items.exposingNode(),
7478
});
@@ -78,6 +82,7 @@ export class NavItemComp extends MultiBaseComp<ChildrenType> {
7882
type NavItemExposing = {
7983
label: Node<string>;
8084
hidden: Node<boolean>;
85+
disabled: Node<boolean>;
8186
active: Node<boolean>;
8287
items: Node<RecordNodeToValue<NavItemExposing>[]>;
8388
};

client/packages/lowcoder/src/i18n/locales/en.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ export const en = {
2626
"text": "Text",
2727
"basic": "Basic",
2828
"label": "Label",
29+
"hidden": "Hidden",
30+
"disabled": "Disabled",
2931
"layout": "Layout",
3032
"color": "Color",
3133
"form": "Form",
@@ -3204,7 +3206,7 @@ export const en = {
32043206
"logoURL": "Navigation Logo URL",
32053207
"horizontalAlignment": "Horizontal Alignment",
32063208
"logoURLDesc": "You can display a Logo on the left side by entering URI Value or Base64 String like data:image/png;base64,AAA... CCC",
3207-
"itemsDesc": "Hierarchical Navigation Menu Items"
3209+
"itemsDesc": "Menu Items"
32083210
},
32093211
"droppadbleMenuItem": {
32103212
"subMenu": "Submenu {number}"

0 commit comments

Comments
 (0)