@springmicro/forms 0.7.5 → 0.7.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.eslintrc.cjs +22 -22
- package/dist/index.js +4436 -4439
- package/dist/index.umd.cjs +36 -36
- package/package.json +3 -3
- package/src/builder/bottom-drawer.tsx +429 -429
- package/src/builder/form-builder.tsx +256 -256
- package/src/builder/modal.tsx +39 -39
- package/src/builder/nodes/node-base.tsx +94 -94
- package/src/builder/nodes/node-child-helpers.tsx +273 -273
- package/src/builder/nodes/node-parent.tsx +187 -187
- package/src/builder/nodes/node-types/array-node.tsx +134 -134
- package/src/builder/nodes/node-types/date-node.tsx +60 -60
- package/src/builder/nodes/node-types/file-node.tsx +67 -67
- package/src/builder/nodes/node-types/integer-node.tsx +60 -60
- package/src/builder/nodes/node-types/object-node.tsx +67 -67
- package/src/builder/nodes/node-types/text-node.tsx +66 -66
- package/src/index.tsx +26 -26
- package/src/types/form-builder.ts +135 -135
- package/src/types/utils.type.ts +1 -1
- package/src/utils/form-builder.ts +424 -424
|
@@ -1,256 +1,256 @@
|
|
|
1
|
-
import React from "react";
|
|
2
|
-
import NodeParent from "./nodes/node-parent";
|
|
3
|
-
import BottomDrawer from "./bottom-drawer";
|
|
4
|
-
import {
|
|
5
|
-
CountdownType,
|
|
6
|
-
EditingStateType,
|
|
7
|
-
FormNodeType,
|
|
8
|
-
FormType,
|
|
9
|
-
ObjectNode,
|
|
10
|
-
} from "../types/form-builder";
|
|
11
|
-
import { DragDropContext, Droppable } from "react-beautiful-dnd";
|
|
12
|
-
import { serializeBuilderToForm } from "../utils/form-builder";
|
|
13
|
-
import { RJSFSchema, UiSchema } from "@rjsf/utils";
|
|
14
|
-
import { UseStateType } from "../types/utils.type";
|
|
15
|
-
import { Box } from "@mui/material";
|
|
16
|
-
|
|
17
|
-
export type FormBuilderProps = {
|
|
18
|
-
nodes: UseStateType<FormNodeType[]>;
|
|
19
|
-
form: UseStateType<FormType>;
|
|
20
|
-
saveCallback: (data: { form: RJSFSchema; ui: UiSchema }) => Promise<boolean>;
|
|
21
|
-
};
|
|
22
|
-
|
|
23
|
-
const countdownSeconds = 5;
|
|
24
|
-
|
|
25
|
-
export default function FormBuilder({
|
|
26
|
-
nodes: providedNodes,
|
|
27
|
-
form: providedForm,
|
|
28
|
-
saveCallback,
|
|
29
|
-
}: FormBuilderProps) {
|
|
30
|
-
const [form, setForm] = providedForm;
|
|
31
|
-
const [baseNodes, setBaseNodes] = providedNodes;
|
|
32
|
-
const [path, setPath] = React.useState<number[]>([]);
|
|
33
|
-
const [nodes, setNodes] = React.useState<FormNodeType[]>([...baseNodes]);
|
|
34
|
-
const editingState = React.useState<EditingStateType>(false);
|
|
35
|
-
|
|
36
|
-
/**
|
|
37
|
-
* ------------------------- PATH MANAGEMENT -----------------------------
|
|
38
|
-
*/
|
|
39
|
-
|
|
40
|
-
React.useEffect(() => {
|
|
41
|
-
if (path.length === 0) {
|
|
42
|
-
setBaseNodes([...nodes]);
|
|
43
|
-
} else {
|
|
44
|
-
setBaseNodes((bn) => {
|
|
45
|
-
const basePathSearch = [...bn]; // The new base of nodes.
|
|
46
|
-
let pathSearch = basePathSearch; // Runs through the path and modifies basePathSearch.
|
|
47
|
-
for (let i = 0; i < path.length - 1; i++) {
|
|
48
|
-
// Sets pathSearch to every child in the path except for the last one.
|
|
49
|
-
pathSearch = (pathSearch[path[i]] as ObjectNode).children;
|
|
50
|
-
}
|
|
51
|
-
(pathSearch[path[path.length - 1]] as ObjectNode).children = nodes; // Sets the value pathSearch.children in the last value of the path to nodes. (Where the user is currently editing)
|
|
52
|
-
return [...basePathSearch];
|
|
53
|
-
});
|
|
54
|
-
}
|
|
55
|
-
}, [nodes]);
|
|
56
|
-
|
|
57
|
-
React.useEffect(() => {
|
|
58
|
-
let pathSearch = [...baseNodes];
|
|
59
|
-
for (let i = 0; i < path.length; i++) {
|
|
60
|
-
pathSearch = (pathSearch[path[i]] as ObjectNode).children;
|
|
61
|
-
}
|
|
62
|
-
setNodes(pathSearch);
|
|
63
|
-
}, [path]);
|
|
64
|
-
|
|
65
|
-
function updatePath(newPathNum?: number) {
|
|
66
|
-
editingState[1](false);
|
|
67
|
-
if (newPathNum === undefined) {
|
|
68
|
-
// If undefined, go up a layer in the path.
|
|
69
|
-
setPath((p) => p.filter((_, i) => i !== p.length - 1));
|
|
70
|
-
} else if (newPathNum < 0) {
|
|
71
|
-
// If negative, reset to base path.
|
|
72
|
-
setPath([]);
|
|
73
|
-
} else {
|
|
74
|
-
// If 0 or higher, push to the end of the path.
|
|
75
|
-
setPath((p) => [...p, newPathNum]);
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
/**
|
|
80
|
-
* ------------------------- AUTOSAVE -----------------------------
|
|
81
|
-
*/
|
|
82
|
-
|
|
83
|
-
const timerRef = React.useRef<NodeJS.Timeout>();
|
|
84
|
-
const [countdown, setCountdown] = React.useState<CountdownType>(undefined);
|
|
85
|
-
|
|
86
|
-
function resetCountdown() {
|
|
87
|
-
if (timerRef.current) {
|
|
88
|
-
clearTimeout(timerRef.current);
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
setCountdown(countdownSeconds);
|
|
92
|
-
timerRef.current = setTimeout(() => {
|
|
93
|
-
Save();
|
|
94
|
-
}, countdownSeconds * 1000);
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
const AutosaveNodes: React.Dispatch<React.SetStateAction<FormNodeType[]>> = (
|
|
98
|
-
action: React.SetStateAction<FormNodeType[]>
|
|
99
|
-
) => {
|
|
100
|
-
setNodes((prevNodes) => {
|
|
101
|
-
let currentState: FormNodeType[] = prevNodes;
|
|
102
|
-
if (typeof action === "function") {
|
|
103
|
-
currentState = action(currentState);
|
|
104
|
-
} else {
|
|
105
|
-
currentState = [...action];
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
resetCountdown();
|
|
109
|
-
return currentState;
|
|
110
|
-
});
|
|
111
|
-
};
|
|
112
|
-
|
|
113
|
-
const AutosaveForm: React.Dispatch<React.SetStateAction<FormType>> = (
|
|
114
|
-
action: React.SetStateAction<FormType>
|
|
115
|
-
) => {
|
|
116
|
-
setForm((prevForm) => {
|
|
117
|
-
let currentState: FormType = prevForm;
|
|
118
|
-
if (typeof action === "function") {
|
|
119
|
-
currentState = action(currentState);
|
|
120
|
-
} else {
|
|
121
|
-
currentState = { ...prevForm, ...action };
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
resetCountdown();
|
|
125
|
-
return currentState;
|
|
126
|
-
});
|
|
127
|
-
};
|
|
128
|
-
|
|
129
|
-
React.useEffect(() => {
|
|
130
|
-
if (typeof countdown === "string" || countdown === undefined) return;
|
|
131
|
-
if (countdown > 0) {
|
|
132
|
-
const interval = setInterval(() => {
|
|
133
|
-
setCountdown((prevCount) => (prevCount as number) - 1);
|
|
134
|
-
}, 1000);
|
|
135
|
-
return () => clearInterval(interval);
|
|
136
|
-
}
|
|
137
|
-
}, [countdown]);
|
|
138
|
-
|
|
139
|
-
React.useEffect(() => {
|
|
140
|
-
// Adds a warning when user tries to close tab with unsaved changes
|
|
141
|
-
if (countdown !== "Saved." && countdown !== undefined) {
|
|
142
|
-
const handleBeforeUnload = (event: BeforeUnloadEvent) => {
|
|
143
|
-
const confirmationMessage =
|
|
144
|
-
"You have unsaved changes. Are you sure you want to leave?";
|
|
145
|
-
event.returnValue = confirmationMessage; // Gecko, Trident, Chrome 34+
|
|
146
|
-
return confirmationMessage; // Gecko, WebKit, Chrome <34
|
|
147
|
-
};
|
|
148
|
-
|
|
149
|
-
window.addEventListener("beforeunload", handleBeforeUnload);
|
|
150
|
-
|
|
151
|
-
return () => {
|
|
152
|
-
window.removeEventListener("beforeunload", handleBeforeUnload);
|
|
153
|
-
};
|
|
154
|
-
}
|
|
155
|
-
}, [countdown]);
|
|
156
|
-
|
|
157
|
-
// Runs after autosave countdown finishes.
|
|
158
|
-
function Save() {
|
|
159
|
-
setCountdown("Saving...");
|
|
160
|
-
setBaseNodes((bn) => {
|
|
161
|
-
// Gets the most up-to-date baseNodes through the setBaseNodes function because it updates in a weird way.
|
|
162
|
-
const Form = serializeBuilderToForm(form, bn);
|
|
163
|
-
saveCallback({ ...Form }).then((res) => {
|
|
164
|
-
console.log("PROMISE RES:", res);
|
|
165
|
-
if (res) {
|
|
166
|
-
setCountdown("Saved.");
|
|
167
|
-
setTimeout(() => {
|
|
168
|
-
setCountdown(undefined);
|
|
169
|
-
}, 1500); // Waits 1.5s before removing text
|
|
170
|
-
} else {
|
|
171
|
-
setCountdown("Save failed."); // Do not remove this text until another change has been made by the user.
|
|
172
|
-
}
|
|
173
|
-
});
|
|
174
|
-
|
|
175
|
-
return bn; // doesn't need to return a new object because it's not intended to update any state.
|
|
176
|
-
});
|
|
177
|
-
setTimeout(() => {}, Math.random() * 700 + 300); // Simulated API wait time
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
return (
|
|
181
|
-
<Box
|
|
182
|
-
sx={{
|
|
183
|
-
p: 2,
|
|
184
|
-
width: "100%",
|
|
185
|
-
height: "100%",
|
|
186
|
-
borderRadius: 2,
|
|
187
|
-
border: "1px solid #999",
|
|
188
|
-
display: "flex",
|
|
189
|
-
flexDirection: "column",
|
|
190
|
-
}}
|
|
191
|
-
>
|
|
192
|
-
<Box
|
|
193
|
-
id="fb-scrollable"
|
|
194
|
-
sx={{
|
|
195
|
-
display: "flex",
|
|
196
|
-
flexGrow: 1,
|
|
197
|
-
overflow: "auto",
|
|
198
|
-
borderTopLeftRadius: 0.5,
|
|
199
|
-
borderTopRightRadius: 0.5,
|
|
200
|
-
flexDirection: "column",
|
|
201
|
-
scrollBehavior: "smooth",
|
|
202
|
-
}}
|
|
203
|
-
>
|
|
204
|
-
<Box sx={{ display: "flex", flexDirection: "column" }}>
|
|
205
|
-
<DragDropContext
|
|
206
|
-
onDragEnd={(result) => {
|
|
207
|
-
AutosaveNodes((prevNodes) => {
|
|
208
|
-
if (!result.destination) return prevNodes;
|
|
209
|
-
|
|
210
|
-
const items = Array.from(prevNodes);
|
|
211
|
-
const [reorderedItem] = items.splice(result.source.index, 1);
|
|
212
|
-
items.splice(result.destination.index, 0, reorderedItem);
|
|
213
|
-
|
|
214
|
-
return items;
|
|
215
|
-
});
|
|
216
|
-
}}
|
|
217
|
-
>
|
|
218
|
-
<Droppable droppableId="nodes">
|
|
219
|
-
{(provided) => (
|
|
220
|
-
<Box
|
|
221
|
-
ref={provided.innerRef}
|
|
222
|
-
{...provided.droppableProps}
|
|
223
|
-
sx={{ display: "flex", flexDirection: "column", gap: 1 }}
|
|
224
|
-
>
|
|
225
|
-
{nodes.map((node, i) => (
|
|
226
|
-
<NodeParent
|
|
227
|
-
key={node.nodeId}
|
|
228
|
-
node={node}
|
|
229
|
-
nodesState={[nodes, AutosaveNodes]}
|
|
230
|
-
index={i}
|
|
231
|
-
editingState={editingState}
|
|
232
|
-
updatePath={() => {
|
|
233
|
-
updatePath(i);
|
|
234
|
-
}}
|
|
235
|
-
deleteNode={() => {
|
|
236
|
-
AutosaveNodes((n) => n.filter((_, j) => j !== i));
|
|
237
|
-
}}
|
|
238
|
-
/>
|
|
239
|
-
))}
|
|
240
|
-
{provided.placeholder}
|
|
241
|
-
</Box>
|
|
242
|
-
)}
|
|
243
|
-
</Droppable>
|
|
244
|
-
</DragDropContext>
|
|
245
|
-
</Box>
|
|
246
|
-
</Box>
|
|
247
|
-
<BottomDrawer
|
|
248
|
-
nodesState={[nodes, AutosaveNodes]}
|
|
249
|
-
formState={[form, AutosaveForm]}
|
|
250
|
-
countdown={countdown}
|
|
251
|
-
path={[path, updatePath]}
|
|
252
|
-
setEditing={editingState[1]}
|
|
253
|
-
/>
|
|
254
|
-
</Box>
|
|
255
|
-
);
|
|
256
|
-
}
|
|
1
|
+
import React from "react";
|
|
2
|
+
import NodeParent from "./nodes/node-parent";
|
|
3
|
+
import BottomDrawer from "./bottom-drawer";
|
|
4
|
+
import {
|
|
5
|
+
CountdownType,
|
|
6
|
+
EditingStateType,
|
|
7
|
+
FormNodeType,
|
|
8
|
+
FormType,
|
|
9
|
+
ObjectNode,
|
|
10
|
+
} from "../types/form-builder";
|
|
11
|
+
import { DragDropContext, Droppable } from "react-beautiful-dnd";
|
|
12
|
+
import { serializeBuilderToForm } from "../utils/form-builder";
|
|
13
|
+
import { RJSFSchema, UiSchema } from "@rjsf/utils";
|
|
14
|
+
import { UseStateType } from "../types/utils.type";
|
|
15
|
+
import { Box } from "@mui/material";
|
|
16
|
+
|
|
17
|
+
export type FormBuilderProps = {
|
|
18
|
+
nodes: UseStateType<FormNodeType[]>;
|
|
19
|
+
form: UseStateType<FormType>;
|
|
20
|
+
saveCallback: (data: { form: RJSFSchema; ui: UiSchema }) => Promise<boolean>;
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
const countdownSeconds = 5;
|
|
24
|
+
|
|
25
|
+
export default function FormBuilder({
|
|
26
|
+
nodes: providedNodes,
|
|
27
|
+
form: providedForm,
|
|
28
|
+
saveCallback,
|
|
29
|
+
}: FormBuilderProps) {
|
|
30
|
+
const [form, setForm] = providedForm;
|
|
31
|
+
const [baseNodes, setBaseNodes] = providedNodes;
|
|
32
|
+
const [path, setPath] = React.useState<number[]>([]);
|
|
33
|
+
const [nodes, setNodes] = React.useState<FormNodeType[]>([...baseNodes]);
|
|
34
|
+
const editingState = React.useState<EditingStateType>(false);
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* ------------------------- PATH MANAGEMENT -----------------------------
|
|
38
|
+
*/
|
|
39
|
+
|
|
40
|
+
React.useEffect(() => {
|
|
41
|
+
if (path.length === 0) {
|
|
42
|
+
setBaseNodes([...nodes]);
|
|
43
|
+
} else {
|
|
44
|
+
setBaseNodes((bn) => {
|
|
45
|
+
const basePathSearch = [...bn]; // The new base of nodes.
|
|
46
|
+
let pathSearch = basePathSearch; // Runs through the path and modifies basePathSearch.
|
|
47
|
+
for (let i = 0; i < path.length - 1; i++) {
|
|
48
|
+
// Sets pathSearch to every child in the path except for the last one.
|
|
49
|
+
pathSearch = (pathSearch[path[i]] as ObjectNode).children;
|
|
50
|
+
}
|
|
51
|
+
(pathSearch[path[path.length - 1]] as ObjectNode).children = nodes; // Sets the value pathSearch.children in the last value of the path to nodes. (Where the user is currently editing)
|
|
52
|
+
return [...basePathSearch];
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
}, [nodes]);
|
|
56
|
+
|
|
57
|
+
React.useEffect(() => {
|
|
58
|
+
let pathSearch = [...baseNodes];
|
|
59
|
+
for (let i = 0; i < path.length; i++) {
|
|
60
|
+
pathSearch = (pathSearch[path[i]] as ObjectNode).children;
|
|
61
|
+
}
|
|
62
|
+
setNodes(pathSearch);
|
|
63
|
+
}, [path]);
|
|
64
|
+
|
|
65
|
+
function updatePath(newPathNum?: number) {
|
|
66
|
+
editingState[1](false);
|
|
67
|
+
if (newPathNum === undefined) {
|
|
68
|
+
// If undefined, go up a layer in the path.
|
|
69
|
+
setPath((p) => p.filter((_, i) => i !== p.length - 1));
|
|
70
|
+
} else if (newPathNum < 0) {
|
|
71
|
+
// If negative, reset to base path.
|
|
72
|
+
setPath([]);
|
|
73
|
+
} else {
|
|
74
|
+
// If 0 or higher, push to the end of the path.
|
|
75
|
+
setPath((p) => [...p, newPathNum]);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* ------------------------- AUTOSAVE -----------------------------
|
|
81
|
+
*/
|
|
82
|
+
|
|
83
|
+
const timerRef = React.useRef<NodeJS.Timeout>();
|
|
84
|
+
const [countdown, setCountdown] = React.useState<CountdownType>(undefined);
|
|
85
|
+
|
|
86
|
+
function resetCountdown() {
|
|
87
|
+
if (timerRef.current) {
|
|
88
|
+
clearTimeout(timerRef.current);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
setCountdown(countdownSeconds);
|
|
92
|
+
timerRef.current = setTimeout(() => {
|
|
93
|
+
Save();
|
|
94
|
+
}, countdownSeconds * 1000);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
const AutosaveNodes: React.Dispatch<React.SetStateAction<FormNodeType[]>> = (
|
|
98
|
+
action: React.SetStateAction<FormNodeType[]>
|
|
99
|
+
) => {
|
|
100
|
+
setNodes((prevNodes) => {
|
|
101
|
+
let currentState: FormNodeType[] = prevNodes;
|
|
102
|
+
if (typeof action === "function") {
|
|
103
|
+
currentState = action(currentState);
|
|
104
|
+
} else {
|
|
105
|
+
currentState = [...action];
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
resetCountdown();
|
|
109
|
+
return currentState;
|
|
110
|
+
});
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
const AutosaveForm: React.Dispatch<React.SetStateAction<FormType>> = (
|
|
114
|
+
action: React.SetStateAction<FormType>
|
|
115
|
+
) => {
|
|
116
|
+
setForm((prevForm) => {
|
|
117
|
+
let currentState: FormType = prevForm;
|
|
118
|
+
if (typeof action === "function") {
|
|
119
|
+
currentState = action(currentState);
|
|
120
|
+
} else {
|
|
121
|
+
currentState = { ...prevForm, ...action };
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
resetCountdown();
|
|
125
|
+
return currentState;
|
|
126
|
+
});
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
React.useEffect(() => {
|
|
130
|
+
if (typeof countdown === "string" || countdown === undefined) return;
|
|
131
|
+
if (countdown > 0) {
|
|
132
|
+
const interval = setInterval(() => {
|
|
133
|
+
setCountdown((prevCount) => (prevCount as number) - 1);
|
|
134
|
+
}, 1000);
|
|
135
|
+
return () => clearInterval(interval);
|
|
136
|
+
}
|
|
137
|
+
}, [countdown]);
|
|
138
|
+
|
|
139
|
+
React.useEffect(() => {
|
|
140
|
+
// Adds a warning when user tries to close tab with unsaved changes
|
|
141
|
+
if (countdown !== "Saved." && countdown !== undefined) {
|
|
142
|
+
const handleBeforeUnload = (event: BeforeUnloadEvent) => {
|
|
143
|
+
const confirmationMessage =
|
|
144
|
+
"You have unsaved changes. Are you sure you want to leave?";
|
|
145
|
+
event.returnValue = confirmationMessage; // Gecko, Trident, Chrome 34+
|
|
146
|
+
return confirmationMessage; // Gecko, WebKit, Chrome <34
|
|
147
|
+
};
|
|
148
|
+
|
|
149
|
+
window.addEventListener("beforeunload", handleBeforeUnload);
|
|
150
|
+
|
|
151
|
+
return () => {
|
|
152
|
+
window.removeEventListener("beforeunload", handleBeforeUnload);
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
}, [countdown]);
|
|
156
|
+
|
|
157
|
+
// Runs after autosave countdown finishes.
|
|
158
|
+
function Save() {
|
|
159
|
+
setCountdown("Saving...");
|
|
160
|
+
setBaseNodes((bn) => {
|
|
161
|
+
// Gets the most up-to-date baseNodes through the setBaseNodes function because it updates in a weird way.
|
|
162
|
+
const Form = serializeBuilderToForm(form, bn);
|
|
163
|
+
saveCallback({ ...Form }).then((res) => {
|
|
164
|
+
console.log("PROMISE RES:", res);
|
|
165
|
+
if (res) {
|
|
166
|
+
setCountdown("Saved.");
|
|
167
|
+
setTimeout(() => {
|
|
168
|
+
setCountdown(undefined);
|
|
169
|
+
}, 1500); // Waits 1.5s before removing text
|
|
170
|
+
} else {
|
|
171
|
+
setCountdown("Save failed."); // Do not remove this text until another change has been made by the user.
|
|
172
|
+
}
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
return bn; // doesn't need to return a new object because it's not intended to update any state.
|
|
176
|
+
});
|
|
177
|
+
setTimeout(() => {}, Math.random() * 700 + 300); // Simulated API wait time
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
return (
|
|
181
|
+
<Box
|
|
182
|
+
sx={{
|
|
183
|
+
p: 2,
|
|
184
|
+
width: "100%",
|
|
185
|
+
height: "100%",
|
|
186
|
+
borderRadius: 2,
|
|
187
|
+
border: "1px solid #999",
|
|
188
|
+
display: "flex",
|
|
189
|
+
flexDirection: "column",
|
|
190
|
+
}}
|
|
191
|
+
>
|
|
192
|
+
<Box
|
|
193
|
+
id="fb-scrollable"
|
|
194
|
+
sx={{
|
|
195
|
+
display: "flex",
|
|
196
|
+
flexGrow: 1,
|
|
197
|
+
overflow: "auto",
|
|
198
|
+
borderTopLeftRadius: 0.5,
|
|
199
|
+
borderTopRightRadius: 0.5,
|
|
200
|
+
flexDirection: "column",
|
|
201
|
+
scrollBehavior: "smooth",
|
|
202
|
+
}}
|
|
203
|
+
>
|
|
204
|
+
<Box sx={{ display: "flex", flexDirection: "column" }}>
|
|
205
|
+
<DragDropContext
|
|
206
|
+
onDragEnd={(result) => {
|
|
207
|
+
AutosaveNodes((prevNodes) => {
|
|
208
|
+
if (!result.destination) return prevNodes;
|
|
209
|
+
|
|
210
|
+
const items = Array.from(prevNodes);
|
|
211
|
+
const [reorderedItem] = items.splice(result.source.index, 1);
|
|
212
|
+
items.splice(result.destination.index, 0, reorderedItem);
|
|
213
|
+
|
|
214
|
+
return items;
|
|
215
|
+
});
|
|
216
|
+
}}
|
|
217
|
+
>
|
|
218
|
+
<Droppable droppableId="nodes">
|
|
219
|
+
{(provided) => (
|
|
220
|
+
<Box
|
|
221
|
+
ref={provided.innerRef}
|
|
222
|
+
{...provided.droppableProps}
|
|
223
|
+
sx={{ display: "flex", flexDirection: "column", gap: 1 }}
|
|
224
|
+
>
|
|
225
|
+
{nodes.map((node, i) => (
|
|
226
|
+
<NodeParent
|
|
227
|
+
key={node.nodeId}
|
|
228
|
+
node={node}
|
|
229
|
+
nodesState={[nodes, AutosaveNodes]}
|
|
230
|
+
index={i}
|
|
231
|
+
editingState={editingState}
|
|
232
|
+
updatePath={() => {
|
|
233
|
+
updatePath(i);
|
|
234
|
+
}}
|
|
235
|
+
deleteNode={() => {
|
|
236
|
+
AutosaveNodes((n) => n.filter((_, j) => j !== i));
|
|
237
|
+
}}
|
|
238
|
+
/>
|
|
239
|
+
))}
|
|
240
|
+
{provided.placeholder}
|
|
241
|
+
</Box>
|
|
242
|
+
)}
|
|
243
|
+
</Droppable>
|
|
244
|
+
</DragDropContext>
|
|
245
|
+
</Box>
|
|
246
|
+
</Box>
|
|
247
|
+
<BottomDrawer
|
|
248
|
+
nodesState={[nodes, AutosaveNodes]}
|
|
249
|
+
formState={[form, AutosaveForm]}
|
|
250
|
+
countdown={countdown}
|
|
251
|
+
path={[path, updatePath]}
|
|
252
|
+
setEditing={editingState[1]}
|
|
253
|
+
/>
|
|
254
|
+
</Box>
|
|
255
|
+
);
|
|
256
|
+
}
|
package/src/builder/modal.tsx
CHANGED
|
@@ -1,39 +1,39 @@
|
|
|
1
|
-
import { Box, Modal as MuiModal, SxProps } from "@mui/material";
|
|
2
|
-
|
|
3
|
-
const style: SxProps = {
|
|
4
|
-
position: "absolute",
|
|
5
|
-
top: "50%",
|
|
6
|
-
left: "50%",
|
|
7
|
-
transform: "translate(-50%, -50%)",
|
|
8
|
-
width: 400,
|
|
9
|
-
bgcolor: "white",
|
|
10
|
-
borderRadius: 2,
|
|
11
|
-
boxShadow: 24,
|
|
12
|
-
p: 4,
|
|
13
|
-
gap: 2,
|
|
14
|
-
display: "flex",
|
|
15
|
-
flexDirection: "column",
|
|
16
|
-
};
|
|
17
|
-
|
|
18
|
-
export type ModalProps = {
|
|
19
|
-
open: boolean;
|
|
20
|
-
onClose?: () => void;
|
|
21
|
-
children: React.ReactNode;
|
|
22
|
-
sx?: SxProps;
|
|
23
|
-
innerBoxSx?: SxProps;
|
|
24
|
-
};
|
|
25
|
-
|
|
26
|
-
export default function Modal({
|
|
27
|
-
open,
|
|
28
|
-
onClose,
|
|
29
|
-
children,
|
|
30
|
-
sx,
|
|
31
|
-
innerBoxSx,
|
|
32
|
-
}: ModalProps) {
|
|
33
|
-
const innerSx = { ...style, ...innerBoxSx };
|
|
34
|
-
return (
|
|
35
|
-
<MuiModal open={open} onClose={onClose} sx={sx}>
|
|
36
|
-
<Box sx={innerSx}>{children}</Box>
|
|
37
|
-
</MuiModal>
|
|
38
|
-
);
|
|
39
|
-
}
|
|
1
|
+
import { Box, Modal as MuiModal, SxProps } from "@mui/material";
|
|
2
|
+
|
|
3
|
+
const style: SxProps = {
|
|
4
|
+
position: "absolute",
|
|
5
|
+
top: "50%",
|
|
6
|
+
left: "50%",
|
|
7
|
+
transform: "translate(-50%, -50%)",
|
|
8
|
+
width: 400,
|
|
9
|
+
bgcolor: "white",
|
|
10
|
+
borderRadius: 2,
|
|
11
|
+
boxShadow: 24,
|
|
12
|
+
p: 4,
|
|
13
|
+
gap: 2,
|
|
14
|
+
display: "flex",
|
|
15
|
+
flexDirection: "column",
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
export type ModalProps = {
|
|
19
|
+
open: boolean;
|
|
20
|
+
onClose?: () => void;
|
|
21
|
+
children: React.ReactNode;
|
|
22
|
+
sx?: SxProps;
|
|
23
|
+
innerBoxSx?: SxProps;
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
export default function Modal({
|
|
27
|
+
open,
|
|
28
|
+
onClose,
|
|
29
|
+
children,
|
|
30
|
+
sx,
|
|
31
|
+
innerBoxSx,
|
|
32
|
+
}: ModalProps) {
|
|
33
|
+
const innerSx = { ...style, ...innerBoxSx };
|
|
34
|
+
return (
|
|
35
|
+
<MuiModal open={open} onClose={onClose} sx={sx}>
|
|
36
|
+
<Box sx={innerSx}>{children}</Box>
|
|
37
|
+
</MuiModal>
|
|
38
|
+
);
|
|
39
|
+
}
|