Skip to content

Commit 09f315c

Browse files
committed
Adds dynamic layer indexing
1 parent 18045b9 commit 09f315c

File tree

6 files changed

+201
-9
lines changed

6 files changed

+201
-9
lines changed

client/packages/lowcoder/src/comps/comps/preLoadComp/actionConfigs.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@ import {
99
changeLayoutAction,
1010
addEventHandlerAction,
1111
applyStyleAction,
12-
nestComponentAction
12+
nestComponentAction,
13+
updateDynamicLayoutAction
1314
} from "./actions";
1415

1516
export const actionCategories: ActionCategory[] = [
@@ -33,7 +34,7 @@ export const actionCategories: ActionCategory[] = [
3334
{
3435
key: 'layout',
3536
label: 'Layout',
36-
actions: [changeLayoutAction]
37+
actions: [changeLayoutAction, updateDynamicLayoutAction]
3738
},
3839
{
3940
key: 'events',

client/packages/lowcoder/src/comps/comps/preLoadComp/actionInputSection.tsx

Lines changed: 74 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,11 @@ import { default as Space } from "antd/es/space";
1212
import { default as Flex } from "antd/es/flex";
1313
import type { InputRef } from 'antd';
1414
import { default as DownOutlined } from "@ant-design/icons/DownOutlined";
15-
import { BaseSection } from "lowcoder-design";
15+
import { BaseSection, Dropdown } from "lowcoder-design";
1616
import { EditorContext } from "comps/editorState";
1717
import { message } from "antd";
1818
import { CustomDropdown } from "./styled";
19-
import { generateComponentActionItems, getComponentCategories } from "./utils";
19+
import { generateComponentActionItems, getComponentCategories, getEditorComponentInfo, getLayoutItemsOrder } from "./utils";
2020
import { actionRegistry, getAllActionItems } from "./actionConfigs";
2121

2222
export function ActionInputSection() {
@@ -31,6 +31,8 @@ export function ActionInputSection() {
3131
const [showStylingInput, setShowStylingInput] = useState<boolean>(false);
3232
const [selectedEditorComponent, setSelectedEditorComponent] = useState<string | null>(null);
3333
const [validationError, setValidationError] = useState<string | null>(null);
34+
const [showDynamicLayoutDropdown, setShowDynamicLayoutDropdown] = useState<boolean>(false);
35+
const [selectedDynamicLayoutIndex, setSelectedDynamicLayoutIndex] = useState<string | null>(null);
3436
const inputRef = useRef<InputRef>(null);
3537
const editorState = useContext(EditorContext);
3638

@@ -56,6 +58,25 @@ export function ActionInputSection() {
5658
}));
5759
}, [editorState]);
5860

61+
const simpleLayoutItems = useMemo(() => {
62+
if(!editorComponents) return [];
63+
64+
const editorComponentInfo = getEditorComponentInfo(editorState);
65+
if(!editorComponentInfo) return [];
66+
67+
const currentLayout = editorComponentInfo.currentLayout;
68+
const items = editorComponentInfo.items;
69+
70+
return Object.keys(currentLayout).map((key) => {
71+
const item = items ? items[key] : null;
72+
const componentName = item ? (item as any).children.name.getView() : key;
73+
return {
74+
label: componentName,
75+
key: componentName
76+
};
77+
});
78+
}, [editorState]);
79+
5980
const currentAction = useMemo(() => {
6081
return selectedActionKey ? actionRegistry.get(selectedActionKey) : null;
6182
}, [selectedActionKey]);
@@ -81,7 +102,9 @@ export function ActionInputSection() {
81102
setSelectedEditorComponent(null);
82103
setIsNestedComponent(false);
83104
setSelectedNestComponent(null);
105+
setShowDynamicLayoutDropdown(false);
84106
setActionValue("");
107+
setSelectedDynamicLayoutIndex(null);
85108

86109
if (action.requiresComponentSelection) {
87110
setShowComponentDropdown(true);
@@ -103,6 +126,9 @@ export function ActionInputSection() {
103126
if (action.isNested) {
104127
setIsNestedComponent(true);
105128
}
129+
if(action.dynamicLayout) {
130+
setShowDynamicLayoutDropdown(true);
131+
}
106132
}, []);
107133

108134
const handleComponentSelection = useCallback((key: string) => {
@@ -175,6 +201,7 @@ export function ActionInputSection() {
175201
selectedComponent,
176202
selectedEditorComponent,
177203
selectedNestComponent,
204+
selectedDynamicLayoutIndex,
178205
editorState
179206
});
180207

@@ -189,6 +216,8 @@ export function ActionInputSection() {
189216
setValidationError(null);
190217
setIsNestedComponent(false);
191218
setSelectedNestComponent(null);
219+
setShowDynamicLayoutDropdown(false);
220+
setSelectedDynamicLayoutIndex(null);
192221

193222
} catch (error) {
194223
console.error('Error executing action:', error);
@@ -200,6 +229,7 @@ export function ActionInputSection() {
200229
selectedComponent,
201230
selectedEditorComponent,
202231
selectedNestComponent,
232+
selectedDynamicLayoutIndex,
203233
editorState,
204234
currentAction,
205235
validateInput
@@ -299,7 +329,7 @@ export function ActionInputSection() {
299329
popupRender={() => (
300330
<Menu
301331
items={editorComponents}
302-
onClick={({ key }) => {
332+
onClick={({key}) => {
303333
handleEditorComponentSelection(key);
304334
}}
305335
/>
@@ -314,6 +344,47 @@ export function ActionInputSection() {
314344
</CustomDropdown>
315345
)}
316346

347+
{showDynamicLayoutDropdown && (
348+
<CustomDropdown
349+
overlayStyle={{
350+
maxHeight: '400px',
351+
overflow: 'auto',
352+
zIndex: 9999
353+
}}
354+
popupRender={() => (
355+
<Menu
356+
items={simpleLayoutItems}
357+
onClick={({key}) => {
358+
handleEditorComponentSelection(key);
359+
}}
360+
/>
361+
)}
362+
>
363+
<Button size={"small"}>
364+
<Space>
365+
{selectedEditorComponent ? selectedEditorComponent : 'Layout'}
366+
<DownOutlined />
367+
</Space>
368+
</Button>
369+
</CustomDropdown>
370+
)}
371+
372+
{showDynamicLayoutDropdown && (
373+
<Dropdown
374+
options={getLayoutItemsOrder(simpleLayoutItems)}
375+
onChange={(value) => {
376+
setSelectedDynamicLayoutIndex(value);
377+
}}
378+
>
379+
<Button size={"small"}>
380+
<Space>
381+
{selectedEditorComponent ? selectedEditorComponent : 'Layout'}
382+
<DownOutlined />
383+
</Space>
384+
</Button>
385+
</Dropdown>
386+
)}
387+
317388
{shouldShowInput && (
318389
showStylingInput ? (
319390
<Input.TextArea

client/packages/lowcoder/src/comps/comps/preLoadComp/actions/componentLayout.ts

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import { message } from "antd";
22
import { ActionConfig, ActionExecuteParams } from "../types";
3+
import { getEditorComponentInfo } from "../utils";
4+
import { changeValueAction, multiChangeAction, wrapActionExtraInfo } from "lowcoder-core";
35

46
export const changeLayoutAction: ActionConfig = {
57
key: 'change-layout',
@@ -16,4 +18,101 @@ export const changeLayoutAction: ActionConfig = {
1618

1719
// TODO: Implement actual layout change logic
1820
}
21+
};
22+
23+
export const updateDynamicLayoutAction: ActionConfig = {
24+
key: 'update-dynamic-layout',
25+
label: 'Update Dynamic Layout',
26+
category: 'layout',
27+
requiresInput: false,
28+
dynamicLayout: true,
29+
execute: async (params: ActionExecuteParams) => {
30+
const { selectedDynamicLayoutIndex, selectedEditorComponent, editorState } = params;
31+
32+
if (!selectedEditorComponent || !editorState || !selectedDynamicLayoutIndex) {
33+
message.error('Component, editor state, and layout index are required');
34+
return;
35+
}
36+
37+
try {
38+
const componentInfo = getEditorComponentInfo(editorState, selectedEditorComponent);
39+
40+
if (!componentInfo) {
41+
message.error(`Component "${selectedEditorComponent}" not found`);
42+
return;
43+
}
44+
45+
const { componentKey, currentLayout, simpleContainer, items } = componentInfo;
46+
47+
if (!componentKey || !currentLayout[componentKey]) {
48+
message.error(`Component "${selectedEditorComponent}" not found in layout`);
49+
return;
50+
}
51+
52+
const currentLayoutItem = currentLayout[componentKey];
53+
const newPos = parseInt(selectedDynamicLayoutIndex);
54+
55+
if (isNaN(newPos)) {
56+
message.error('Invalid layout index provided');
57+
return;
58+
}
59+
60+
const currentPos = currentLayoutItem.pos || 0;
61+
const layoutItems = Object.entries(currentLayout).map(([key, item]) => ({
62+
key,
63+
item: item as any,
64+
pos: (item as any).pos || 0
65+
})).sort((a, b) => a.pos - b.pos);
66+
67+
const otherItems = layoutItems.filter(item => item.key !== componentKey);
68+
const newLayout: any = {};
69+
70+
newLayout[componentKey] = {
71+
...currentLayoutItem,
72+
pos: newPos
73+
};
74+
75+
// Update other components with shifted positions
76+
otherItems.forEach((item) => {
77+
let adjustedPos = item.pos;
78+
79+
// If moving to a position before the current position, shift items in between
80+
if (newPos < currentPos && item.pos >= newPos && item.pos < currentPos) {
81+
adjustedPos = item.pos + 1;
82+
}
83+
// If moving to a position after the current position, shift items in between
84+
else if (newPos > currentPos && item.pos > currentPos && item.pos <= newPos) {
85+
adjustedPos = item.pos - 1;
86+
}
87+
88+
newLayout[item.key] = {
89+
...item.item,
90+
pos: adjustedPos
91+
};
92+
});
93+
94+
simpleContainer.dispatch(
95+
wrapActionExtraInfo(
96+
multiChangeAction({
97+
layout: changeValueAction(newLayout, true),
98+
}),
99+
{
100+
compInfos: [{
101+
compName: selectedEditorComponent,
102+
compType: (items[componentKey] as any).children.compType.getView(),
103+
type: "layout"
104+
}]
105+
}
106+
)
107+
);
108+
109+
editorState.setSelectedCompNames(new Set([selectedEditorComponent]), "layoutComp");
110+
111+
message.success(`Component "${selectedEditorComponent}" moved to position ${newPos}`);
112+
113+
} catch (error) {
114+
console.error('Error updating dynamic layout:', error);
115+
message.error('Failed to update dynamic layout. Please try again.');
116+
}
117+
}
19118
};

client/packages/lowcoder/src/comps/comps/preLoadComp/actions/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ export * from './componentManagement';
55
export { configureComponentAction } from './componentConfiguration';
66

77
// Layout Actions
8-
export { changeLayoutAction } from './componentLayout';
8+
export { changeLayoutAction, updateDynamicLayoutAction } from './componentLayout';
99

1010
// Event Actions
1111
export { addEventHandlerAction } from './componentEvents';

client/packages/lowcoder/src/comps/comps/preLoadComp/types.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ export interface ActionConfig {
3535
requiresInput?: boolean;
3636
requiresStyle?: boolean;
3737
isNested?: boolean;
38+
dynamicLayout?: boolean;
3839
inputPlaceholder?: string;
3940
inputType?: 'text' | 'number' | 'textarea' | 'json';
4041
validation?: (value: string) => string | null;
@@ -47,6 +48,7 @@ export interface ActionExecuteParams {
4748
selectedComponent: string | null;
4849
selectedEditorComponent: string | null;
4950
selectedNestComponent: string | null;
51+
selectedDynamicLayoutIndex: string | null;
5052
editorState: any;
5153
}
5254

client/packages/lowcoder/src/comps/comps/preLoadComp/utils.ts

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ export function getComponentCategories() {
5454
});
5555
return cats;
5656
}
57-
export function getEditorComponentInfo(editorState: EditorState, componentName: string): {
57+
export function getEditorComponentInfo(editorState: EditorState, componentName?: string): {
5858
componentKey: string | null;
5959
currentLayout: any;
6060
simpleContainer: any;
@@ -63,7 +63,7 @@ export function getEditorComponentInfo(editorState: EditorState, componentName:
6363
} | null {
6464
try {
6565
// Get the UI component container
66-
if (!editorState || !componentName) {
66+
if (!editorState) {
6767
return null;
6868
}
6969

@@ -85,6 +85,16 @@ export function getEditorComponentInfo(editorState: EditorState, componentName:
8585
const currentLayout = simpleContainer.children.layout.getView();
8686
const items = getCombinedItems(uiCompTree);
8787

88+
// If no componentName is provided, return all items
89+
if (!componentName) {
90+
return {
91+
componentKey: null,
92+
currentLayout,
93+
simpleContainer,
94+
items,
95+
};
96+
}
97+
8898
// Find the component by name and get its key
8999
let componentKey: string | null = null;
90100
let componentType: string | null = null;
@@ -93,7 +103,7 @@ export function getEditorComponentInfo(editorState: EditorState, componentName:
93103
if ((item as any).children.name.getView() === componentName) {
94104
componentKey = key;
95105
componentType = (item as any).children.compType.getView();
96-
break
106+
break;
97107
}
98108
}
99109

@@ -137,3 +147,12 @@ function getCombinedItems(uiCompTree: any) {
137147

138148
return combined;
139149
}
150+
151+
export function getLayoutItemsOrder(layoutItems: any[]){
152+
const maxIndex = layoutItems.length;
153+
return Array.from({ length: maxIndex }, (_, index) => ({
154+
key: index,
155+
label: `Position ${index}`,
156+
value: index.toString()
157+
}));
158+
}

0 commit comments

Comments
 (0)