Skip to content

Commit 734b320

Browse files
Merge pull request #1846 from kamalqureshi/actions_js_console
Actions for JS Console
2 parents e14a7e7 + 18045b9 commit 734b320

File tree

9 files changed

+297
-37
lines changed

9 files changed

+297
-37
lines changed

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

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,8 @@ import {
88
configureComponentAction,
99
changeLayoutAction,
1010
addEventHandlerAction,
11-
applyStyleAction
11+
applyStyleAction,
12+
nestComponentAction
1213
} from "./actions";
1314

1415
export const actionCategories: ActionCategory[] = [
@@ -20,7 +21,8 @@ export const actionCategories: ActionCategory[] = [
2021
moveComponentAction,
2122
deleteComponentAction,
2223
resizeComponentAction,
23-
renameComponentAction
24+
renameComponentAction,
25+
nestComponentAction
2426
]
2527
},
2628
{

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

Lines changed: 60 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,10 @@ export function ActionInputSection() {
2525
const [placeholderText, setPlaceholderText] = useState<string>("");
2626
const [selectedComponent, setSelectedComponent] = useState<string | null>(null);
2727
const [showComponentDropdown, setShowComponentDropdown] = useState<boolean>(false);
28+
const [isNestedComponent, setIsNestedComponent] = useState<boolean>(false);
29+
const [selectedNestComponent, setSelectedNestComponent] = useState<string | null>(null);
2830
const [showEditorComponentsDropdown, setShowEditorComponentsDropdown] = useState<boolean>(false);
31+
const [showStylingInput, setShowStylingInput] = useState<boolean>(false);
2932
const [selectedEditorComponent, setSelectedEditorComponent] = useState<string | null>(null);
3033
const [validationError, setValidationError] = useState<string | null>(null);
3134
const inputRef = useRef<InputRef>(null);
@@ -73,44 +76,55 @@ export function ActionInputSection() {
7376

7477
setShowComponentDropdown(false);
7578
setShowEditorComponentsDropdown(false);
79+
setShowStylingInput(false);
7680
setSelectedComponent(null);
7781
setSelectedEditorComponent(null);
82+
setIsNestedComponent(false);
83+
setSelectedNestComponent(null);
7884
setActionValue("");
7985

8086
if (action.requiresComponentSelection) {
8187
setShowComponentDropdown(true);
8288
setPlaceholderText("Select a component to add");
83-
} else if (action.requiresEditorComponentSelection) {
89+
}
90+
if (action.requiresEditorComponentSelection) {
8491
setShowEditorComponentsDropdown(true);
8592
setPlaceholderText(`Select a component to ${action.label.toLowerCase()}`);
86-
} else if (action.requiresInput) {
93+
}
94+
if (action.requiresInput) {
8795
setPlaceholderText(action.inputPlaceholder || `Enter ${action.label.toLowerCase()} value`);
8896
} else {
8997
setPlaceholderText(`Execute ${action.label.toLowerCase()}`);
9098
}
99+
if (action.requiresStyle) {
100+
setShowStylingInput(true);
101+
setPlaceholderText(`Select a component to style`);
102+
}
103+
if (action.isNested) {
104+
setIsNestedComponent(true);
105+
}
91106
}, []);
92107

93108
const handleComponentSelection = useCallback((key: string) => {
94109
if (key.startsWith('comp-')) {
95110
const compName = key.replace('comp-', '');
96-
setSelectedComponent(compName);
111+
isNestedComponent ? setSelectedNestComponent(compName) : setSelectedComponent(compName);
97112
setPlaceholderText(`Configure ${compName} component`);
98113
}
99-
}, []);
114+
}, [isNestedComponent]);
100115

101116
const handleEditorComponentSelection = useCallback((key: string) => {
102117
setSelectedEditorComponent(key);
103-
if (currentAction) {
104-
setPlaceholderText(`${currentAction.label}`);
105-
}
118+
setPlaceholderText(`${currentAction?.label}`);
106119
}, [currentAction]);
107120

121+
108122
const validateInput = useCallback((value: string): string | null => {
109123
if (!currentAction?.validation) return null;
110124
return currentAction.validation(value);
111125
}, [currentAction]);
112126

