@mieweb/forms-editor 0.1.0
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/README.md +256 -0
- package/dist/index.css +6 -0
- package/dist/index.js +1120 -0
- package/package.json +36 -0
- package/src/index.css +14 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,1120 @@
|
|
|
1
|
+
// src/QuestionnaireEditor.jsx
|
|
2
|
+
import React14, { useEffect as useEffect3, useRef as useRef2 } from "react";
|
|
3
|
+
|
|
4
|
+
// src/components/Header.jsx
|
|
5
|
+
import React2, { useState } from "react";
|
|
6
|
+
import { useFormStore, useFieldsArray, useUIApi, useUIStore } from "@mieweb/forms-engine";
|
|
7
|
+
|
|
8
|
+
// src/components/DataViewer.jsx
|
|
9
|
+
import React from "react";
|
|
10
|
+
import { AnimatePresence, motion } from "framer-motion";
|
|
11
|
+
import yaml from "js-yaml";
|
|
12
|
+
function DataViewer({
|
|
13
|
+
open,
|
|
14
|
+
onClose,
|
|
15
|
+
data,
|
|
16
|
+
title = "Data Viewer",
|
|
17
|
+
placement = "center",
|
|
18
|
+
// "center" | "bottom"
|
|
19
|
+
pretty = 2,
|
|
20
|
+
defaultMode = "yaml",
|
|
21
|
+
// "json" | "yaml"
|
|
22
|
+
fileBaseName = "form-data",
|
|
23
|
+
contentClassName = ""
|
|
24
|
+
}) {
|
|
25
|
+
const isCenter = placement === "center";
|
|
26
|
+
const [mode, setMode] = React.useState(defaultMode === "yaml" ? "yaml" : "json");
|
|
27
|
+
const viewerText = React.useMemo(() => {
|
|
28
|
+
const indent = Math.max(2, pretty | 0);
|
|
29
|
+
try {
|
|
30
|
+
return mode === "yaml" ? yaml.dump(data ?? {}, {
|
|
31
|
+
indent,
|
|
32
|
+
lineWidth: 80,
|
|
33
|
+
noRefs: true,
|
|
34
|
+
forceQuotes: true,
|
|
35
|
+
skipInvalid: true
|
|
36
|
+
}) : JSON.stringify(data ?? {}, null, indent);
|
|
37
|
+
} catch {
|
|
38
|
+
return mode === "yaml" ? "# Failed to render YAML" : String(data);
|
|
39
|
+
}
|
|
40
|
+
}, [data, mode, pretty]);
|
|
41
|
+
const stop = (e) => e.stopPropagation();
|
|
42
|
+
const download = (e) => {
|
|
43
|
+
stop(e);
|
|
44
|
+
const isYaml = mode === "yaml";
|
|
45
|
+
const blob = new Blob([viewerText], { type: isYaml ? "text/yaml" : "application/json" });
|
|
46
|
+
const url = URL.createObjectURL(blob);
|
|
47
|
+
const a = document.createElement("a");
|
|
48
|
+
a.href = url;
|
|
49
|
+
a.download = `${fileBaseName}.${isYaml ? "yml" : "json"}`;
|
|
50
|
+
a.click();
|
|
51
|
+
URL.revokeObjectURL(url);
|
|
52
|
+
};
|
|
53
|
+
const copy = async (e) => {
|
|
54
|
+
stop(e);
|
|
55
|
+
try {
|
|
56
|
+
await navigator.clipboard.writeText(viewerText);
|
|
57
|
+
} catch {
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
return /* @__PURE__ */ React.createElement(AnimatePresence, null, open && /* @__PURE__ */ React.createElement(
|
|
61
|
+
motion.div,
|
|
62
|
+
{
|
|
63
|
+
className: "fixed inset-0 z-[60] flex items-center justify-center",
|
|
64
|
+
initial: { opacity: 0 },
|
|
65
|
+
animate: { opacity: 1 },
|
|
66
|
+
exit: { opacity: 0 },
|
|
67
|
+
onMouseDown: onClose,
|
|
68
|
+
"aria-modal": "true",
|
|
69
|
+
role: "dialog"
|
|
70
|
+
},
|
|
71
|
+
/* @__PURE__ */ React.createElement("div", { className: "w-full h-full flex items-end sm:items-center justify-center sm:max-w-4xl" }, /* @__PURE__ */ React.createElement(
|
|
72
|
+
motion.div,
|
|
73
|
+
{
|
|
74
|
+
onMouseDown: stop,
|
|
75
|
+
initial: { y: isCenter ? 20 : "100%", opacity: isCenter ? 0 : 1, scale: isCenter ? 0.98 : 1 },
|
|
76
|
+
animate: { y: 0, opacity: 1, scale: 1 },
|
|
77
|
+
exit: { y: isCenter ? 20 : "100%", opacity: isCenter ? 0 : 1, scale: isCenter ? 0.98 : 1 },
|
|
78
|
+
transition: { type: "spring", stiffness: 160, damping: 20 },
|
|
79
|
+
className: isCenter ? "w-full sm:max-w-2xl sm:rounded-2xl rounded-t-2xl bg-white shadow-lg border border-black/10 max-h-[80vh] overflow-hidden" : "w-full mx-auto bg-black/5 border border-black/15 px-6 py-4 rounded-2xl backdrop-blur-xl overflow-hidden"
|
|
80
|
+
},
|
|
81
|
+
/* @__PURE__ */ React.createElement("div", { className: `flex items-center justify-between ${isCenter ? "px-4 py-3 border-b border-black/10" : ""}` }, /* @__PURE__ */ React.createElement("h3", { className: "font-semibold" }, title, " (", mode.toUpperCase(), ")"), /* @__PURE__ */ React.createElement("div", { className: "flex items-center gap-2" }, /* @__PURE__ */ React.createElement("div", { className: "inline-flex rounded-lg border border-black/10 overflow-hidden" }, /* @__PURE__ */ React.createElement(
|
|
82
|
+
"button",
|
|
83
|
+
{
|
|
84
|
+
className: `px-3 py-1 text-sm ${mode === "yaml" ? "bg-black/6" : "bg-gray-200 hover:bg-black/5"}`,
|
|
85
|
+
"aria-pressed": mode === "yaml",
|
|
86
|
+
onClick: (e) => {
|
|
87
|
+
stop(e);
|
|
88
|
+
setMode("yaml");
|
|
89
|
+
}
|
|
90
|
+
},
|
|
91
|
+
"YAML"
|
|
92
|
+
), /* @__PURE__ */ React.createElement(
|
|
93
|
+
"button",
|
|
94
|
+
{
|
|
95
|
+
className: `px-3 py-1 text-sm ${mode === "json" ? "bg-black/6" : "bg-gray-200 hover:bg-black/5"}`,
|
|
96
|
+
"aria-pressed": mode === "json",
|
|
97
|
+
onClick: (e) => {
|
|
98
|
+
stop(e);
|
|
99
|
+
setMode("json");
|
|
100
|
+
}
|
|
101
|
+
},
|
|
102
|
+
"JSON"
|
|
103
|
+
)), /* @__PURE__ */ React.createElement("button", { className: "px-3 py-1 rounded-lg border border-black/10 hover:bg-black/5 text-sm", onClick: copy }, "Copy"), /* @__PURE__ */ React.createElement("button", { className: "px-3 py-1 rounded-lg border border-black/10 hover:bg-black/5 text-sm", onClick: download }, "Download"), /* @__PURE__ */ React.createElement(
|
|
104
|
+
"button",
|
|
105
|
+
{
|
|
106
|
+
className: "px-3 py-1 rounded-lg border border-black/10 hover:bg-black/5 text-sm",
|
|
107
|
+
onClick: (e) => {
|
|
108
|
+
stop(e);
|
|
109
|
+
onClose == null ? void 0 : onClose();
|
|
110
|
+
}
|
|
111
|
+
},
|
|
112
|
+
"Close"
|
|
113
|
+
))),
|
|
114
|
+
/* @__PURE__ */ React.createElement("div", { className: isCenter ? `p-4 overflow-auto max-h-[70vh] ${contentClassName}` : `mt-2 p-2 rounded-lg overflow-y-auto max-h-96 ${contentClassName}` }, /* @__PURE__ */ React.createElement("pre", { className: "whitespace-pre-wrap break-words text-sm text-gray-700" }, viewerText))
|
|
115
|
+
))
|
|
116
|
+
));
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// src/components/Header.jsx
|
|
120
|
+
function Header() {
|
|
121
|
+
const [showData, setShowData] = useState(false);
|
|
122
|
+
const replaceAll = useFormStore((s) => s.replaceAll);
|
|
123
|
+
const fieldsArray = useFieldsArray();
|
|
124
|
+
const ui = useUIApi();
|
|
125
|
+
const isPreview = ui.state.isPreview;
|
|
126
|
+
const importData = (data) => {
|
|
127
|
+
try {
|
|
128
|
+
const text = String(data).replace(/^\uFEFF/, "").trim();
|
|
129
|
+
const parsed = JSON.parse(text);
|
|
130
|
+
const arr = Array.isArray(parsed) ? parsed : parsed == null ? void 0 : parsed.fields;
|
|
131
|
+
if (!Array.isArray(arr)) throw new Error("Expected [] or { fields: [] }");
|
|
132
|
+
replaceAll(arr);
|
|
133
|
+
ui.selectedFieldId.clear();
|
|
134
|
+
ui.preview.set(false);
|
|
135
|
+
} catch (err) {
|
|
136
|
+
alert((err == null ? void 0 : err.message) || "Invalid JSON format");
|
|
137
|
+
}
|
|
138
|
+
};
|
|
139
|
+
const onPreview = () => {
|
|
140
|
+
ui.preview.set(true);
|
|
141
|
+
ui.selectedFieldId.clear();
|
|
142
|
+
};
|
|
143
|
+
const onEdit = () => {
|
|
144
|
+
ui.preview.set(false);
|
|
145
|
+
};
|
|
146
|
+
return /* @__PURE__ */ React2.createElement("header", { className: "sticky top-0 z-49 bg-transparent mx-auto" }, /* @__PURE__ */ React2.createElement("div", { className: "py-6 text-center" }, /* @__PURE__ */ React2.createElement("h1", { className: "text-3xl sm:text-4xl tracking-tight" }, "Questionnaire Builder"), /* @__PURE__ */ React2.createElement("p", { className: "mt-1 text-sm text-black/60" }, "Build dynamic questionnaires with JSON config and FHIR export")), /* @__PURE__ */ React2.createElement("div", { className: "max-w-6xl mx-auto px-4" }, /* @__PURE__ */ React2.createElement("div", { className: "grid grid-cols-2 rounded-xl border border-black/10 bg-white shadow-sm" }, /* @__PURE__ */ React2.createElement(
|
|
147
|
+
"button",
|
|
148
|
+
{
|
|
149
|
+
className: `py-3 rounded-xl text-sm font-medium ${!isPreview ? "bg-black/5" : ""}`,
|
|
150
|
+
onClick: onEdit
|
|
151
|
+
},
|
|
152
|
+
"Builder"
|
|
153
|
+
), /* @__PURE__ */ React2.createElement(
|
|
154
|
+
"button",
|
|
155
|
+
{
|
|
156
|
+
className: `py-3 rounded-xl text-sm font-medium ${isPreview ? "bg-black/5" : ""}`,
|
|
157
|
+
onClick: onPreview
|
|
158
|
+
},
|
|
159
|
+
"Preview"
|
|
160
|
+
)), /* @__PURE__ */ React2.createElement("div", { className: "mt-4 flex flex-wrap gap-2 items-center justify-end" }, /* @__PURE__ */ React2.createElement("label", { className: "px-4 py-2 rounded-xl border border-black/15 bg-white hover:bg-black/5 cursor-pointer text-sm" }, "Import", /* @__PURE__ */ React2.createElement(
|
|
161
|
+
"input",
|
|
162
|
+
{
|
|
163
|
+
className: "hidden",
|
|
164
|
+
type: "file",
|
|
165
|
+
accept: ".json,application/json",
|
|
166
|
+
onChange: (e) => {
|
|
167
|
+
var _a;
|
|
168
|
+
const file = (_a = e.target.files) == null ? void 0 : _a[0];
|
|
169
|
+
if (!file) return;
|
|
170
|
+
const reader = new FileReader();
|
|
171
|
+
reader.onload = (ev) => {
|
|
172
|
+
var _a2;
|
|
173
|
+
return importData(((_a2 = ev.target) == null ? void 0 : _a2.result) ?? "");
|
|
174
|
+
};
|
|
175
|
+
reader.readAsText(file);
|
|
176
|
+
e.target.value = "";
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
)), /* @__PURE__ */ React2.createElement(
|
|
180
|
+
"button",
|
|
181
|
+
{
|
|
182
|
+
className: "px-4 py-2 rounded-xl border border-black/15 bg-white hover:bg-black/5 text-sm",
|
|
183
|
+
onClick: () => setShowData(true)
|
|
184
|
+
},
|
|
185
|
+
"Data Viewer"
|
|
186
|
+
))), /* @__PURE__ */ React2.createElement(
|
|
187
|
+
DataViewer,
|
|
188
|
+
{
|
|
189
|
+
open: showData,
|
|
190
|
+
onClose: () => setShowData(false),
|
|
191
|
+
data: fieldsArray,
|
|
192
|
+
title: "Form Data",
|
|
193
|
+
placement: "bottom",
|
|
194
|
+
contentClassName: "custom-scrollbar"
|
|
195
|
+
}
|
|
196
|
+
));
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
// src/components/MobileToolBar.jsx
|
|
200
|
+
import React3, { useState as useState2, useEffect, useRef } from "react";
|
|
201
|
+
import { motion as motion2 } from "framer-motion";
|
|
202
|
+
import { DATALOG_ICON, EYEEDIT_ICON, EYECLOSED_ICON, PLUSSQUARE_ICON, X_ICON } from "@mieweb/forms-engine";
|
|
203
|
+
import { useFormStore as useFormStore2, useFieldsArray as useFieldsArray2, useUIApi as useUIApi2, fieldTypes } from "@mieweb/forms-engine";
|
|
204
|
+
function MobileToolBar() {
|
|
205
|
+
const [isToolBarExpanded, setIsToolBarExpanded] = useState2(false);
|
|
206
|
+
const [isLogExpanded, setIsLogExpanded] = useState2(false);
|
|
207
|
+
const containerRef = useRef(null);
|
|
208
|
+
const addField = useFormStore2((s) => s.addField);
|
|
209
|
+
const fieldsArray = useFieldsArray2();
|
|
210
|
+
const ui = useUIApi2();
|
|
211
|
+
const isPreview = ui.state.isPreview;
|
|
212
|
+
const handleToolBarExpanded = () => {
|
|
213
|
+
setIsToolBarExpanded((v) => !v);
|
|
214
|
+
setIsLogExpanded(false);
|
|
215
|
+
};
|
|
216
|
+
const handleLogExpanded = () => {
|
|
217
|
+
setIsLogExpanded((v) => !v);
|
|
218
|
+
setIsToolBarExpanded(false);
|
|
219
|
+
};
|
|
220
|
+
const handlePreviewMode = () => {
|
|
221
|
+
ui.preview.toggle();
|
|
222
|
+
setIsToolBarExpanded(false);
|
|
223
|
+
setIsLogExpanded(false);
|
|
224
|
+
};
|
|
225
|
+
useEffect(() => {
|
|
226
|
+
if (!isToolBarExpanded || isLogExpanded) return;
|
|
227
|
+
const handleDocDown = (event) => {
|
|
228
|
+
const el = containerRef.current;
|
|
229
|
+
if (el && !el.contains(event.target)) {
|
|
230
|
+
setIsToolBarExpanded(false);
|
|
231
|
+
setIsLogExpanded(false);
|
|
232
|
+
}
|
|
233
|
+
};
|
|
234
|
+
document.addEventListener("pointerdown", handleDocDown);
|
|
235
|
+
return () => document.removeEventListener("pointerdown", handleDocDown);
|
|
236
|
+
}, [isToolBarExpanded, isLogExpanded]);
|
|
237
|
+
return /* @__PURE__ */ React3.createElement("div", { className: "MobileToolBar fixed bottom-0 left-0 w-full text-stone-900 shadow-lg z-10" }, /* @__PURE__ */ React3.createElement(
|
|
238
|
+
motion2.div,
|
|
239
|
+
{
|
|
240
|
+
initial: { y: "100%" },
|
|
241
|
+
animate: { y: "0%" },
|
|
242
|
+
transition: { duration: 0.8, ease: [0.25, 0.1, 0.25, 1] },
|
|
243
|
+
className: `flex ${!isPreview ? "justify-around" : "justify-center"} pb-5 max-w-xl sm:max-w-4xl mx-auto`
|
|
244
|
+
},
|
|
245
|
+
/* @__PURE__ */ React3.createElement(
|
|
246
|
+
motion2.button,
|
|
247
|
+
{
|
|
248
|
+
onClick: handlePreviewMode,
|
|
249
|
+
initial: { opacity: 1, scale: 1, x: "0%" },
|
|
250
|
+
animate: {
|
|
251
|
+
opacity: isToolBarExpanded || isLogExpanded ? 0 : 1,
|
|
252
|
+
scale: isToolBarExpanded || isLogExpanded ? 0 : 1,
|
|
253
|
+
x: !isPreview ? "0%" : "120%"
|
|
254
|
+
},
|
|
255
|
+
className: `relative cursor-pointer ${!isPreview ? "" : "mx-29"} bg-black/5 rounded-2xl p-3 backdrop-blur-xl`
|
|
256
|
+
},
|
|
257
|
+
!isPreview ? /* @__PURE__ */ React3.createElement(EYECLOSED_ICON, { className: "h-12 w-12" }) : /* @__PURE__ */ React3.createElement(EYEEDIT_ICON, { className: "h-12 w-12" })
|
|
258
|
+
),
|
|
259
|
+
!isPreview && /* @__PURE__ */ React3.createElement(
|
|
260
|
+
motion2.button,
|
|
261
|
+
{
|
|
262
|
+
onClick: handleToolBarExpanded,
|
|
263
|
+
initial: { opacity: 1, scale: 1 },
|
|
264
|
+
animate: {
|
|
265
|
+
opacity: isToolBarExpanded || isLogExpanded ? 0 : 1,
|
|
266
|
+
scale: isToolBarExpanded || isLogExpanded ? 0 : 1
|
|
267
|
+
},
|
|
268
|
+
className: "relative cursor-pointer bg-black/5 rounded-2xl p-3 backdrop-blur-xl"
|
|
269
|
+
},
|
|
270
|
+
/* @__PURE__ */ React3.createElement(PLUSSQUARE_ICON, { className: "h-12 w-12" })
|
|
271
|
+
),
|
|
272
|
+
/* @__PURE__ */ React3.createElement(
|
|
273
|
+
motion2.button,
|
|
274
|
+
{
|
|
275
|
+
onClick: handleLogExpanded,
|
|
276
|
+
initial: { opacity: 1, scale: 1, x: "0%" },
|
|
277
|
+
animate: {
|
|
278
|
+
opacity: isLogExpanded || isToolBarExpanded ? 0 : 1,
|
|
279
|
+
scale: isLogExpanded || isToolBarExpanded ? 0 : 1,
|
|
280
|
+
x: !isPreview ? "0%" : "-120%"
|
|
281
|
+
},
|
|
282
|
+
className: `relative cursor-pointer ${!isPreview ? "" : "mx-29"} bg-black/5 rounded-2xl p-3 backdrop-blur-xl`
|
|
283
|
+
},
|
|
284
|
+
/* @__PURE__ */ React3.createElement(DATALOG_ICON, { className: "h-12 w-12" })
|
|
285
|
+
)
|
|
286
|
+
), /* @__PURE__ */ React3.createElement(
|
|
287
|
+
DataViewer,
|
|
288
|
+
{
|
|
289
|
+
open: isLogExpanded,
|
|
290
|
+
onClose: () => setIsLogExpanded(false),
|
|
291
|
+
data: fieldsArray,
|
|
292
|
+
title: "Form Data",
|
|
293
|
+
placement: "bottom",
|
|
294
|
+
contentClassName: "custom-scrollbar"
|
|
295
|
+
}
|
|
296
|
+
), /* @__PURE__ */ React3.createElement(
|
|
297
|
+
motion2.div,
|
|
298
|
+
{
|
|
299
|
+
ref: containerRef,
|
|
300
|
+
onPointerDown: (e) => e.stopPropagation(),
|
|
301
|
+
initial: { opacity: 0, y: "100%", scale: 0 },
|
|
302
|
+
animate: {
|
|
303
|
+
opacity: isToolBarExpanded ? 1 : 0,
|
|
304
|
+
y: isToolBarExpanded ? "0%" : "100%",
|
|
305
|
+
scale: isToolBarExpanded ? 1 : 0.6
|
|
306
|
+
},
|
|
307
|
+
transition: { type: "spring", stiffness: 150, damping: 20 },
|
|
308
|
+
className: "MobileToolBar-Modal fixed bottom-0 w-full mx-auto bg-black/5 border-black/15 border px-9 py-4 mb-2 rounded-2xl backdrop-blur-xl overflow-y-scroll"
|
|
309
|
+
},
|
|
310
|
+
/* @__PURE__ */ React3.createElement("div", { className: "grid grid-cols-1 gap-2" }, /* @__PURE__ */ React3.createElement("button", { className: "flex w-full justify-end", onClick: () => setIsToolBarExpanded(false) }, /* @__PURE__ */ React3.createElement(X_ICON, null)), Object.keys(fieldTypes).map((type) => /* @__PURE__ */ React3.createElement(
|
|
311
|
+
"button",
|
|
312
|
+
{
|
|
313
|
+
key: type,
|
|
314
|
+
className: "px-4 pl-6 py-2 text-black text-left rounded hover:bg-slate-50",
|
|
315
|
+
onClick: () => {
|
|
316
|
+
addField(type);
|
|
317
|
+
setIsToolBarExpanded(false);
|
|
318
|
+
}
|
|
319
|
+
},
|
|
320
|
+
"Add ",
|
|
321
|
+
fieldTypes[type].label
|
|
322
|
+
)))
|
|
323
|
+
));
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
// src/components/desktopLayout/Layout.jsx
|
|
327
|
+
import React13 from "react";
|
|
328
|
+
|
|
329
|
+
// src/components/desktopLayout/toolPanel/ToolPanel.jsx
|
|
330
|
+
import React4, { useMemo } from "react";
|
|
331
|
+
import { fieldTypes as fieldTypes2, useFormStore as useFormStore3 } from "@mieweb/forms-engine";
|
|
332
|
+
var TOOL_ITEMS = Object.entries(fieldTypes2).map(([type, cfg]) => ({
|
|
333
|
+
type,
|
|
334
|
+
label: cfg.label
|
|
335
|
+
}));
|
|
336
|
+
var ToolPanelImpl = ({ isPreview = false }) => {
|
|
337
|
+
if (isPreview) return null;
|
|
338
|
+
const addField = useFormStore3((s) => s.addField);
|
|
339
|
+
const handlers = useMemo(() => {
|
|
340
|
+
const m = {};
|
|
341
|
+
for (const { type } of TOOL_ITEMS) m[type] = () => addField(type);
|
|
342
|
+
return m;
|
|
343
|
+
}, [addField]);
|
|
344
|
+
return /* @__PURE__ */ React4.createElement("div", { className: "p-4 bg-white border border-gray-200 rounded-lg shadow-sm" }, /* @__PURE__ */ React4.createElement("h3", { className: "text-lg font-semibold mb-3" }, "Tools"), /* @__PURE__ */ React4.createElement("div", { className: "grid grid-cols-1 gap-2" }, TOOL_ITEMS.map(({ type, label }) => /* @__PURE__ */ React4.createElement(
|
|
345
|
+
"button",
|
|
346
|
+
{
|
|
347
|
+
key: type,
|
|
348
|
+
className: "px-3 py-2 text-left border border-black/10 rounded-md hover:bg-slate-50",
|
|
349
|
+
onClick: handlers[type]
|
|
350
|
+
},
|
|
351
|
+
"Add ",
|
|
352
|
+
label
|
|
353
|
+
))));
|
|
354
|
+
};
|
|
355
|
+
var ToolPanel = React4.memo(ToolPanelImpl, (prev, next) => prev.isPreview === next.isPreview);
|
|
356
|
+
var ToolPanel_default = ToolPanel;
|
|
357
|
+
|
|
358
|
+
// src/components/desktopLayout/editPanel/EditPanel.jsx
|
|
359
|
+
import React11 from "react";
|
|
360
|
+
|
|
361
|
+
// src/components/desktopLayout/editPanel/types/NonSectionEditor.jsx
|
|
362
|
+
import React8, { useCallback, useMemo as useMemo2 } from "react";
|
|
363
|
+
|
|
364
|
+
// src/components/desktopLayout/editPanel/types/CommonEditor.jsx
|
|
365
|
+
import React6 from "react";
|
|
366
|
+
|
|
367
|
+
// src/components/desktopLayout/editPanel/types/DraftIdEditor.jsx
|
|
368
|
+
import React5 from "react";
|
|
369
|
+
function DraftIdEditor({
|
|
370
|
+
id = "",
|
|
371
|
+
label = "ID",
|
|
372
|
+
onCommit,
|
|
373
|
+
validate,
|
|
374
|
+
placeholder = "Enter unique ID\u2026",
|
|
375
|
+
className = ""
|
|
376
|
+
}) {
|
|
377
|
+
const [draft, setDraft] = React5.useState(id ?? "");
|
|
378
|
+
const [err, setErr] = React5.useState("");
|
|
379
|
+
React5.useEffect(() => {
|
|
380
|
+
setDraft(id ?? "");
|
|
381
|
+
setErr("");
|
|
382
|
+
}, [id]);
|
|
383
|
+
const commit = React5.useCallback(() => {
|
|
384
|
+
const next = String(draft ?? "").trim();
|
|
385
|
+
if (!next) {
|
|
386
|
+
setErr("ID cannot be empty.");
|
|
387
|
+
setDraft(id ?? "");
|
|
388
|
+
return;
|
|
389
|
+
}
|
|
390
|
+
if (next === (id ?? "")) return;
|
|
391
|
+
if (typeof validate === "function") {
|
|
392
|
+
const msg = validate(next, id ?? "");
|
|
393
|
+
if (msg) {
|
|
394
|
+
setErr(msg);
|
|
395
|
+
return;
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
onCommit == null ? void 0 : onCommit(next);
|
|
399
|
+
}, [draft, id, onCommit, validate]);
|
|
400
|
+
return /* @__PURE__ */ React5.createElement("div", { className }, /* @__PURE__ */ React5.createElement("label", { className: "block text-sm mb-1" }, label), /* @__PURE__ */ React5.createElement(
|
|
401
|
+
"input",
|
|
402
|
+
{
|
|
403
|
+
className: "w-full px-3 py-2 border border-black/20 rounded",
|
|
404
|
+
value: draft,
|
|
405
|
+
onChange: (e) => {
|
|
406
|
+
if (err) setErr("");
|
|
407
|
+
setDraft(e.target.value);
|
|
408
|
+
},
|
|
409
|
+
onBlur: commit,
|
|
410
|
+
onKeyDown: (e) => {
|
|
411
|
+
if (e.key === "Enter") {
|
|
412
|
+
e.preventDefault();
|
|
413
|
+
commit();
|
|
414
|
+
}
|
|
415
|
+
},
|
|
416
|
+
placeholder
|
|
417
|
+
}
|
|
418
|
+
), err ? /* @__PURE__ */ React5.createElement("p", { className: "text-xs text-red-600 mt-1" }, err) : null);
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
// src/components/desktopLayout/editPanel/types/CommonEditor.jsx
|
|
422
|
+
function CommonEditor({ f, onUpdateField }) {
|
|
423
|
+
return /* @__PURE__ */ React6.createElement("div", { className: "space-y-3" }, /* @__PURE__ */ React6.createElement(
|
|
424
|
+
DraftIdEditor,
|
|
425
|
+
{
|
|
426
|
+
id: f.id ?? "",
|
|
427
|
+
onCommit: (next) => onUpdateField == null ? void 0 : onUpdateField("id", next)
|
|
428
|
+
}
|
|
429
|
+
), /* @__PURE__ */ React6.createElement("div", null, /* @__PURE__ */ React6.createElement("label", { className: "block text-sm mb-1" }, "Label / Question"), /* @__PURE__ */ React6.createElement(
|
|
430
|
+
"input",
|
|
431
|
+
{
|
|
432
|
+
className: "w-full px-3 py-2 border border-black/20 rounded",
|
|
433
|
+
value: f.question || "",
|
|
434
|
+
onChange: (e) => onUpdateField("question", e.target.value),
|
|
435
|
+
placeholder: "Enter question text"
|
|
436
|
+
}
|
|
437
|
+
)), /* @__PURE__ */ React6.createElement("div", null, /* @__PURE__ */ React6.createElement("label", { className: "inline-flex items-center gap-2 text-sm" }, /* @__PURE__ */ React6.createElement(
|
|
438
|
+
"input",
|
|
439
|
+
{
|
|
440
|
+
type: "checkbox",
|
|
441
|
+
checked: !!f.required,
|
|
442
|
+
onChange: (e) => onUpdateField("required", e.target.checked)
|
|
443
|
+
}
|
|
444
|
+
), "Required")), /* @__PURE__ */ React6.createElement("div", null, /* @__PURE__ */ React6.createElement("label", { className: "block text-sm mb-1" }, "Sublabel (optional)"), /* @__PURE__ */ React6.createElement(
|
|
445
|
+
"textarea",
|
|
446
|
+
{
|
|
447
|
+
className: "w-full px-3 py-2 border border-black/20 rounded",
|
|
448
|
+
value: f.sublabel || "",
|
|
449
|
+
onChange: (e) => onUpdateField("sublabel", e.target.value),
|
|
450
|
+
placeholder: "Helper text / description"
|
|
451
|
+
}
|
|
452
|
+
)));
|
|
453
|
+
}
|
|
454
|
+
var CommonEditor_default = CommonEditor;
|
|
455
|
+
|
|
456
|
+
// src/components/desktopLayout/editPanel/types/OptionListEditor.jsx
|
|
457
|
+
import React7 from "react";
|
|
458
|
+
|
|
459
|
+
// ../../node_modules/uuid/dist/esm/stringify.js
|
|
460
|
+
var byteToHex = [];
|
|
461
|
+
for (let i = 0; i < 256; ++i) {
|
|
462
|
+
byteToHex.push((i + 256).toString(16).slice(1));
|
|
463
|
+
}
|
|
464
|
+
function unsafeStringify(arr, offset = 0) {
|
|
465
|
+
return (byteToHex[arr[offset + 0]] + byteToHex[arr[offset + 1]] + byteToHex[arr[offset + 2]] + byteToHex[arr[offset + 3]] + "-" + byteToHex[arr[offset + 4]] + byteToHex[arr[offset + 5]] + "-" + byteToHex[arr[offset + 6]] + byteToHex[arr[offset + 7]] + "-" + byteToHex[arr[offset + 8]] + byteToHex[arr[offset + 9]] + "-" + byteToHex[arr[offset + 10]] + byteToHex[arr[offset + 11]] + byteToHex[arr[offset + 12]] + byteToHex[arr[offset + 13]] + byteToHex[arr[offset + 14]] + byteToHex[arr[offset + 15]]).toLowerCase();
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
// ../../node_modules/uuid/dist/esm/rng.js
|
|
469
|
+
import { randomFillSync } from "crypto";
|
|
470
|
+
var rnds8Pool = new Uint8Array(256);
|
|
471
|
+
var poolPtr = rnds8Pool.length;
|
|
472
|
+
function rng() {
|
|
473
|
+
if (poolPtr > rnds8Pool.length - 16) {
|
|
474
|
+
randomFillSync(rnds8Pool);
|
|
475
|
+
poolPtr = 0;
|
|
476
|
+
}
|
|
477
|
+
return rnds8Pool.slice(poolPtr, poolPtr += 16);
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
// ../../node_modules/uuid/dist/esm/native.js
|
|
481
|
+
import { randomUUID } from "crypto";
|
|
482
|
+
var native_default = { randomUUID };
|
|
483
|
+
|
|
484
|
+
// ../../node_modules/uuid/dist/esm/v4.js
|
|
485
|
+
function v4(options, buf, offset) {
|
|
486
|
+
var _a;
|
|
487
|
+
if (native_default.randomUUID && !buf && !options) {
|
|
488
|
+
return native_default.randomUUID();
|
|
489
|
+
}
|
|
490
|
+
options = options || {};
|
|
491
|
+
const rnds = options.random ?? ((_a = options.rng) == null ? void 0 : _a.call(options)) ?? rng();
|
|
492
|
+
if (rnds.length < 16) {
|
|
493
|
+
throw new Error("Random bytes length must be >= 16");
|
|
494
|
+
}
|
|
495
|
+
rnds[6] = rnds[6] & 15 | 64;
|
|
496
|
+
rnds[8] = rnds[8] & 63 | 128;
|
|
497
|
+
if (buf) {
|
|
498
|
+
offset = offset || 0;
|
|
499
|
+
if (offset < 0 || offset + 16 > buf.length) {
|
|
500
|
+
throw new RangeError(`UUID byte range ${offset}:${offset + 15} is out of buffer bounds`);
|
|
501
|
+
}
|
|
502
|
+
for (let i = 0; i < 16; ++i) {
|
|
503
|
+
buf[offset + i] = rnds[i];
|
|
504
|
+
}
|
|
505
|
+
return buf;
|
|
506
|
+
}
|
|
507
|
+
return unsafeStringify(rnds);
|
|
508
|
+
}
|
|
509
|
+
var v4_default = v4;
|
|
510
|
+
|
|
511
|
+
// src/components/desktopLayout/editPanel/types/OptionListEditor.jsx
|
|
512
|
+
function OptionListEditor({ field, onUpdateField }) {
|
|
513
|
+
const opts = field.options || [];
|
|
514
|
+
const addOption = () => {
|
|
515
|
+
onUpdateField("options", [...opts, { id: v4_default(), value: "" }]);
|
|
516
|
+
};
|
|
517
|
+
const updateOption = (id, value) => {
|
|
518
|
+
onUpdateField(
|
|
519
|
+
"options",
|
|
520
|
+
opts.map((o) => o.id === id ? { ...o, value } : o)
|
|
521
|
+
);
|
|
522
|
+
};
|
|
523
|
+
const removeOption = (id) => {
|
|
524
|
+
onUpdateField(
|
|
525
|
+
"options",
|
|
526
|
+
opts.filter((o) => o.id !== id)
|
|
527
|
+
);
|
|
528
|
+
};
|
|
529
|
+
return /* @__PURE__ */ React7.createElement("div", { className: "mt-3" }, /* @__PURE__ */ React7.createElement("div", { className: "text-sm font-medium mb-1" }, "Options"), opts.map((opt) => /* @__PURE__ */ React7.createElement("div", { key: opt.id, className: "flex items-center gap-2 mb-2" }, /* @__PURE__ */ React7.createElement(
|
|
530
|
+
"input",
|
|
531
|
+
{
|
|
532
|
+
className: "flex-1 px-3 py-2 border border-black/20 rounded",
|
|
533
|
+
value: opt.value,
|
|
534
|
+
onChange: (e) => updateOption(opt.id, e.target.value),
|
|
535
|
+
placeholder: "Option text"
|
|
536
|
+
}
|
|
537
|
+
), /* @__PURE__ */ React7.createElement(
|
|
538
|
+
"button",
|
|
539
|
+
{
|
|
540
|
+
onClick: () => removeOption(opt.id),
|
|
541
|
+
className: "px-2 py-1 text-sm border border-black/20 rounded hover:bg-slate-50"
|
|
542
|
+
},
|
|
543
|
+
"Remove"
|
|
544
|
+
))), /* @__PURE__ */ React7.createElement(
|
|
545
|
+
"button",
|
|
546
|
+
{
|
|
547
|
+
onClick: addOption,
|
|
548
|
+
className: "mt-1 px-3 py-2 text-sm border border-black/20 rounded hover:bg-slate-50"
|
|
549
|
+
},
|
|
550
|
+
"+ Add Option"
|
|
551
|
+
));
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
// src/components/desktopLayout/editPanel/types/NonSectionEditor.jsx
|
|
555
|
+
import { useFormApi } from "@mieweb/forms-engine";
|
|
556
|
+
function NonSectionEditor({ f }) {
|
|
557
|
+
const api = useFormApi(f.id);
|
|
558
|
+
const onUpdateField = useCallback(
|
|
559
|
+
(key, value) => api.field.update(key, value),
|
|
560
|
+
[api]
|
|
561
|
+
);
|
|
562
|
+
const isChoice = useMemo2(
|
|
563
|
+
() => ["radio", "check", "selection"].includes(f.fieldType),
|
|
564
|
+
[f.fieldType]
|
|
565
|
+
);
|
|
566
|
+
return /* @__PURE__ */ React8.createElement(React8.Fragment, null, /* @__PURE__ */ React8.createElement("h3", { className: "text-lg font-semibold mb-3" }, "Edit"), /* @__PURE__ */ React8.createElement(CommonEditor_default, { f, onUpdateField }), f.fieldType === "input" && /* @__PURE__ */ React8.createElement("div", { className: "mt-4" }, /* @__PURE__ */ React8.createElement("div", { className: "text-sm font-medium mb-1" }, "Default Answer"), /* @__PURE__ */ React8.createElement(
|
|
567
|
+
"input",
|
|
568
|
+
{
|
|
569
|
+
className: "w-full px-3 py-2 border border-black/20 rounded",
|
|
570
|
+
value: f.answer || "",
|
|
571
|
+
onChange: (e) => onUpdateField("answer", e.target.value),
|
|
572
|
+
placeholder: "Default value"
|
|
573
|
+
}
|
|
574
|
+
)), isChoice && /* @__PURE__ */ React8.createElement(OptionListEditor, { field: f, onUpdateField }));
|
|
575
|
+
}
|
|
576
|
+
var NonSectionEditor_default = NonSectionEditor;
|
|
577
|
+
|
|
578
|
+
// src/components/desktopLayout/editPanel/types/SectionEditor.jsx
|
|
579
|
+
import React9, { useState as useState3, useEffect as useEffect2, useMemo as useMemo3, useCallback as useCallback2 } from "react";
|
|
580
|
+
import { fieldTypes as fieldTypes3, useFormStore as useFormStore4, useFormApi as useFormApi2 } from "@mieweb/forms-engine";
|
|
581
|
+
function SectionEditor({ section, onActiveChildChange }) {
|
|
582
|
+
var _a, _b, _c;
|
|
583
|
+
const sectionApi = useFormApi2(section.id);
|
|
584
|
+
const children = Array.isArray(section.fields) ? section.fields : [];
|
|
585
|
+
const [activeChildId, setActiveChildId] = useState3(((_a = children[0]) == null ? void 0 : _a.id) || null);
|
|
586
|
+
useEffect2(() => {
|
|
587
|
+
var _a2;
|
|
588
|
+
setActiveChildId(((_a2 = children[0]) == null ? void 0 : _a2.id) || null);
|
|
589
|
+
}, [section.id]);
|
|
590
|
+
useEffect2(() => {
|
|
591
|
+
if (!children.length) {
|
|
592
|
+
if (activeChildId !== null) setActiveChildId(null);
|
|
593
|
+
return;
|
|
594
|
+
}
|
|
595
|
+
const stillExists = children.some((c) => c.id === activeChildId);
|
|
596
|
+
if (!stillExists) setActiveChildId(children[0].id);
|
|
597
|
+
}, [children, activeChildId]);
|
|
598
|
+
useEffect2(() => {
|
|
599
|
+
onActiveChildChange == null ? void 0 : onActiveChildChange(section.id, activeChildId || null);
|
|
600
|
+
}, [section.id, activeChildId, onActiveChildChange]);
|
|
601
|
+
const onUpdateSection = useCallback2(
|
|
602
|
+
(key, value) => sectionApi.field.update(key, value),
|
|
603
|
+
[sectionApi]
|
|
604
|
+
);
|
|
605
|
+
const activeChild = useMemo3(
|
|
606
|
+
() => children.find((c) => c.id === activeChildId) || null,
|
|
607
|
+
[children, activeChildId]
|
|
608
|
+
);
|
|
609
|
+
const onUpdateChild = useCallback2(
|
|
610
|
+
(key, value) => {
|
|
611
|
+
if (!activeChild) return;
|
|
612
|
+
if (key === "id") {
|
|
613
|
+
const next = String(value ?? "").trim();
|
|
614
|
+
if (!next) return;
|
|
615
|
+
useFormStore4.getState().updateField(
|
|
616
|
+
activeChild.id,
|
|
617
|
+
{ id: next },
|
|
618
|
+
{
|
|
619
|
+
sectionId: section.id,
|
|
620
|
+
onIdChange: (newId, oldId) => {
|
|
621
|
+
setActiveChildId((curr) => curr === oldId ? newId : curr);
|
|
622
|
+
}
|
|
623
|
+
}
|
|
624
|
+
);
|
|
625
|
+
return;
|
|
626
|
+
}
|
|
627
|
+
useFormStore4.getState().updateField(
|
|
628
|
+
activeChild.id,
|
|
629
|
+
{ [key]: value },
|
|
630
|
+
{ sectionId: section.id }
|
|
631
|
+
);
|
|
632
|
+
},
|
|
633
|
+
[activeChild, section.id]
|
|
634
|
+
);
|
|
635
|
+
const onDeleteChild = useCallback2(() => {
|
|
636
|
+
if (!activeChild) return;
|
|
637
|
+
useFormStore4.getState().deleteField(activeChild.id, { sectionId: section.id });
|
|
638
|
+
}, [activeChild, section.id]);
|
|
639
|
+
const isChoiceChild = useMemo3(
|
|
640
|
+
() => activeChild && ["radio", "check", "selection"].includes(activeChild.fieldType),
|
|
641
|
+
[activeChild]
|
|
642
|
+
);
|
|
643
|
+
return /* @__PURE__ */ React9.createElement(React9.Fragment, null, /* @__PURE__ */ React9.createElement("h3", { className: "text-lg font-semibold mb-3" }, "Edit Section"), /* @__PURE__ */ React9.createElement(
|
|
644
|
+
DraftIdEditor,
|
|
645
|
+
{
|
|
646
|
+
id: section.id,
|
|
647
|
+
onCommit: (next) => sectionApi.field.renameId(next)
|
|
648
|
+
}
|
|
649
|
+
), /* @__PURE__ */ React9.createElement("div", { className: "mt-3" }, /* @__PURE__ */ React9.createElement("label", { className: "block text-sm mb-1" }, "Section Title"), /* @__PURE__ */ React9.createElement(
|
|
650
|
+
"input",
|
|
651
|
+
{
|
|
652
|
+
className: "w-full px-3 py-2 border border-black/20 rounded",
|
|
653
|
+
value: section.title || "",
|
|
654
|
+
onChange: (e) => onUpdateSection("title", e.target.value),
|
|
655
|
+
placeholder: "Section title"
|
|
656
|
+
}
|
|
657
|
+
)), /* @__PURE__ */ React9.createElement("div", { className: "mt-6" }, /* @__PURE__ */ React9.createElement("div", { className: "text-sm font-semibold mb-2" }, "Fields in this section"), children.length === 0 ? /* @__PURE__ */ React9.createElement("div", { className: "text-sm text-gray-500" }, "No fields yet. Use the section\u2019s mini toolbar to add fields.") : /* @__PURE__ */ React9.createElement(React9.Fragment, null, /* @__PURE__ */ React9.createElement("div", { className: "flex flex-wrap gap-2 mb-3" }, children.map((c) => {
|
|
658
|
+
var _a2, _b2;
|
|
659
|
+
return /* @__PURE__ */ React9.createElement(
|
|
660
|
+
"button",
|
|
661
|
+
{
|
|
662
|
+
key: c.id,
|
|
663
|
+
className: [
|
|
664
|
+
"px-3 py-1.5 text-sm rounded border",
|
|
665
|
+
activeChildId === c.id ? "bg-black/5 border-black/20" : "bg-white border-black/10 hover:bg-slate-50"
|
|
666
|
+
].join(" "),
|
|
667
|
+
onClick: () => setActiveChildId(c.id),
|
|
668
|
+
title: c.question || c.fieldType
|
|
669
|
+
},
|
|
670
|
+
((_a2 = c.question) == null ? void 0 : _a2.trim()) || ((_b2 = fieldTypes3[c.fieldType]) == null ? void 0 : _b2.label) || "Field"
|
|
671
|
+
);
|
|
672
|
+
})), activeChild && /* @__PURE__ */ React9.createElement("div", { className: "mt-2" }, /* @__PURE__ */ React9.createElement("div", { className: "text-sm font-semibold mb-2" }, "Editing: ", ((_b = activeChild.question) == null ? void 0 : _b.trim()) || ((_c = fieldTypes3[activeChild.fieldType]) == null ? void 0 : _c.label)), /* @__PURE__ */ React9.createElement(CommonEditor_default, { f: activeChild, onUpdateField: onUpdateChild }), activeChild.fieldType === "input" && /* @__PURE__ */ React9.createElement("div", { className: "mt-4" }, /* @__PURE__ */ React9.createElement("div", { className: "text-sm font-medium mb-1" }, "Default Answer"), /* @__PURE__ */ React9.createElement(
|
|
673
|
+
"input",
|
|
674
|
+
{
|
|
675
|
+
className: "w-full px-3 py-2 border border-black/20 rounded",
|
|
676
|
+
value: activeChild.answer || "",
|
|
677
|
+
onChange: (e) => onUpdateChild("answer", e.target.value),
|
|
678
|
+
placeholder: "Default value"
|
|
679
|
+
}
|
|
680
|
+
)), isChoiceChild && /* @__PURE__ */ React9.createElement(OptionListEditor, { field: activeChild, onUpdateField: onUpdateChild }), /* @__PURE__ */ React9.createElement(
|
|
681
|
+
"button",
|
|
682
|
+
{
|
|
683
|
+
className: "mt-3 px-3 py-2 text-sm text-red-400 border rounded",
|
|
684
|
+
onClick: onDeleteChild
|
|
685
|
+
},
|
|
686
|
+
"Delete this field"
|
|
687
|
+
)))));
|
|
688
|
+
}
|
|
689
|
+
var SectionEditor_default = SectionEditor;
|
|
690
|
+
|
|
691
|
+
// src/components/desktopLayout/editPanel/types/LogicEditor.jsx
|
|
692
|
+
import React10 from "react";
|
|
693
|
+
import { useUIApi as useUIApi3, useFormStore as useFormStore5 } from "@mieweb/forms-engine";
|
|
694
|
+
function getOperatorsForFieldType(fieldType) {
|
|
695
|
+
switch (fieldType) {
|
|
696
|
+
case "input":
|
|
697
|
+
return ["equals", "contains"];
|
|
698
|
+
case "radio":
|
|
699
|
+
case "selection":
|
|
700
|
+
return ["equals"];
|
|
701
|
+
case "check":
|
|
702
|
+
return ["includes"];
|
|
703
|
+
default:
|
|
704
|
+
return ["equals"];
|
|
705
|
+
}
|
|
706
|
+
}
|
|
707
|
+
function normOption(opt) {
|
|
708
|
+
if (opt && typeof opt === "object") {
|
|
709
|
+
const id = opt.id ?? String(opt.value ?? "");
|
|
710
|
+
const value = String(opt.value ?? opt.label ?? opt.id ?? "");
|
|
711
|
+
return { id, value };
|
|
712
|
+
}
|
|
713
|
+
const s = String(opt ?? "");
|
|
714
|
+
return { id: s, value: s };
|
|
715
|
+
}
|
|
716
|
+
function LogicEditor() {
|
|
717
|
+
var _a, _b, _c;
|
|
718
|
+
const ui = useUIApi3();
|
|
719
|
+
const byId = useFormStore5((s) => s.byId);
|
|
720
|
+
const setEnableWhen = useFormStore5((s) => s.setEnableWhen);
|
|
721
|
+
const updateField = useFormStore5((s) => s.updateField);
|
|
722
|
+
const selectedId = ((_a = ui == null ? void 0 : ui.selectedFieldId) == null ? void 0 : _a.value) ?? null;
|
|
723
|
+
const rawParentId = ((_b = ui == null ? void 0 : ui.selectedChildId) == null ? void 0 : _b.ParentId) ?? null;
|
|
724
|
+
const rawChildId = ((_c = ui == null ? void 0 : ui.selectedChildId) == null ? void 0 : _c.ChildId) ?? null;
|
|
725
|
+
const selectedField = selectedId ? byId[selectedId] : null;
|
|
726
|
+
const isSectionCtx = (selectedField == null ? void 0 : selectedField.fieldType) === "section";
|
|
727
|
+
const sectionChildren = React10.useMemo(() => {
|
|
728
|
+
if (!isSectionCtx) return [];
|
|
729
|
+
const arr = Array.isArray(selectedField == null ? void 0 : selectedField.fields) ? selectedField.fields : [];
|
|
730
|
+
return arr;
|
|
731
|
+
}, [isSectionCtx, selectedField]);
|
|
732
|
+
const childValidForSection = React10.useMemo(() => {
|
|
733
|
+
if (!isSectionCtx || !rawChildId) return false;
|
|
734
|
+
return sectionChildren.some((c) => c.id === rawChildId);
|
|
735
|
+
}, [isSectionCtx, rawChildId, sectionChildren]);
|
|
736
|
+
const initialTarget = React10.useMemo(() => {
|
|
737
|
+
if (!isSectionCtx) return "";
|
|
738
|
+
if (childValidForSection) return `child:${rawChildId}`;
|
|
739
|
+
return "";
|
|
740
|
+
}, [isSectionCtx, childValidForSection, rawChildId]);
|
|
741
|
+
const [target, setTarget] = React10.useState(initialTarget);
|
|
742
|
+
React10.useEffect(() => {
|
|
743
|
+
setTarget(initialTarget);
|
|
744
|
+
}, [initialTarget]);
|
|
745
|
+
React10.useEffect(() => {
|
|
746
|
+
if (!isSectionCtx) return;
|
|
747
|
+
if (target && target.startsWith("child:")) {
|
|
748
|
+
const cid = target.slice(6);
|
|
749
|
+
ui.selectedChildId.set(selectedId, cid);
|
|
750
|
+
} else {
|
|
751
|
+
ui.selectedChildId.set(null, null);
|
|
752
|
+
}
|
|
753
|
+
}, [isSectionCtx, target, selectedId, ui]);
|
|
754
|
+
const isChildScope = Boolean(isSectionCtx && target && target.startsWith("child:"));
|
|
755
|
+
const effectiveChildId = isChildScope ? target.slice(6) : null;
|
|
756
|
+
const effectiveId = isChildScope ? effectiveChildId : selectedId;
|
|
757
|
+
const field = React10.useMemo(() => {
|
|
758
|
+
if (!effectiveId) return null;
|
|
759
|
+
if (isChildScope) {
|
|
760
|
+
return sectionChildren.find((c) => c.id === effectiveChildId) ?? null;
|
|
761
|
+
}
|
|
762
|
+
return byId[effectiveId] ?? null;
|
|
763
|
+
}, [byId, effectiveId, isChildScope, effectiveChildId, sectionChildren]);
|
|
764
|
+
const targets = React10.useMemo(() => {
|
|
765
|
+
var _a2, _b2, _c2;
|
|
766
|
+
const out = [];
|
|
767
|
+
for (const f of Object.values(byId)) {
|
|
768
|
+
if (!f) continue;
|
|
769
|
+
if (f.fieldType === "section" && Array.isArray(f.fields)) {
|
|
770
|
+
const sectTitle = ((_a2 = f.title) == null ? void 0 : _a2.trim()) || "Section";
|
|
771
|
+
f.fields.forEach((c) => {
|
|
772
|
+
var _a3;
|
|
773
|
+
out.push({
|
|
774
|
+
id: c.id,
|
|
775
|
+
parentId: f.id,
|
|
776
|
+
label: `${sectTitle} \u203A ${((_a3 = c.question) == null ? void 0 : _a3.trim()) || c.id}`,
|
|
777
|
+
fieldType: c.fieldType,
|
|
778
|
+
options: Array.isArray(c.options) ? c.options : []
|
|
779
|
+
});
|
|
780
|
+
});
|
|
781
|
+
} else {
|
|
782
|
+
out.push({
|
|
783
|
+
id: f.id,
|
|
784
|
+
parentId: null,
|
|
785
|
+
label: ((_b2 = f.question) == null ? void 0 : _b2.trim()) || ((_c2 = f.title) == null ? void 0 : _c2.trim()) || f.id,
|
|
786
|
+
fieldType: f.fieldType,
|
|
787
|
+
options: Array.isArray(f.options) ? f.options : []
|
|
788
|
+
});
|
|
789
|
+
}
|
|
790
|
+
}
|
|
791
|
+
return out;
|
|
792
|
+
}, [byId]);
|
|
793
|
+
const findTarget = React10.useCallback(
|
|
794
|
+
(tid) => targets.find((t) => t.id === tid) || null,
|
|
795
|
+
[targets]
|
|
796
|
+
);
|
|
797
|
+
const writeEnableWhen = React10.useCallback(
|
|
798
|
+
(next) => {
|
|
799
|
+
if (!effectiveId) return;
|
|
800
|
+
const normalized = next && Array.isArray(next.conditions) ? { logic: next.logic || "AND", conditions: next.conditions } : void 0;
|
|
801
|
+
if (isChildScope && selectedId) {
|
|
802
|
+
updateField(effectiveId, { enableWhen: normalized }, { sectionId: selectedId });
|
|
803
|
+
} else {
|
|
804
|
+
setEnableWhen(effectiveId, normalized);
|
|
805
|
+
}
|
|
806
|
+
},
|
|
807
|
+
[effectiveId, isChildScope, selectedId, setEnableWhen, updateField]
|
|
808
|
+
);
|
|
809
|
+
const ew = React10.useMemo(() => {
|
|
810
|
+
return (field == null ? void 0 : field.enableWhen) && Array.isArray(field.enableWhen.conditions) ? { logic: field.enableWhen.logic || "AND", conditions: field.enableWhen.conditions } : { logic: "AND", conditions: [] };
|
|
811
|
+
}, [field]);
|
|
812
|
+
const addCond = React10.useCallback(() => {
|
|
813
|
+
const next = {
|
|
814
|
+
logic: ew.logic || "AND",
|
|
815
|
+
conditions: [
|
|
816
|
+
...Array.isArray(ew.conditions) ? ew.conditions : [],
|
|
817
|
+
{ targetId: "", operator: "equals", value: "" }
|
|
818
|
+
]
|
|
819
|
+
};
|
|
820
|
+
writeEnableWhen(next);
|
|
821
|
+
}, [ew, writeEnableWhen]);
|
|
822
|
+
const clear = React10.useCallback(() => writeEnableWhen(null), [writeEnableWhen]);
|
|
823
|
+
const removeCond = React10.useCallback(
|
|
824
|
+
(idx) => {
|
|
825
|
+
const base = Array.isArray(ew.conditions) ? ew.conditions : [];
|
|
826
|
+
const nextConds = base.filter((_, i) => i !== idx);
|
|
827
|
+
writeEnableWhen({ ...ew, conditions: nextConds });
|
|
828
|
+
},
|
|
829
|
+
[ew, writeEnableWhen]
|
|
830
|
+
);
|
|
831
|
+
const updateCond = React10.useCallback(
|
|
832
|
+
(idx, patch) => {
|
|
833
|
+
const base = Array.isArray(ew.conditions) ? ew.conditions : [];
|
|
834
|
+
const nextConds = [...base];
|
|
835
|
+
const curr = nextConds[idx] || { targetId: "", operator: "equals", value: "" };
|
|
836
|
+
const updated = { ...curr, ...patch };
|
|
837
|
+
if ("targetId" in patch) {
|
|
838
|
+
const meta = findTarget(updated.targetId);
|
|
839
|
+
const ops = getOperatorsForFieldType(meta == null ? void 0 : meta.fieldType);
|
|
840
|
+
if (!ops.includes(updated.operator)) updated.operator = ops[0] || "equals";
|
|
841
|
+
const opts = Array.isArray(meta == null ? void 0 : meta.options) ? meta.options.map(normOption) : [];
|
|
842
|
+
if (opts.length > 0) {
|
|
843
|
+
const valid = new Set(opts.map((o) => o.id));
|
|
844
|
+
if (!valid.has(updated.value)) updated.value = "";
|
|
845
|
+
}
|
|
846
|
+
}
|
|
847
|
+
nextConds[idx] = updated;
|
|
848
|
+
writeEnableWhen({ ...ew, conditions: nextConds });
|
|
849
|
+
},
|
|
850
|
+
[ew, findTarget, writeEnableWhen]
|
|
851
|
+
);
|
|
852
|
+
const filteredTargets = React10.useMemo(
|
|
853
|
+
() => targets.filter((t) => t.id !== effectiveId),
|
|
854
|
+
[targets, effectiveId]
|
|
855
|
+
);
|
|
856
|
+
const isDisabled = !field || !effectiveId;
|
|
857
|
+
return /* @__PURE__ */ React10.createElement("div", { className: "space-y-2" }, isSectionCtx && /* @__PURE__ */ React10.createElement("div", { className: "flex gap-2 items-center" }, /* @__PURE__ */ React10.createElement("label", { className: "text-sm" }, "Target:"), /* @__PURE__ */ React10.createElement(
|
|
858
|
+
"select",
|
|
859
|
+
{
|
|
860
|
+
className: "border p-1 rounded",
|
|
861
|
+
value: target,
|
|
862
|
+
onChange: (e) => setTarget(e.target.value)
|
|
863
|
+
},
|
|
864
|
+
/* @__PURE__ */ React10.createElement("option", { value: "" }, "Section (this)"),
|
|
865
|
+
sectionChildren.map((c) => {
|
|
866
|
+
var _a2, _b2;
|
|
867
|
+
return /* @__PURE__ */ React10.createElement("option", { key: c.id, value: `child:${c.id}` }, ((_a2 = c.question) == null ? void 0 : _a2.trim()) || ((_b2 = c.title) == null ? void 0 : _b2.trim()) || c.id);
|
|
868
|
+
})
|
|
869
|
+
)), /* @__PURE__ */ React10.createElement("div", { className: "flex gap-2 items-center" }, /* @__PURE__ */ React10.createElement("label", { className: "text-sm" }, "Logic:"), /* @__PURE__ */ React10.createElement(
|
|
870
|
+
"select",
|
|
871
|
+
{
|
|
872
|
+
value: ew.logic || "AND",
|
|
873
|
+
onChange: (e) => writeEnableWhen({ ...ew, logic: e.target.value }),
|
|
874
|
+
className: "border p-1 rounded",
|
|
875
|
+
disabled: isDisabled
|
|
876
|
+
},
|
|
877
|
+
/* @__PURE__ */ React10.createElement("option", { value: "AND" }, "AND"),
|
|
878
|
+
/* @__PURE__ */ React10.createElement("option", { value: "OR" }, "OR")
|
|
879
|
+
), /* @__PURE__ */ React10.createElement("button", { type: "button", className: "px-2 py-1 border rounded", onClick: addCond, disabled: isDisabled }, "+ Condition"), /* @__PURE__ */ React10.createElement("button", { type: "button", className: "px-2 py-1 border rounded", onClick: clear, disabled: isDisabled }, "Clear")), (Array.isArray(ew.conditions) ? ew.conditions : []).map((c, i) => {
|
|
880
|
+
const meta = findTarget(c.targetId);
|
|
881
|
+
const allowedOps = getOperatorsForFieldType(meta == null ? void 0 : meta.fieldType);
|
|
882
|
+
const optList = Array.isArray(meta == null ? void 0 : meta.options) ? meta.options.map(normOption) : [];
|
|
883
|
+
const hasOptions = optList.length > 0;
|
|
884
|
+
return /* @__PURE__ */ React10.createElement("div", { key: i, className: "grid grid-cols-1 sm:grid-cols-7 gap-2 items-center opacity-100" }, /* @__PURE__ */ React10.createElement(
|
|
885
|
+
"button",
|
|
886
|
+
{
|
|
887
|
+
type: "button",
|
|
888
|
+
onClick: () => removeCond(i),
|
|
889
|
+
className: "px-2 py-1 border rounded sm:col-span-1",
|
|
890
|
+
title: "Remove condition",
|
|
891
|
+
disabled: isDisabled
|
|
892
|
+
},
|
|
893
|
+
"\u2715"
|
|
894
|
+
), /* @__PURE__ */ React10.createElement(
|
|
895
|
+
"select",
|
|
896
|
+
{
|
|
897
|
+
className: "border p-1 rounded sm:col-span-3",
|
|
898
|
+
value: c.targetId,
|
|
899
|
+
onChange: (e) => updateCond(i, { targetId: e.target.value }),
|
|
900
|
+
disabled: isDisabled
|
|
901
|
+
},
|
|
902
|
+
/* @__PURE__ */ React10.createElement("option", { value: "" }, "\u2014 Select field \u2014"),
|
|
903
|
+
filteredTargets.map((t) => /* @__PURE__ */ React10.createElement("option", { key: t.id, value: t.id }, t.label))
|
|
904
|
+
), /* @__PURE__ */ React10.createElement(
|
|
905
|
+
"select",
|
|
906
|
+
{
|
|
907
|
+
className: "border p-1 rounded sm:col-span-1",
|
|
908
|
+
value: c.operator,
|
|
909
|
+
onChange: (e) => updateCond(i, { operator: e.target.value }),
|
|
910
|
+
disabled: isDisabled || !meta
|
|
911
|
+
},
|
|
912
|
+
(meta ? allowedOps : ["equals"]).map((op) => /* @__PURE__ */ React10.createElement("option", { key: op, value: op }, op))
|
|
913
|
+
), hasOptions ? /* @__PURE__ */ React10.createElement(
|
|
914
|
+
"select",
|
|
915
|
+
{
|
|
916
|
+
className: "border p-1 rounded sm:col-span-2",
|
|
917
|
+
value: c.value,
|
|
918
|
+
onChange: (e) => updateCond(i, { value: e.target.value }),
|
|
919
|
+
disabled: isDisabled || !meta
|
|
920
|
+
},
|
|
921
|
+
/* @__PURE__ */ React10.createElement("option", { value: "" }, "\u2014 Select option \u2014"),
|
|
922
|
+
optList.map((opt) => /* @__PURE__ */ React10.createElement("option", { key: opt.id, value: opt.id }, opt.value))
|
|
923
|
+
) : /* @__PURE__ */ React10.createElement(
|
|
924
|
+
"input",
|
|
925
|
+
{
|
|
926
|
+
className: "border p-1 rounded sm:col-span-2",
|
|
927
|
+
placeholder: "Enter value",
|
|
928
|
+
value: c.value,
|
|
929
|
+
onChange: (e) => updateCond(i, { value: e.target.value }),
|
|
930
|
+
disabled: isDisabled || !meta
|
|
931
|
+
}
|
|
932
|
+
));
|
|
933
|
+
}));
|
|
934
|
+
}
|
|
935
|
+
|
|
936
|
+
// src/components/desktopLayout/editPanel/EditPanel.jsx
|
|
937
|
+
import { useUIApi as useUIApi4, useFormStore as useFormStore6 } from "@mieweb/forms-engine";
|
|
938
|
+
function EditPanel() {
|
|
939
|
+
const ui = useUIApi4();
|
|
940
|
+
const selectedField = useFormStore6(
|
|
941
|
+
React11.useCallback(
|
|
942
|
+
(s) => ui.selectedFieldId.value ? s.byId[ui.selectedFieldId.value] : null,
|
|
943
|
+
[ui.selectedFieldId.value]
|
|
944
|
+
)
|
|
945
|
+
);
|
|
946
|
+
const [tab, setTab] = React11.useState("EDIT");
|
|
947
|
+
const handleActiveChildChange = React11.useCallback(
|
|
948
|
+
(sectionId, childId) => {
|
|
949
|
+
ui.selectedChildId.set(sectionId, childId);
|
|
950
|
+
},
|
|
951
|
+
[ui.selectedChildId.set]
|
|
952
|
+
);
|
|
953
|
+
React11.useEffect(() => {
|
|
954
|
+
ui.selectedChildId.set(null, null);
|
|
955
|
+
setTab("EDIT");
|
|
956
|
+
}, [ui.selectedFieldId.value]);
|
|
957
|
+
if (ui.state.isPreview) return null;
|
|
958
|
+
const isNone = !selectedField;
|
|
959
|
+
const isSection = (selectedField == null ? void 0 : selectedField.fieldType) === "section";
|
|
960
|
+
return /* @__PURE__ */ React11.createElement(
|
|
961
|
+
"div",
|
|
962
|
+
{
|
|
963
|
+
className: `p-4 bg-white border border-gray-200 rounded-lg shadow-sm overflow-y-auto custom-scrollbar
|
|
964
|
+
${selectedField ? "" : "max-h-32"} max-h-[calc(100svh-19rem)] lg:max-h-[calc(100dvh-15rem)]`
|
|
965
|
+
},
|
|
966
|
+
/* @__PURE__ */ React11.createElement(
|
|
967
|
+
"div",
|
|
968
|
+
{
|
|
969
|
+
className: "sticky top-0 z-30 mb-4 inline-flex rounded-md border border-gray-200 overflow-hidden bg-white",
|
|
970
|
+
role: "tablist"
|
|
971
|
+
},
|
|
972
|
+
/* @__PURE__ */ React11.createElement(
|
|
973
|
+
"button",
|
|
974
|
+
{
|
|
975
|
+
type: "button",
|
|
976
|
+
onClick: () => setTab("EDIT"),
|
|
977
|
+
"aria-selected": tab === "EDIT",
|
|
978
|
+
className: `px-3 py-1 text-sm ${tab === "EDIT" ? "bg-gray-100 font-semibold" : "bg-white"}`
|
|
979
|
+
},
|
|
980
|
+
"EDIT"
|
|
981
|
+
),
|
|
982
|
+
/* @__PURE__ */ React11.createElement(
|
|
983
|
+
"button",
|
|
984
|
+
{
|
|
985
|
+
type: "button",
|
|
986
|
+
onClick: () => setTab("LOGIC"),
|
|
987
|
+
"aria-selected": tab === "LOGIC",
|
|
988
|
+
className: `px-3 py-1 text-sm border-l border-gray-200 ${tab === "LOGIC" ? "bg-gray-100 font-semibold" : "bg-white"}`
|
|
989
|
+
},
|
|
990
|
+
"LOGIC"
|
|
991
|
+
)
|
|
992
|
+
),
|
|
993
|
+
isNone && /* @__PURE__ */ React11.createElement("div", { className: "text-gray-600" }, /* @__PURE__ */ React11.createElement("h3", { className: "text-lg font-semibold mb-2" }, "Edit"), /* @__PURE__ */ React11.createElement("p", null, "Select a field in the center panel to edit its properties.")),
|
|
994
|
+
!isNone && tab === "EDIT" && /* @__PURE__ */ React11.createElement(React11.Fragment, null, !isSection && /* @__PURE__ */ React11.createElement(NonSectionEditor_default, { f: selectedField }), isSection && /* @__PURE__ */ React11.createElement(
|
|
995
|
+
SectionEditor_default,
|
|
996
|
+
{
|
|
997
|
+
section: selectedField,
|
|
998
|
+
onActiveChildChange: handleActiveChildChange
|
|
999
|
+
}
|
|
1000
|
+
)),
|
|
1001
|
+
!isNone && tab === "LOGIC" && /* @__PURE__ */ React11.createElement(LogicEditor, null)
|
|
1002
|
+
);
|
|
1003
|
+
}
|
|
1004
|
+
|
|
1005
|
+
// src/components/FormBuilderMain.jsx
|
|
1006
|
+
import React12, { useMemo as useMemo4 } from "react";
|
|
1007
|
+
import { fieldTypes as fieldTypes4, getFieldComponent, useFieldsArray as useFieldsArray3, useFormStore as useFormStore7, useUIApi as useUIApi5, isVisible } from "@mieweb/forms-engine";
|
|
1008
|
+
function FormBuilderMain() {
|
|
1009
|
+
const ui = useUIApi5();
|
|
1010
|
+
const fields = useFieldsArray3();
|
|
1011
|
+
const allFlat = useMemo4(() => {
|
|
1012
|
+
const out = [];
|
|
1013
|
+
(fields || []).forEach((f) => {
|
|
1014
|
+
out.push(f);
|
|
1015
|
+
if ((f == null ? void 0 : f.fieldType) === "section" && Array.isArray(f.fields)) out.push(...f.fields);
|
|
1016
|
+
});
|
|
1017
|
+
return out;
|
|
1018
|
+
}, [fields]);
|
|
1019
|
+
const visibleIds = useMemo4(() => {
|
|
1020
|
+
const list = ui.state.isPreview ? fields.filter((f) => isVisible(f, allFlat)) : fields;
|
|
1021
|
+
return list.map((f) => f.id);
|
|
1022
|
+
}, [ui.state.isPreview, fields, allFlat]);
|
|
1023
|
+
return /* @__PURE__ */ React12.createElement(
|
|
1024
|
+
"div",
|
|
1025
|
+
{
|
|
1026
|
+
className: "w-full max-w-4xl mx-auto rounded-lg overflow-y-auto max-h-[calc(100svh-19rem)] lg:max-h-[calc(100dvh-15rem)] custom-scrollbar pr-2",
|
|
1027
|
+
onClick: () => !ui.state.isPreview && ui.selectedFieldId.clear()
|
|
1028
|
+
},
|
|
1029
|
+
visibleIds.length === 0 ? /* @__PURE__ */ React12.createElement(EmptyState, null) : visibleIds.map((id) => /* @__PURE__ */ React12.createElement(FieldRow, { key: id, id }))
|
|
1030
|
+
);
|
|
1031
|
+
}
|
|
1032
|
+
var FieldRow = React12.memo(function FieldRow2({ id }) {
|
|
1033
|
+
const field = useFormStore7(React12.useCallback((s) => s.byId[id], [id]));
|
|
1034
|
+
if (!field) return null;
|
|
1035
|
+
const FieldComponent = getFieldComponent(field.fieldType);
|
|
1036
|
+
if (!FieldComponent) return null;
|
|
1037
|
+
return /* @__PURE__ */ React12.createElement("div", { className: "mb-1.5", "data-field-type": field.fieldType, "data-field-id": field.id }, /* @__PURE__ */ React12.createElement(FieldComponent, { field }));
|
|
1038
|
+
});
|
|
1039
|
+
function EmptyState() {
|
|
1040
|
+
return /* @__PURE__ */ React12.createElement("div", { className: "flex flex-col items-center justify-center h-72 bg-gradient-to-br from-gray-50 to-gray-100 border-2 border-dashed border-blue-200 rounded-xl shadow-md text-center px-8 py-10" }, /* @__PURE__ */ React12.createElement("div", { className: "text-xl font-semibold text-gray-700 mb-2" }, "Start building your questionnaire"), /* @__PURE__ */ React12.createElement("div", { className: "text-base text-gray-500" }, "Add tools with ", /* @__PURE__ */ React12.createElement("span", { className: "font-semibold text-blue-500" }, "Tool Panel"), " on the left.", /* @__PURE__ */ React12.createElement("br", null), "Select fields to edit on the ", /* @__PURE__ */ React12.createElement("span", { className: "font-semibold text-blue-500" }, "Edit Panel"), " on the left."));
|
|
1041
|
+
}
|
|
1042
|
+
|
|
1043
|
+
// src/components/desktopLayout/Layout.jsx
|
|
1044
|
+
import { useUIApi as useUIApi6 } from "@mieweb/forms-engine";
|
|
1045
|
+
function Layout({ selectedField }) {
|
|
1046
|
+
const ui = useUIApi6();
|
|
1047
|
+
const isPreview = ui.state.isPreview;
|
|
1048
|
+
const isEditModalOpen = ui.state.isEditModalOpen;
|
|
1049
|
+
const panelResetKey = ui.state.panelResetKey;
|
|
1050
|
+
const editMode = !isPreview;
|
|
1051
|
+
const cols = editMode ? "lg:grid-cols-[280px_minmax(0,1fr)_320px]" : "lg:grid-cols-[minmax(0,1fr)]";
|
|
1052
|
+
return /* @__PURE__ */ React13.createElement("div", { className: "w-full max-w-6xl mx-auto px-4 h-fit rounded-lg mt-2" }, /* @__PURE__ */ React13.createElement("div", { className: `grid grid-cols-1 ${cols} gap-3 h-full items-start` }, editMode && /* @__PURE__ */ React13.createElement("div", { className: "hidden lg:block" }, /* @__PURE__ */ React13.createElement(ToolPanel_default, null)), /* @__PURE__ */ React13.createElement("div", null, /* @__PURE__ */ React13.createElement(FormBuilderMain, null)), editMode && /* @__PURE__ */ React13.createElement("div", { className: "hidden lg:block" }, /* @__PURE__ */ React13.createElement(EditPanel, { key: panelResetKey })), editMode && /* @__PURE__ */ React13.createElement("div", { className: "lg:hidden" }, isEditModalOpen && selectedField && /* @__PURE__ */ React13.createElement(
|
|
1053
|
+
"div",
|
|
1054
|
+
{
|
|
1055
|
+
className: "fixed inset-0 top-5 z-50 flex items-center justify-center bg-transparent/30 backdrop-blur-sm p-4",
|
|
1056
|
+
onClick: () => ui.modal.set(false)
|
|
1057
|
+
},
|
|
1058
|
+
/* @__PURE__ */ React13.createElement(
|
|
1059
|
+
"div",
|
|
1060
|
+
{
|
|
1061
|
+
className: "w-full max-w-md mx-auto relative bg-white rounded-lg overflow-hidden",
|
|
1062
|
+
onClick: (e) => e.stopPropagation()
|
|
1063
|
+
},
|
|
1064
|
+
/* @__PURE__ */ React13.createElement(
|
|
1065
|
+
"button",
|
|
1066
|
+
{
|
|
1067
|
+
className: "absolute top-3 right-7 text-gray-500",
|
|
1068
|
+
onClick: () => ui.modal.set(false)
|
|
1069
|
+
},
|
|
1070
|
+
/* @__PURE__ */ React13.createElement("span", { className: "text-3xl" }, "\xD7")
|
|
1071
|
+
),
|
|
1072
|
+
/* @__PURE__ */ React13.createElement(EditPanel, { key: panelResetKey })
|
|
1073
|
+
)
|
|
1074
|
+
))));
|
|
1075
|
+
}
|
|
1076
|
+
|
|
1077
|
+
// src/QuestionnaireEditor.jsx
|
|
1078
|
+
import {
|
|
1079
|
+
useFormStore as useFormStore8,
|
|
1080
|
+
useFormApi as useFormApi3,
|
|
1081
|
+
useUIApi as useUIApi7,
|
|
1082
|
+
useFieldsArray as useFieldsArray4
|
|
1083
|
+
} from "@mieweb/forms-engine";
|
|
1084
|
+
function QuestionnaireEditor({
|
|
1085
|
+
initialFields,
|
|
1086
|
+
onChange,
|
|
1087
|
+
className = "",
|
|
1088
|
+
showHeader = true,
|
|
1089
|
+
showMobileToolbar = true,
|
|
1090
|
+
startInPreview = false
|
|
1091
|
+
}) {
|
|
1092
|
+
const ui = useUIApi7();
|
|
1093
|
+
const formStoreInitialized = useRef2(false);
|
|
1094
|
+
useEffect3(() => {
|
|
1095
|
+
if (formStoreInitialized.current) return;
|
|
1096
|
+
if (Array.isArray(initialFields) && initialFields.length) {
|
|
1097
|
+
useFormStore8.getState().replaceAll(initialFields);
|
|
1098
|
+
}
|
|
1099
|
+
ui.preview.set(!!startInPreview);
|
|
1100
|
+
formStoreInitialized.current = true;
|
|
1101
|
+
}, [initialFields, startInPreview, ui.preview]);
|
|
1102
|
+
useEffect3(() => {
|
|
1103
|
+
if (!onChange) return;
|
|
1104
|
+
const unsub = useFormStore8.subscribe((s) => {
|
|
1105
|
+
const arr = s.flatArray ? s.flatArray() : Object.values(s.byId);
|
|
1106
|
+
onChange(arr);
|
|
1107
|
+
});
|
|
1108
|
+
return unsub;
|
|
1109
|
+
}, [onChange]);
|
|
1110
|
+
const selectedField = useFormStore8(
|
|
1111
|
+
React14.useCallback(
|
|
1112
|
+
(s) => ui.selectedFieldId.value ? s.byId[ui.selectedFieldId.value] : null,
|
|
1113
|
+
[ui.selectedFieldId.value]
|
|
1114
|
+
)
|
|
1115
|
+
);
|
|
1116
|
+
return /* @__PURE__ */ React14.createElement("div", { className: `qb-editor-root min-h-screen bg-gray-100 font-titillium ${className}` }, showHeader && /* @__PURE__ */ React14.createElement(Header, null), showMobileToolbar && /* @__PURE__ */ React14.createElement("div", { className: "lg:hidden" }, /* @__PURE__ */ React14.createElement(MobileToolBar, null)), /* @__PURE__ */ React14.createElement(Layout, { selectedField }));
|
|
1117
|
+
}
|
|
1118
|
+
export {
|
|
1119
|
+
QuestionnaireEditor
|
|
1120
|
+
};
|