@btst/components 0.0.0-stage → 1.0.0-beta.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/LICENSE.md +17 -0
- package/dist/_private/ui/styles/markdown-editor.css +31 -0
- package/dist/auto-form/index.cjs +8 -0
- package/dist/auto-form/index.d.cts +26 -0
- package/dist/auto-form/index.d.mts +26 -0
- package/dist/auto-form/index.d.ts +26 -0
- package/dist/auto-form/index.mjs +1 -0
- package/dist/empty/index.cjs +12 -0
- package/dist/empty/index.d.cts +15 -0
- package/dist/empty/index.d.mts +15 -0
- package/dist/empty/index.d.ts +15 -0
- package/dist/empty/index.mjs +1 -0
- package/dist/form-builder/index.cjs +16 -0
- package/dist/form-builder/index.d.cts +395 -0
- package/dist/form-builder/index.d.mts +395 -0
- package/dist/form-builder/index.d.ts +395 -0
- package/dist/form-builder/index.mjs +4 -0
- package/dist/markdown/index.cjs +9 -0
- package/dist/markdown/index.d.cts +46 -0
- package/dist/markdown/index.d.mts +46 -0
- package/dist/markdown/index.d.ts +46 -0
- package/dist/markdown/index.mjs +2 -0
- package/dist/multi-select/index.cjs +7 -0
- package/dist/multi-select/index.d.cts +78 -0
- package/dist/multi-select/index.d.mts +78 -0
- package/dist/multi-select/index.d.ts +78 -0
- package/dist/multi-select/index.mjs +1 -0
- package/dist/search-select/index.cjs +7 -0
- package/dist/search-select/index.d.cts +16 -0
- package/dist/search-select/index.d.mts +16 -0
- package/dist/search-select/index.d.ts +16 -0
- package/dist/search-select/index.mjs +1 -0
- package/dist/shared/components.BdvBiegH.d.cts +291 -0
- package/dist/shared/components.BdvBiegH.d.mts +291 -0
- package/dist/shared/components.BdvBiegH.d.ts +291 -0
- package/dist/shared/components.CZMWR72v.d.cts +10 -0
- package/dist/shared/components.CZMWR72v.d.mts +10 -0
- package/dist/shared/components.CZMWR72v.d.ts +10 -0
- package/dist/stepped-auto-form/index.cjs +7 -0
- package/dist/stepped-auto-form/index.d.cts +37 -0
- package/dist/stepped-auto-form/index.d.mts +37 -0
- package/dist/stepped-auto-form/index.d.ts +37 -0
- package/dist/stepped-auto-form/index.mjs +1 -0
- package/dist/style.css +440 -0
- package/dist/ui/src/components/accordion.cjs +81 -0
- package/dist/ui/src/components/accordion.mjs +62 -0
- package/dist/ui/src/components/auto-form/common/label.cjs +18 -0
- package/dist/ui/src/components/auto-form/common/label.mjs +16 -0
- package/dist/ui/src/components/auto-form/common/tooltip.cjs +9 -0
- package/dist/ui/src/components/auto-form/common/tooltip.mjs +7 -0
- package/dist/ui/src/components/auto-form/config.cjs +40 -0
- package/dist/ui/src/components/auto-form/config.mjs +37 -0
- package/dist/ui/src/components/auto-form/dependencies.cjs +48 -0
- package/dist/ui/src/components/auto-form/dependencies.mjs +46 -0
- package/dist/ui/src/components/auto-form/fields/array.cjs +266 -0
- package/dist/ui/src/components/auto-form/fields/array.mjs +264 -0
- package/dist/ui/src/components/auto-form/fields/checkbox.cjs +38 -0
- package/dist/ui/src/components/auto-form/fields/checkbox.mjs +36 -0
- package/dist/ui/src/components/auto-form/fields/date.cjs +67 -0
- package/dist/ui/src/components/auto-form/fields/date.mjs +65 -0
- package/dist/ui/src/components/auto-form/fields/enum.cjs +73 -0
- package/dist/ui/src/components/auto-form/fields/enum.mjs +71 -0
- package/dist/ui/src/components/auto-form/fields/input.cjs +32 -0
- package/dist/ui/src/components/auto-form/fields/input.mjs +30 -0
- package/dist/ui/src/components/auto-form/fields/number.cjs +31 -0
- package/dist/ui/src/components/auto-form/fields/number.mjs +29 -0
- package/dist/ui/src/components/auto-form/fields/object.cjs +233 -0
- package/dist/ui/src/components/auto-form/fields/object.mjs +217 -0
- package/dist/ui/src/components/auto-form/fields/radio-group.cjs +67 -0
- package/dist/ui/src/components/auto-form/fields/radio-group.mjs +65 -0
- package/dist/ui/src/components/auto-form/fields/switch.cjs +38 -0
- package/dist/ui/src/components/auto-form/fields/switch.mjs +36 -0
- package/dist/ui/src/components/auto-form/fields/textarea.cjs +31 -0
- package/dist/ui/src/components/auto-form/fields/textarea.mjs +29 -0
- package/dist/ui/src/components/auto-form/helpers.cjs +270 -0
- package/dist/ui/src/components/auto-form/helpers.mjs +259 -0
- package/dist/ui/src/components/auto-form/index.cjs +102 -0
- package/dist/ui/src/components/auto-form/index.mjs +93 -0
- package/dist/ui/src/components/auto-form/stepped-auto-form.cjs +374 -0
- package/dist/ui/src/components/auto-form/stepped-auto-form.mjs +368 -0
- package/dist/ui/src/components/auto-form/types.cjs +11 -0
- package/dist/ui/src/components/auto-form/types.mjs +9 -0
- package/dist/ui/src/components/badge.cjs +42 -0
- package/dist/ui/src/components/badge.mjs +39 -0
- package/dist/ui/src/components/button.cjs +56 -0
- package/dist/ui/src/components/button.mjs +53 -0
- package/dist/ui/src/components/calendar.cjs +207 -0
- package/dist/ui/src/components/calendar.mjs +190 -0
- package/dist/ui/src/components/checkbox.cjs +48 -0
- package/dist/ui/src/components/checkbox.mjs +32 -0
- package/dist/ui/src/components/command.cjs +118 -0
- package/dist/ui/src/components/command.mjs +111 -0
- package/dist/ui/src/components/date-picker.cjs +41 -0
- package/dist/ui/src/components/date-picker.mjs +39 -0
- package/dist/ui/src/components/dialog.cjs +141 -0
- package/dist/ui/src/components/dialog.mjs +118 -0
- package/dist/ui/src/components/empty.cjs +104 -0
- package/dist/ui/src/components/empty.mjs +97 -0
- package/dist/ui/src/components/form-builder/canvas.cjs +111 -0
- package/dist/ui/src/components/form-builder/canvas.mjs +109 -0
- package/dist/ui/src/components/form-builder/components/index.cjs +571 -0
- package/dist/ui/src/components/form-builder/components/index.mjs +554 -0
- package/dist/ui/src/components/form-builder/edit-field-dialog.cjs +131 -0
- package/dist/ui/src/components/form-builder/edit-field-dialog.mjs +129 -0
- package/dist/ui/src/components/form-builder/form-preview.cjs +73 -0
- package/dist/ui/src/components/form-builder/form-preview.mjs +71 -0
- package/dist/ui/src/components/form-builder/index.cjs +350 -0
- package/dist/ui/src/components/form-builder/index.mjs +344 -0
- package/dist/ui/src/components/form-builder/nested-field-editor-dialog.cjs +263 -0
- package/dist/ui/src/components/form-builder/nested-field-editor-dialog.mjs +261 -0
- package/dist/ui/src/components/form-builder/palette.cjs +52 -0
- package/dist/ui/src/components/form-builder/palette.mjs +49 -0
- package/dist/ui/src/components/form-builder/schema-utils.cjs +128 -0
- package/dist/ui/src/components/form-builder/schema-utils.mjs +122 -0
- package/dist/ui/src/components/form-builder/sortable-field.cjs +151 -0
- package/dist/ui/src/components/form-builder/sortable-field.mjs +148 -0
- package/dist/ui/src/components/form-builder/step-tabs.cjs +180 -0
- package/dist/ui/src/components/form-builder/step-tabs.mjs +178 -0
- package/dist/ui/src/components/form-builder/types.cjs +7 -0
- package/dist/ui/src/components/form-builder/types.mjs +5 -0
- package/dist/ui/src/components/form-builder/validation-schemas.cjs +67 -0
- package/dist/ui/src/components/form-builder/validation-schemas.mjs +56 -0
- package/dist/ui/src/components/form.cjs +120 -0
- package/dist/ui/src/components/form.mjs +98 -0
- package/dist/ui/src/components/input.cjs +23 -0
- package/dist/ui/src/components/input.mjs +21 -0
- package/dist/ui/src/components/label.cjs +39 -0
- package/dist/ui/src/components/label.mjs +23 -0
- package/dist/ui/src/components/markdown-content.cjs +334 -0
- package/dist/ui/src/components/markdown-content.mjs +325 -0
- package/dist/ui/src/components/markdown-editor.cjs +180 -0
- package/dist/ui/src/components/markdown-editor.mjs +178 -0
- package/dist/ui/src/components/multi-select.cjs +555 -0
- package/dist/ui/src/components/multi-select.mjs +540 -0
- package/dist/ui/src/components/popover.cjs +60 -0
- package/dist/ui/src/components/popover.mjs +42 -0
- package/dist/ui/src/components/radio-group.cjs +62 -0
- package/dist/ui/src/components/radio-group.mjs +45 -0
- package/dist/ui/src/components/search-select.cjs +99 -0
- package/dist/ui/src/components/search-select.mjs +83 -0
- package/dist/ui/src/components/select.cjs +161 -0
- package/dist/ui/src/components/select.mjs +139 -0
- package/dist/ui/src/components/separator.cjs +43 -0
- package/dist/ui/src/components/separator.mjs +27 -0
- package/dist/ui/src/components/switch.cjs +48 -0
- package/dist/ui/src/components/switch.mjs +32 -0
- package/dist/ui/src/components/tabs.cjs +84 -0
- package/dist/ui/src/components/tabs.mjs +65 -0
- package/dist/ui/src/components/textarea.cjs +20 -0
- package/dist/ui/src/components/textarea.mjs +18 -0
- package/dist/ui/src/hooks/use-debounce.cjs +16 -0
- package/dist/ui/src/hooks/use-debounce.mjs +14 -0
- package/dist/ui/src/lib/schema-converter.cjs +94 -0
- package/dist/ui/src/lib/schema-converter.mjs +92 -0
- package/dist/ui/src/lib/throttle.cjs +56 -0
- package/dist/ui/src/lib/throttle.mjs +54 -0
- package/dist/ui/src/lib/utils.cjs +10 -0
- package/dist/ui/src/lib/utils.mjs +8 -0
- package/dist/ui.css +131 -0
- package/package.json +176 -3
- package/README.md +0 -3
|
@@ -0,0 +1,261 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
3
|
+
import { useId, useMemo, useState, useCallback } from 'react';
|
|
4
|
+
import { createPortal } from 'react-dom';
|
|
5
|
+
import { useSensors, useSensor, PointerSensor, TouchSensor, KeyboardSensor, DndContext, closestCenter, DragOverlay } from '@dnd-kit/core';
|
|
6
|
+
import { snapCenterToCursor } from '@dnd-kit/modifiers';
|
|
7
|
+
import { sortableKeyboardCoordinates, arrayMove } from '@dnd-kit/sortable';
|
|
8
|
+
import { FolderOpen, List } from 'lucide-react';
|
|
9
|
+
import { Button } from '../button.mjs';
|
|
10
|
+
import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription, DialogFooter } from '../dialog.mjs';
|
|
11
|
+
import { Palette, PaletteDragOverlay } from './palette.mjs';
|
|
12
|
+
import { Canvas } from './canvas.mjs';
|
|
13
|
+
import { EditFieldDialog } from './edit-field-dialog.mjs';
|
|
14
|
+
import { FieldDragOverlay } from './sortable-field.mjs';
|
|
15
|
+
import { getComponentByType } from './components/index.mjs';
|
|
16
|
+
import { generateFieldId } from './schema-utils.mjs';
|
|
17
|
+
|
|
18
|
+
function NestedFieldEditorDialog({
|
|
19
|
+
open,
|
|
20
|
+
onOpenChange,
|
|
21
|
+
field,
|
|
22
|
+
components,
|
|
23
|
+
onSave
|
|
24
|
+
}) {
|
|
25
|
+
const dndContextId = useId();
|
|
26
|
+
const initialFields = useMemo(() => {
|
|
27
|
+
if (!field) return [];
|
|
28
|
+
if (field.type === "object") return field.children || [];
|
|
29
|
+
if (field.type === "array") return field.itemTemplate || [];
|
|
30
|
+
return [];
|
|
31
|
+
}, [field]);
|
|
32
|
+
const [nestedFields, setNestedFields] = useState(initialFields);
|
|
33
|
+
const [activeId, setActiveId] = useState(null);
|
|
34
|
+
const [editDialogFieldId, setEditDialogFieldId] = useState(null);
|
|
35
|
+
const [prevField, setPrevField] = useState(field);
|
|
36
|
+
const [prevOpen, setPrevOpen] = useState(open);
|
|
37
|
+
if (prevField !== field || open && !prevOpen) {
|
|
38
|
+
setPrevField(field);
|
|
39
|
+
setPrevOpen(open);
|
|
40
|
+
setNestedFields(initialFields);
|
|
41
|
+
setEditDialogFieldId(null);
|
|
42
|
+
}
|
|
43
|
+
if (prevOpen !== open) {
|
|
44
|
+
setPrevOpen(open);
|
|
45
|
+
}
|
|
46
|
+
const sensors = useSensors(
|
|
47
|
+
useSensor(PointerSensor, {
|
|
48
|
+
activationConstraint: { distance: 8 }
|
|
49
|
+
}),
|
|
50
|
+
useSensor(TouchSensor, {
|
|
51
|
+
activationConstraint: { distance: 8 }
|
|
52
|
+
}),
|
|
53
|
+
useSensor(KeyboardSensor, {
|
|
54
|
+
coordinateGetter: sortableKeyboardCoordinates
|
|
55
|
+
})
|
|
56
|
+
);
|
|
57
|
+
const activeDragData = useMemo(() => {
|
|
58
|
+
if (!activeId) return null;
|
|
59
|
+
const idStr = String(activeId);
|
|
60
|
+
if (idStr.startsWith("palette-")) {
|
|
61
|
+
const componentType = idStr.replace("palette-", "");
|
|
62
|
+
return {
|
|
63
|
+
type: "palette",
|
|
64
|
+
componentType,
|
|
65
|
+
component: getComponentByType(componentType, components)
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
const nestedField = nestedFields.find((f) => f.id === idStr);
|
|
69
|
+
if (nestedField) {
|
|
70
|
+
return {
|
|
71
|
+
type: "field",
|
|
72
|
+
field: nestedField,
|
|
73
|
+
component: getComponentByType(nestedField.type, components)
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
return null;
|
|
77
|
+
}, [activeId, nestedFields, components]);
|
|
78
|
+
const handleDragStart = (event) => {
|
|
79
|
+
setActiveId(event.active.id);
|
|
80
|
+
};
|
|
81
|
+
const handleDragEnd = (event) => {
|
|
82
|
+
const { active, over } = event;
|
|
83
|
+
setActiveId(null);
|
|
84
|
+
if (!over) return;
|
|
85
|
+
const activeData = active.data.current;
|
|
86
|
+
if (activeData?.type === "palette") {
|
|
87
|
+
const { componentType } = activeData;
|
|
88
|
+
const component = getComponentByType(componentType, components);
|
|
89
|
+
if (component) {
|
|
90
|
+
const newField = {
|
|
91
|
+
id: generateFieldId(componentType),
|
|
92
|
+
type: componentType,
|
|
93
|
+
props: {
|
|
94
|
+
label: component.defaultProps.label || component.label,
|
|
95
|
+
...component.defaultProps
|
|
96
|
+
}
|
|
97
|
+
};
|
|
98
|
+
let insertIndex = nestedFields.length;
|
|
99
|
+
const overId = String(over.id);
|
|
100
|
+
if (overId === "drop-zone-start") {
|
|
101
|
+
insertIndex = 0;
|
|
102
|
+
} else if (overId.startsWith("drop-zone-")) {
|
|
103
|
+
const fieldId = overId.replace("drop-zone-", "");
|
|
104
|
+
const overIndex = nestedFields.findIndex((f) => f.id === fieldId);
|
|
105
|
+
if (overIndex !== -1) {
|
|
106
|
+
insertIndex = overIndex + 1;
|
|
107
|
+
}
|
|
108
|
+
} else if (overId !== "canvas") {
|
|
109
|
+
const overIndex = nestedFields.findIndex((f) => f.id === overId);
|
|
110
|
+
if (overIndex !== -1) {
|
|
111
|
+
insertIndex = overIndex + 1;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
const newFields = [...nestedFields];
|
|
115
|
+
newFields.splice(insertIndex, 0, newField);
|
|
116
|
+
setNestedFields(newFields);
|
|
117
|
+
}
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
if (activeData?.type === "field" && active.id !== over.id) {
|
|
121
|
+
const oldIndex = nestedFields.findIndex((f) => f.id === active.id);
|
|
122
|
+
const newIndex = nestedFields.findIndex((f) => f.id === over.id);
|
|
123
|
+
if (oldIndex !== -1 && newIndex !== -1) {
|
|
124
|
+
setNestedFields(arrayMove(nestedFields, oldIndex, newIndex));
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
};
|
|
128
|
+
const handleDeleteField = useCallback((id) => {
|
|
129
|
+
setNestedFields((fields) => fields.filter((f) => f.id !== id));
|
|
130
|
+
}, []);
|
|
131
|
+
const handleEditField = useCallback((id) => {
|
|
132
|
+
setEditDialogFieldId(id);
|
|
133
|
+
}, []);
|
|
134
|
+
const handleUpdateField = useCallback(
|
|
135
|
+
(id, props, newId) => {
|
|
136
|
+
if (newId && newId !== id) {
|
|
137
|
+
const idExists = nestedFields.some((f) => f.id === newId && f.id !== id);
|
|
138
|
+
if (idExists) {
|
|
139
|
+
console.warn(`Cannot rename field "${id}" to "${newId}": a field with that ID already exists`);
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
setNestedFields(
|
|
144
|
+
(fields) => fields.map((f) => {
|
|
145
|
+
if (f.id !== id) return f;
|
|
146
|
+
return {
|
|
147
|
+
...f,
|
|
148
|
+
id: newId || f.id,
|
|
149
|
+
props: { ...f.props, ...props }
|
|
150
|
+
};
|
|
151
|
+
})
|
|
152
|
+
);
|
|
153
|
+
},
|
|
154
|
+
[nestedFields]
|
|
155
|
+
);
|
|
156
|
+
const handleSave = useCallback(() => {
|
|
157
|
+
if (!field) return;
|
|
158
|
+
onSave(field.id, nestedFields);
|
|
159
|
+
onOpenChange(false);
|
|
160
|
+
}, [field, nestedFields, onSave, onOpenChange]);
|
|
161
|
+
if (!field) return null;
|
|
162
|
+
const isObjectField = field.type === "object";
|
|
163
|
+
const Icon = isObjectField ? FolderOpen : List;
|
|
164
|
+
const fieldTypeLabel = isObjectField ? "Field Group" : "Repeating Group";
|
|
165
|
+
const nestedLabel = isObjectField ? "nested fields" : "item fields";
|
|
166
|
+
const editDialogField = nestedFields.find((f) => f.id === editDialogFieldId) || null;
|
|
167
|
+
const editDialogComponent = editDialogField ? getComponentByType(editDialogField.type, components) || null : null;
|
|
168
|
+
return /* @__PURE__ */ jsxs(Dialog, { open, onOpenChange, children: [
|
|
169
|
+
/* @__PURE__ */ jsxs(DialogContent, { className: "max-w-6xl! max-h-[90vh] overflow-hidden flex flex-col", children: [
|
|
170
|
+
/* @__PURE__ */ jsxs(DialogHeader, { children: [
|
|
171
|
+
/* @__PURE__ */ jsxs(DialogTitle, { className: "flex items-center gap-2", children: [
|
|
172
|
+
/* @__PURE__ */ jsx(Icon, { className: "h-5 w-5 text-muted-foreground" }),
|
|
173
|
+
"Configure ",
|
|
174
|
+
fieldTypeLabel,
|
|
175
|
+
": ",
|
|
176
|
+
field.props.label
|
|
177
|
+
] }),
|
|
178
|
+
/* @__PURE__ */ jsxs(DialogDescription, { children: [
|
|
179
|
+
"Add and arrange the ",
|
|
180
|
+
nestedLabel,
|
|
181
|
+
" for this ",
|
|
182
|
+
fieldTypeLabel.toLowerCase(),
|
|
183
|
+
".",
|
|
184
|
+
isObjectField ? " These fields will be grouped together." : " Each item in the array will have these fields."
|
|
185
|
+
] })
|
|
186
|
+
] }),
|
|
187
|
+
/* @__PURE__ */ jsx("div", { className: "flex-1 overflow-hidden", children: /* @__PURE__ */ jsxs(
|
|
188
|
+
DndContext,
|
|
189
|
+
{
|
|
190
|
+
id: dndContextId,
|
|
191
|
+
sensors,
|
|
192
|
+
collisionDetection: closestCenter,
|
|
193
|
+
onDragStart: handleDragStart,
|
|
194
|
+
onDragEnd: handleDragEnd,
|
|
195
|
+
children: [
|
|
196
|
+
/* @__PURE__ */ jsxs("div", { className: "flex h-[50vh] gap-4", children: [
|
|
197
|
+
/* @__PURE__ */ jsx("div", { className: "w-48 overflow-auto shrink-0 border-r pr-4", children: /* @__PURE__ */ jsx(Palette, { components }) }),
|
|
198
|
+
/* @__PURE__ */ jsx("div", { className: "flex-1 overflow-auto", children: /* @__PURE__ */ jsx(
|
|
199
|
+
Canvas,
|
|
200
|
+
{
|
|
201
|
+
fields: nestedFields,
|
|
202
|
+
components,
|
|
203
|
+
onEditField: handleEditField,
|
|
204
|
+
onDeleteField: handleDeleteField,
|
|
205
|
+
isDraggingFromPalette: activeDragData?.type === "palette",
|
|
206
|
+
steps: [],
|
|
207
|
+
activeStepIndex: 0,
|
|
208
|
+
onActiveStepChange: () => {
|
|
209
|
+
},
|
|
210
|
+
onAddStep: () => {
|
|
211
|
+
},
|
|
212
|
+
onDeleteStep: () => {
|
|
213
|
+
},
|
|
214
|
+
onRenameStep: () => {
|
|
215
|
+
},
|
|
216
|
+
hideStepControls: true
|
|
217
|
+
}
|
|
218
|
+
) })
|
|
219
|
+
] }),
|
|
220
|
+
typeof document !== "undefined" && createPortal(
|
|
221
|
+
/* @__PURE__ */ jsxs(DragOverlay, { dropAnimation: null, modifiers: [snapCenterToCursor], children: [
|
|
222
|
+
activeDragData?.type === "palette" && activeDragData.component && /* @__PURE__ */ jsx(PaletteDragOverlay, { component: activeDragData.component }),
|
|
223
|
+
activeDragData?.type === "field" && activeDragData.field && /* @__PURE__ */ jsx(
|
|
224
|
+
FieldDragOverlay,
|
|
225
|
+
{
|
|
226
|
+
field: activeDragData.field,
|
|
227
|
+
component: activeDragData.component
|
|
228
|
+
}
|
|
229
|
+
)
|
|
230
|
+
] }),
|
|
231
|
+
document.body
|
|
232
|
+
)
|
|
233
|
+
]
|
|
234
|
+
}
|
|
235
|
+
) }),
|
|
236
|
+
/* @__PURE__ */ jsxs(DialogFooter, { className: "border-t pt-4", children: [
|
|
237
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 text-sm text-muted-foreground mr-auto", children: [
|
|
238
|
+
nestedFields.length,
|
|
239
|
+
" ",
|
|
240
|
+
nestedFields.length === 1 ? "field" : "fields"
|
|
241
|
+
] }),
|
|
242
|
+
/* @__PURE__ */ jsx(Button, { variant: "outline", onClick: () => onOpenChange(false), children: "Cancel" }),
|
|
243
|
+
/* @__PURE__ */ jsx(Button, { onClick: handleSave, children: "Save Fields" })
|
|
244
|
+
] })
|
|
245
|
+
] }),
|
|
246
|
+
/* @__PURE__ */ jsx(
|
|
247
|
+
EditFieldDialog,
|
|
248
|
+
{
|
|
249
|
+
open: editDialogFieldId !== null,
|
|
250
|
+
onOpenChange: (open2) => !open2 && setEditDialogFieldId(null),
|
|
251
|
+
field: editDialogField,
|
|
252
|
+
component: editDialogComponent,
|
|
253
|
+
onUpdate: handleUpdateField,
|
|
254
|
+
steps: [],
|
|
255
|
+
allFieldIds: nestedFields.map((f) => f.id)
|
|
256
|
+
}
|
|
257
|
+
)
|
|
258
|
+
] });
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
export { NestedFieldEditorDialog };
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
const jsxRuntime = require('react/jsx-runtime');
|
|
5
|
+
const core = require('@dnd-kit/core');
|
|
6
|
+
const utils = require('../../lib/utils.cjs');
|
|
7
|
+
|
|
8
|
+
function PaletteItem({ component }) {
|
|
9
|
+
const { attributes, listeners, setNodeRef, isDragging } = core.useDraggable({
|
|
10
|
+
id: `palette-${component.type}`,
|
|
11
|
+
data: {
|
|
12
|
+
type: "palette",
|
|
13
|
+
componentType: component.type
|
|
14
|
+
}
|
|
15
|
+
});
|
|
16
|
+
const Icon = component.icon;
|
|
17
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
18
|
+
"div",
|
|
19
|
+
{
|
|
20
|
+
ref: setNodeRef,
|
|
21
|
+
...listeners,
|
|
22
|
+
...attributes,
|
|
23
|
+
className: utils.cn(
|
|
24
|
+
"flex items-center gap-3 p-3 rounded-lg border bg-card cursor-grab active:cursor-grabbing",
|
|
25
|
+
"hover:bg-accent hover:border-accent-foreground/20 transition-colors",
|
|
26
|
+
"touch-none select-none",
|
|
27
|
+
isDragging && "opacity-50"
|
|
28
|
+
),
|
|
29
|
+
children: [
|
|
30
|
+
Icon && /* @__PURE__ */ jsxRuntime.jsx(Icon, { className: "h-4 w-4 text-muted-foreground shrink-0" }),
|
|
31
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm font-medium", children: component.label })
|
|
32
|
+
]
|
|
33
|
+
}
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
function Palette({ components, className }) {
|
|
37
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: utils.cn("flex flex-col gap-2", className), "data-testid": "form-builder-palette", children: [
|
|
38
|
+
/* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-sm font-semibold text-muted-foreground mb-2", children: "Components" }),
|
|
39
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-row flex-wrap lg:flex-col gap-2", children: components.map((component) => /* @__PURE__ */ jsxRuntime.jsx(PaletteItem, { component }, component.type)) })
|
|
40
|
+
] });
|
|
41
|
+
}
|
|
42
|
+
function PaletteDragOverlay({ component }) {
|
|
43
|
+
if (!component) return null;
|
|
44
|
+
const Icon = component.icon;
|
|
45
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3 p-3 rounded-lg border bg-card shadow-lg cursor-grabbing", children: [
|
|
46
|
+
Icon && /* @__PURE__ */ jsxRuntime.jsx(Icon, { className: "h-4 w-4 text-muted-foreground shrink-0" }),
|
|
47
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm font-medium", children: component.label })
|
|
48
|
+
] });
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
exports.Palette = Palette;
|
|
52
|
+
exports.PaletteDragOverlay = PaletteDragOverlay;
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
3
|
+
import { useDraggable } from '@dnd-kit/core';
|
|
4
|
+
import { cn } from '../../lib/utils.mjs';
|
|
5
|
+
|
|
6
|
+
function PaletteItem({ component }) {
|
|
7
|
+
const { attributes, listeners, setNodeRef, isDragging } = useDraggable({
|
|
8
|
+
id: `palette-${component.type}`,
|
|
9
|
+
data: {
|
|
10
|
+
type: "palette",
|
|
11
|
+
componentType: component.type
|
|
12
|
+
}
|
|
13
|
+
});
|
|
14
|
+
const Icon = component.icon;
|
|
15
|
+
return /* @__PURE__ */ jsxs(
|
|
16
|
+
"div",
|
|
17
|
+
{
|
|
18
|
+
ref: setNodeRef,
|
|
19
|
+
...listeners,
|
|
20
|
+
...attributes,
|
|
21
|
+
className: cn(
|
|
22
|
+
"flex items-center gap-3 p-3 rounded-lg border bg-card cursor-grab active:cursor-grabbing",
|
|
23
|
+
"hover:bg-accent hover:border-accent-foreground/20 transition-colors",
|
|
24
|
+
"touch-none select-none",
|
|
25
|
+
isDragging && "opacity-50"
|
|
26
|
+
),
|
|
27
|
+
children: [
|
|
28
|
+
Icon && /* @__PURE__ */ jsx(Icon, { className: "h-4 w-4 text-muted-foreground shrink-0" }),
|
|
29
|
+
/* @__PURE__ */ jsx("span", { className: "text-sm font-medium", children: component.label })
|
|
30
|
+
]
|
|
31
|
+
}
|
|
32
|
+
);
|
|
33
|
+
}
|
|
34
|
+
function Palette({ components, className }) {
|
|
35
|
+
return /* @__PURE__ */ jsxs("div", { className: cn("flex flex-col gap-2", className), "data-testid": "form-builder-palette", children: [
|
|
36
|
+
/* @__PURE__ */ jsx("h3", { className: "text-sm font-semibold text-muted-foreground mb-2", children: "Components" }),
|
|
37
|
+
/* @__PURE__ */ jsx("div", { className: "flex flex-row flex-wrap lg:flex-col gap-2", children: components.map((component) => /* @__PURE__ */ jsx(PaletteItem, { component }, component.type)) })
|
|
38
|
+
] });
|
|
39
|
+
}
|
|
40
|
+
function PaletteDragOverlay({ component }) {
|
|
41
|
+
if (!component) return null;
|
|
42
|
+
const Icon = component.icon;
|
|
43
|
+
return /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3 p-3 rounded-lg border bg-card shadow-lg cursor-grabbing", children: [
|
|
44
|
+
Icon && /* @__PURE__ */ jsx(Icon, { className: "h-4 w-4 text-muted-foreground shrink-0" }),
|
|
45
|
+
/* @__PURE__ */ jsx("span", { className: "text-sm font-medium", children: component.label })
|
|
46
|
+
] });
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export { Palette, PaletteDragOverlay };
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const helpers = require('../auto-form/helpers.cjs');
|
|
4
|
+
|
|
5
|
+
function fieldsToProperties(fields, components, includeStepGroup = false) {
|
|
6
|
+
const properties = {};
|
|
7
|
+
for (const field of fields) {
|
|
8
|
+
const component = components.find((c) => c.type === field.type);
|
|
9
|
+
if (!component) {
|
|
10
|
+
console.warn(`Unknown component type: ${field.type}`);
|
|
11
|
+
continue;
|
|
12
|
+
}
|
|
13
|
+
const isRequired = field.props.required ?? false;
|
|
14
|
+
const schemaProp = component.toJSONSchema(field.props, isRequired);
|
|
15
|
+
if (includeStepGroup && field.stepGroup !== void 0) {
|
|
16
|
+
schemaProp.stepGroup = field.stepGroup;
|
|
17
|
+
}
|
|
18
|
+
if (field.type === "object" && field.children && field.children.length > 0) {
|
|
19
|
+
schemaProp.properties = fieldsToProperties(field.children, components, false);
|
|
20
|
+
const childRequired = field.children.filter((child) => child.props.required).map((child) => child.id);
|
|
21
|
+
if (childRequired.length > 0) {
|
|
22
|
+
schemaProp.required = childRequired;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
if (field.type === "array" && field.itemTemplate && field.itemTemplate.length > 0) {
|
|
26
|
+
schemaProp.items = {
|
|
27
|
+
type: "object",
|
|
28
|
+
properties: fieldsToProperties(field.itemTemplate, components, false)
|
|
29
|
+
};
|
|
30
|
+
const itemRequired = field.itemTemplate.filter((item) => item.props.required).map((item) => item.id);
|
|
31
|
+
if (itemRequired.length > 0) {
|
|
32
|
+
schemaProp.items.required = itemRequired;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
properties[field.id] = schemaProp;
|
|
36
|
+
}
|
|
37
|
+
return properties;
|
|
38
|
+
}
|
|
39
|
+
function getRequiredFieldIds(fields) {
|
|
40
|
+
return fields.filter((f) => f.props.required).map((f) => f.id);
|
|
41
|
+
}
|
|
42
|
+
function fieldsToJSONSchema(fields, components, steps) {
|
|
43
|
+
const hasMultipleSteps = steps && steps.length > 1;
|
|
44
|
+
const properties = fieldsToProperties(fields, components, hasMultipleSteps);
|
|
45
|
+
const required = getRequiredFieldIds(fields);
|
|
46
|
+
return {
|
|
47
|
+
type: "object",
|
|
48
|
+
properties,
|
|
49
|
+
...required.length > 0 ? { required } : {},
|
|
50
|
+
// Only include steps if there are multiple steps
|
|
51
|
+
...hasMultipleSteps ? { steps } : {}
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
function propertiesToFields(properties, requiredSet, components, stepGroupMap) {
|
|
55
|
+
const fields = [];
|
|
56
|
+
for (const [key, prop] of Object.entries(properties)) {
|
|
57
|
+
const isRequired = requiredSet.has(key);
|
|
58
|
+
let field = null;
|
|
59
|
+
for (const component of components) {
|
|
60
|
+
field = component.fromJSONSchema(prop, key, isRequired);
|
|
61
|
+
if (field) {
|
|
62
|
+
break;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
const resolvedStepGroup = prop.stepGroup !== void 0 ? prop.stepGroup : stepGroupMap?.[key];
|
|
66
|
+
if (field) {
|
|
67
|
+
if (resolvedStepGroup !== void 0) {
|
|
68
|
+
field.stepGroup = resolvedStepGroup;
|
|
69
|
+
}
|
|
70
|
+
if (field.type === "object" && prop.properties) {
|
|
71
|
+
const childRequiredSet = new Set(prop.required || []);
|
|
72
|
+
field.children = propertiesToFields(prop.properties, childRequiredSet, components);
|
|
73
|
+
}
|
|
74
|
+
if (field.type === "array" && prop.items?.properties) {
|
|
75
|
+
const itemRequiredSet = new Set(prop.items.required || []);
|
|
76
|
+
field.itemTemplate = propertiesToFields(prop.items.properties, itemRequiredSet, components);
|
|
77
|
+
}
|
|
78
|
+
fields.push(field);
|
|
79
|
+
} else {
|
|
80
|
+
console.warn(`Could not parse JSON Schema property: ${key}`, prop);
|
|
81
|
+
fields.push({
|
|
82
|
+
id: key,
|
|
83
|
+
type: "text",
|
|
84
|
+
props: {
|
|
85
|
+
label: prop.label || prop.title || helpers.beautifyObjectName(key),
|
|
86
|
+
description: prop.description,
|
|
87
|
+
placeholder: prop.placeholder,
|
|
88
|
+
required: isRequired
|
|
89
|
+
},
|
|
90
|
+
// Include stepGroup even for fallback fields
|
|
91
|
+
...resolvedStepGroup !== void 0 ? { stepGroup: resolvedStepGroup } : {}
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
return fields;
|
|
96
|
+
}
|
|
97
|
+
function jsonSchemaToFieldsAndSteps(schema, components) {
|
|
98
|
+
if (!schema || !schema.properties) {
|
|
99
|
+
return { fields: [], steps: [] };
|
|
100
|
+
}
|
|
101
|
+
const requiredSet = new Set(schema.required || []);
|
|
102
|
+
const fields = propertiesToFields(
|
|
103
|
+
schema.properties,
|
|
104
|
+
requiredSet,
|
|
105
|
+
components,
|
|
106
|
+
schema.stepGroupMap
|
|
107
|
+
);
|
|
108
|
+
const steps = schema.steps || [];
|
|
109
|
+
return { fields, steps };
|
|
110
|
+
}
|
|
111
|
+
function generateFieldId(prefix = "field") {
|
|
112
|
+
return `${prefix}_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
|
|
113
|
+
}
|
|
114
|
+
function generateStepId() {
|
|
115
|
+
return `step_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
|
|
116
|
+
}
|
|
117
|
+
function createStep(index) {
|
|
118
|
+
return {
|
|
119
|
+
id: generateStepId(),
|
|
120
|
+
title: `Step ${index + 1}`
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
exports.createStep = createStep;
|
|
125
|
+
exports.fieldsToJSONSchema = fieldsToJSONSchema;
|
|
126
|
+
exports.generateFieldId = generateFieldId;
|
|
127
|
+
exports.generateStepId = generateStepId;
|
|
128
|
+
exports.jsonSchemaToFieldsAndSteps = jsonSchemaToFieldsAndSteps;
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import { beautifyObjectName } from '../auto-form/helpers.mjs';
|
|
2
|
+
|
|
3
|
+
function fieldsToProperties(fields, components, includeStepGroup = false) {
|
|
4
|
+
const properties = {};
|
|
5
|
+
for (const field of fields) {
|
|
6
|
+
const component = components.find((c) => c.type === field.type);
|
|
7
|
+
if (!component) {
|
|
8
|
+
console.warn(`Unknown component type: ${field.type}`);
|
|
9
|
+
continue;
|
|
10
|
+
}
|
|
11
|
+
const isRequired = field.props.required ?? false;
|
|
12
|
+
const schemaProp = component.toJSONSchema(field.props, isRequired);
|
|
13
|
+
if (includeStepGroup && field.stepGroup !== void 0) {
|
|
14
|
+
schemaProp.stepGroup = field.stepGroup;
|
|
15
|
+
}
|
|
16
|
+
if (field.type === "object" && field.children && field.children.length > 0) {
|
|
17
|
+
schemaProp.properties = fieldsToProperties(field.children, components, false);
|
|
18
|
+
const childRequired = field.children.filter((child) => child.props.required).map((child) => child.id);
|
|
19
|
+
if (childRequired.length > 0) {
|
|
20
|
+
schemaProp.required = childRequired;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
if (field.type === "array" && field.itemTemplate && field.itemTemplate.length > 0) {
|
|
24
|
+
schemaProp.items = {
|
|
25
|
+
type: "object",
|
|
26
|
+
properties: fieldsToProperties(field.itemTemplate, components, false)
|
|
27
|
+
};
|
|
28
|
+
const itemRequired = field.itemTemplate.filter((item) => item.props.required).map((item) => item.id);
|
|
29
|
+
if (itemRequired.length > 0) {
|
|
30
|
+
schemaProp.items.required = itemRequired;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
properties[field.id] = schemaProp;
|
|
34
|
+
}
|
|
35
|
+
return properties;
|
|
36
|
+
}
|
|
37
|
+
function getRequiredFieldIds(fields) {
|
|
38
|
+
return fields.filter((f) => f.props.required).map((f) => f.id);
|
|
39
|
+
}
|
|
40
|
+
function fieldsToJSONSchema(fields, components, steps) {
|
|
41
|
+
const hasMultipleSteps = steps && steps.length > 1;
|
|
42
|
+
const properties = fieldsToProperties(fields, components, hasMultipleSteps);
|
|
43
|
+
const required = getRequiredFieldIds(fields);
|
|
44
|
+
return {
|
|
45
|
+
type: "object",
|
|
46
|
+
properties,
|
|
47
|
+
...required.length > 0 ? { required } : {},
|
|
48
|
+
// Only include steps if there are multiple steps
|
|
49
|
+
...hasMultipleSteps ? { steps } : {}
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
function propertiesToFields(properties, requiredSet, components, stepGroupMap) {
|
|
53
|
+
const fields = [];
|
|
54
|
+
for (const [key, prop] of Object.entries(properties)) {
|
|
55
|
+
const isRequired = requiredSet.has(key);
|
|
56
|
+
let field = null;
|
|
57
|
+
for (const component of components) {
|
|
58
|
+
field = component.fromJSONSchema(prop, key, isRequired);
|
|
59
|
+
if (field) {
|
|
60
|
+
break;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
const resolvedStepGroup = prop.stepGroup !== void 0 ? prop.stepGroup : stepGroupMap?.[key];
|
|
64
|
+
if (field) {
|
|
65
|
+
if (resolvedStepGroup !== void 0) {
|
|
66
|
+
field.stepGroup = resolvedStepGroup;
|
|
67
|
+
}
|
|
68
|
+
if (field.type === "object" && prop.properties) {
|
|
69
|
+
const childRequiredSet = new Set(prop.required || []);
|
|
70
|
+
field.children = propertiesToFields(prop.properties, childRequiredSet, components);
|
|
71
|
+
}
|
|
72
|
+
if (field.type === "array" && prop.items?.properties) {
|
|
73
|
+
const itemRequiredSet = new Set(prop.items.required || []);
|
|
74
|
+
field.itemTemplate = propertiesToFields(prop.items.properties, itemRequiredSet, components);
|
|
75
|
+
}
|
|
76
|
+
fields.push(field);
|
|
77
|
+
} else {
|
|
78
|
+
console.warn(`Could not parse JSON Schema property: ${key}`, prop);
|
|
79
|
+
fields.push({
|
|
80
|
+
id: key,
|
|
81
|
+
type: "text",
|
|
82
|
+
props: {
|
|
83
|
+
label: prop.label || prop.title || beautifyObjectName(key),
|
|
84
|
+
description: prop.description,
|
|
85
|
+
placeholder: prop.placeholder,
|
|
86
|
+
required: isRequired
|
|
87
|
+
},
|
|
88
|
+
// Include stepGroup even for fallback fields
|
|
89
|
+
...resolvedStepGroup !== void 0 ? { stepGroup: resolvedStepGroup } : {}
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
return fields;
|
|
94
|
+
}
|
|
95
|
+
function jsonSchemaToFieldsAndSteps(schema, components) {
|
|
96
|
+
if (!schema || !schema.properties) {
|
|
97
|
+
return { fields: [], steps: [] };
|
|
98
|
+
}
|
|
99
|
+
const requiredSet = new Set(schema.required || []);
|
|
100
|
+
const fields = propertiesToFields(
|
|
101
|
+
schema.properties,
|
|
102
|
+
requiredSet,
|
|
103
|
+
components,
|
|
104
|
+
schema.stepGroupMap
|
|
105
|
+
);
|
|
106
|
+
const steps = schema.steps || [];
|
|
107
|
+
return { fields, steps };
|
|
108
|
+
}
|
|
109
|
+
function generateFieldId(prefix = "field") {
|
|
110
|
+
return `${prefix}_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
|
|
111
|
+
}
|
|
112
|
+
function generateStepId() {
|
|
113
|
+
return `step_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
|
|
114
|
+
}
|
|
115
|
+
function createStep(index) {
|
|
116
|
+
return {
|
|
117
|
+
id: generateStepId(),
|
|
118
|
+
title: `Step ${index + 1}`
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
export { createStep, fieldsToJSONSchema, generateFieldId, generateStepId, jsonSchemaToFieldsAndSteps };
|