113-
const handleInputChange = useCallback((e: React.ChangeEvent<HTMLInputElement>) => {
127+
const handleInputChange = useCallback((e: React.ChangeEvent<HTMLTextAreaElement | HTMLInputElement>) => {
114128
const value = e.target.value;
115129
setActionValue(value);
116130

@@ -149,12 +163,18 @@ export function ActionInputSection() {
149163
return;
150164
}
151165

166+
if(currentAction.isNested && !selectedNestComponent) {
167+
message.error('Please select a component to nest');
168+
return;
169+
}
170+
152171
try {
153172
await currentAction.execute({
154173
actionKey: selectedActionKey,
155174
actionValue,
156175
selectedComponent,
157176
selectedEditorComponent,
177+
selectedNestComponent,
158178
editorState
159179
});
160180

@@ -167,6 +187,8 @@ export function ActionInputSection() {
167187
setSelectedEditorComponent(null);
168188
setPlaceholderText("");
169189
setValidationError(null);
190+
setIsNestedComponent(false);
191+
setSelectedNestComponent(null);
170192

171193
} catch (error) {
172194
console.error('Error executing action:', error);
@@ -177,6 +199,7 @@ export function ActionInputSection() {
177199
actionValue,
178200
selectedComponent,
179201
selectedEditorComponent,
202+
selectedNestComponent,
180203
editorState,
181204
currentAction,
182205
validateInput
@@ -235,7 +258,7 @@ export function ActionInputSection() {
235258
</Button>
236259
</CustomDropdown>
237260

238-
{showComponentDropdown && (
261+
{(showComponentDropdown || isNestedComponent) && (
239262
<CustomDropdown
240263
overlayStyle={{
241264
maxHeight: '400px',
@@ -253,7 +276,13 @@ export function ActionInputSection() {
253276
>
254277
<Button size={"small"}>
255278
<Space>
256-
{selectedComponent ? selectedComponent : 'Select Component'}
279+
{
280+
selectedComponent
281+
? selectedComponent
282+
: selectedNestComponent
283+
? selectedNestComponent
284+
: 'New Component'
285+
}
257286
<DownOutlined />
258287
</Space>
259288
</Button>
@@ -278,23 +307,34 @@ export function ActionInputSection() {
278307
>
279308
<Button size={"small"}>
280309
<Space>
281-
{selectedEditorComponent ? selectedEditorComponent : 'Select Component'}
310+
{selectedEditorComponent ? selectedEditorComponent : 'Editor Component'}
282311
<DownOutlined />
283312
</Space>
284313
</Button>
285314
</CustomDropdown>
286315
)}
287-
316+
288317
{shouldShowInput && (
289-
<Input
290-
ref={inputRef}
291-
value={actionValue}
292-
onChange={handleInputChange}
293-
placeholder={placeholderText}
294-
status={validationError ? 'error' : undefined}
295-
/>
318+
showStylingInput ? (
319+
<Input.TextArea
320+
ref={inputRef}
321+
value={actionValue}
322+
onChange={handleInputChange}
323+
placeholder={placeholderText}
324+
status={validationError ? 'error' : undefined}
325+
autoSize={{ minRows: 1 }}
326+
/>
327+
) : (
328+
<Input
329+
ref={inputRef}
330+
value={actionValue}
331+
onChange={handleInputChange}
332+
placeholder={placeholderText}
333+
status={validationError ? 'error' : undefined}
334+
/>
335+
)
296336
)}
297-
337+
298338
{validationError && (
299339
<div style={{ color: '#ff4d4f', fontSize: '12px', marginTop: '-8px' }}>
300340
{validationError}

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

Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,125 @@ export const addComponentAction: ActionConfig = {
108108
}
109109
};
110110

111+
export const nestComponentAction: ActionConfig = {
112+
key: 'nest-components',
113+
label: 'Nest a component',
114+
category: 'component-management',
115+
requiresEditorComponentSelection: true,
116+
requiresInput: false,
117+
isNested: true,
118+
execute: async (params: ActionExecuteParams) => {
119+
const { selectedEditorComponent, selectedNestComponent, editorState } = params;
120+
121+
if (!selectedEditorComponent || !selectedNestComponent || !editorState) {
122+
message.error('Parent component, child component, and editor state are required');
123+
return;
124+
}
125+
126+
const parentComponentInfo = getEditorComponentInfo(editorState, selectedEditorComponent);
127+
128+
if (!parentComponentInfo) {
129+
message.error(`Parent component "${selectedEditorComponent}" not found`);
130+
return;
131+
}
132+
133+
const { componentKey: parentKey, items } = parentComponentInfo;
134+
135+
if (!parentKey) {
136+
message.error(`Parent component "${selectedEditorComponent}" not found in layout`);
137+
return;
138+
}
139+
140+
const parentItem = items[parentKey];
141+
if (!parentItem) {
142+
message.error(`Parent component "${selectedEditorComponent}" not found in items`);
143+
return;
144+
}
145+
146+
// Check if parent is a container
147+
const parentCompType = parentItem.children.compType.getView();
148+
const parentManifest = uiCompRegistry[parentCompType];
149+
150+
if (!parentManifest?.isContainer) {
151+
message.error(`Component "${selectedEditorComponent}" is not a container and cannot nest components`);
152+
return;
153+
}
154+
155+
try {
156+
157+
const nameGenerator = editorState.getNameGenerator();
158+
const compInfo = parseCompType(selectedNestComponent);
159+
const compName = nameGenerator.genItemName(compInfo.compName);
160+
const key = genRandomKey();
161+
162+
const manifest = uiCompRegistry[selectedNestComponent];
163+
let defaultDataFn = undefined;
164+
165+
if (manifest?.lazyLoad) {
166+
const { defaultDataFnName, defaultDataFnPath } = manifest;
167+
if (defaultDataFnName && defaultDataFnPath) {
168+
const module = await import(`../../../${defaultDataFnPath}.tsx`);
169+
defaultDataFn = module[defaultDataFnName];
170+
}
171+
} else if (!compInfo.isRemote) {
172+
defaultDataFn = manifest?.defaultDataFn;
173+
}
174+
175+
const widgetValue: GridItemDataType = {
176+
compType: selectedNestComponent,
177+
name: compName,
178+
comp: defaultDataFn ? defaultDataFn(compName, nameGenerator, editorState) : undefined,
179+
};
180+
181+
const parentContainer = parentItem.children.comp;
182+
183+
const realContainer = parentContainer.realSimpleContainer();
184+
if (!realContainer) {
185+
message.error(`Container "${selectedEditorComponent}" cannot accept nested components`);
186+
return;
187+
}
188+
189+
const currentLayout = realContainer.children.layout.getView();
190+
const layoutInfo = manifest?.layoutInfo || defaultLayout(selectedNestComponent as UICompType);
191+
192+
let itemPos = 0;
193+
if (Object.keys(currentLayout).length > 0) {
194+
itemPos = Math.max(...Object.values(currentLayout).map((l: any) => l.pos || 0)) + 1;
195+
}
196+
197+
const layoutItem = {
198+
i: key,
199+
x: 0,
200+
y: 0,
201+
w: layoutInfo.w || 6,
202+
h: layoutInfo.h || 5,
203+
pos: itemPos,
204+
isDragging: false,
205+
};
206+
207+
realContainer.dispatch(
208+
wrapActionExtraInfo(
209+
multiChangeAction({
210+
layout: changeValueAction({
211+
...currentLayout,
212+
[key]: layoutItem,
213+
}, true),
214+
items: addMapChildAction(key, widgetValue),
215+
}),
216+
{ compInfos: [{ compName: compName, compType: selectedNestComponent, type: "add" }] }
217+
)
218+
);
219+
220+
editorState.setSelectedCompNames(new Set([compName]), "nestComp");
221+
222+
message.success(`Component "${manifest?.name || selectedNestComponent}" nested in "${selectedEditorComponent}" successfully!`);
223+
} catch (error) {
224+
console.error('Error nesting component:', error);
225+
message.error('Failed to nest component. Please try again.');
226+
}
227+
}
228+
}
229+
111230
export const deleteComponentAction: ActionConfig = {
112231
key: 'delete-components',
113232
label: 'Delete a component',

0 commit comments

Comments
 (0)