@btst/stack 1.10.0 → 1.11.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/dist/packages/ui/src/components/minimal-tiptap/utils.cjs +15 -11
- package/dist/packages/ui/src/components/minimal-tiptap/utils.mjs +15 -11
- package/dist/packages/ui/src/components/ui-builder/index.cjs +9 -7
- package/dist/packages/ui/src/components/ui-builder/index.mjs +9 -7
- package/dist/packages/ui/src/components/ui-builder/internal/canvas/auto-frame.cjs +6 -3
- package/dist/packages/ui/src/components/ui-builder/internal/canvas/auto-frame.mjs +6 -3
- package/dist/packages/ui/src/components/ui-builder/internal/components/add-component-popover.cjs +228 -48
- package/dist/packages/ui/src/components/ui-builder/internal/components/add-component-popover.mjs +228 -48
- package/dist/packages/ui/src/components/ui-builder/internal/components/element-selector.cjs +1 -1
- package/dist/packages/ui/src/components/ui-builder/internal/components/element-selector.mjs +1 -1
- package/dist/packages/ui/src/components/ui-builder/internal/components/error-fallback.cjs +4 -2
- package/dist/packages/ui/src/components/ui-builder/internal/components/error-fallback.mjs +4 -2
- package/dist/packages/ui/src/components/ui-builder/internal/components/multi-select.cjs +6 -3
- package/dist/packages/ui/src/components/ui-builder/internal/components/multi-select.mjs +6 -3
- package/dist/packages/ui/src/components/ui-builder/internal/dnd/draggable-new-component.cjs +67 -0
- package/dist/packages/ui/src/components/ui-builder/internal/dnd/draggable-new-component.mjs +62 -0
- package/dist/packages/ui/src/components/ui-builder/internal/dnd/drop-zone.cjs +181 -37
- package/dist/packages/ui/src/components/ui-builder/internal/dnd/drop-zone.mjs +181 -38
- package/dist/packages/ui/src/components/ui-builder/internal/editor-panel.cjs +1 -1
- package/dist/packages/ui/src/components/ui-builder/internal/editor-panel.mjs +1 -1
- package/dist/packages/ui/src/components/ui-builder/internal/form-fields/classname-control/classname-group-control.cjs +1 -1
- package/dist/packages/ui/src/components/ui-builder/internal/form-fields/classname-control/classname-group-control.mjs +1 -1
- package/dist/packages/ui/src/components/ui-builder/internal/form-fields/classname-control/classname-item-control.cjs +9 -2
- package/dist/packages/ui/src/components/ui-builder/internal/form-fields/classname-control/classname-item-control.mjs +9 -2
- package/dist/packages/ui/src/components/ui-builder/internal/form-fields/iconname-field.cjs +3 -2
- package/dist/packages/ui/src/components/ui-builder/internal/form-fields/iconname-field.mjs +3 -2
- package/dist/packages/ui/src/components/ui-builder/internal/layers-panel.cjs +1 -1
- package/dist/packages/ui/src/components/ui-builder/internal/layers-panel.mjs +1 -1
- package/dist/packages/ui/src/components/ui-builder/internal/props-panel.cjs +17 -5
- package/dist/packages/ui/src/components/ui-builder/internal/props-panel.mjs +17 -5
- package/dist/packages/ui/src/components/ui-builder/internal/utils/render-utils.cjs +70 -16
- package/dist/packages/ui/src/components/ui-builder/internal/utils/render-utils.mjs +73 -20
- package/dist/packages/ui/src/lib/ui-builder/context/dnd-context-colission-utils.cjs +14 -9
- package/dist/packages/ui/src/lib/ui-builder/context/dnd-context-colission-utils.mjs +14 -9
- package/dist/packages/ui/src/lib/ui-builder/context/dnd-context.cjs +38 -10
- package/dist/packages/ui/src/lib/ui-builder/context/dnd-context.mjs +35 -11
- package/dist/packages/ui/src/lib/ui-builder/context/dnd-contexts.cjs +1 -0
- package/dist/packages/ui/src/lib/ui-builder/context/dnd-contexts.mjs +1 -0
- package/dist/packages/ui/src/lib/ui-builder/context/drag-overlay.cjs +7 -4
- package/dist/packages/ui/src/lib/ui-builder/context/drag-overlay.mjs +7 -4
- package/dist/packages/ui/src/lib/ui-builder/hooks/use-auto-scroll.cjs +4 -4
- package/dist/packages/ui/src/lib/ui-builder/hooks/use-auto-scroll.mjs +4 -4
- package/dist/packages/ui/src/lib/ui-builder/hooks/use-dnd-event-handlers.cjs +53 -16
- package/dist/packages/ui/src/lib/ui-builder/hooks/use-dnd-event-handlers.mjs +53 -16
- package/dist/packages/ui/src/lib/ui-builder/hooks/use-drop-validation.cjs +23 -7
- package/dist/packages/ui/src/lib/ui-builder/hooks/use-drop-validation.mjs +23 -7
- package/dist/packages/ui/src/lib/ui-builder/registry/form-field-overrides.cjs +110 -11
- package/dist/packages/ui/src/lib/ui-builder/registry/form-field-overrides.mjs +111 -13
- package/dist/packages/ui/src/lib/ui-builder/store/editor-store.cjs +3 -2
- package/dist/packages/ui/src/lib/ui-builder/store/editor-store.mjs +3 -2
- package/dist/packages/ui/src/lib/ui-builder/store/layer-store.cjs +53 -7
- package/dist/packages/ui/src/lib/ui-builder/store/layer-store.mjs +54 -8
- package/dist/packages/ui/src/lib/ui-builder/store/layer-utils.cjs +4 -3
- package/dist/packages/ui/src/lib/ui-builder/store/layer-utils.mjs +4 -3
- package/dist/packages/ui/src/lib/ui-builder/utils/variable-resolver.cjs +12 -0
- package/dist/packages/ui/src/lib/ui-builder/utils/variable-resolver.mjs +12 -1
- package/dist/plugins/ui-builder/client/components/index.d.cts +1 -1
- package/dist/plugins/ui-builder/client/components/index.d.mts +1 -1
- package/dist/plugins/ui-builder/client/components/index.d.ts +1 -1
- package/dist/plugins/ui-builder/client/hooks/index.d.cts +2 -2
- package/dist/plugins/ui-builder/client/hooks/index.d.mts +2 -2
- package/dist/plugins/ui-builder/client/hooks/index.d.ts +2 -2
- package/dist/plugins/ui-builder/client/index.d.cts +17 -7
- package/dist/plugins/ui-builder/client/index.d.mts +17 -7
- package/dist/plugins/ui-builder/client/index.d.ts +17 -7
- package/dist/plugins/ui-builder/index.d.cts +2 -2
- package/dist/plugins/ui-builder/index.d.mts +2 -2
- package/dist/plugins/ui-builder/index.d.ts +2 -2
- package/dist/shared/{stack.BSM2cgoq.d.cts → stack.BYysGdHl.d.cts} +1 -1
- package/dist/shared/{stack.CqfZWfjJ.d.cts → stack.BdJFrdyt.d.cts} +8 -2
- package/dist/shared/{stack.e1FN86dE.d.mts → stack.ChVuHi5e.d.mts} +8 -2
- package/dist/shared/{stack.CLtOoAqF.d.mts → stack.DYCFcnkL.d.mts} +1 -1
- package/dist/shared/{stack.MMntCVZZ.d.ts → stack.EhM4pmtN.d.ts} +8 -2
- package/dist/shared/{stack.BD1m-4yB.d.ts → stack.kFbDspnF.d.ts} +1 -1
- package/package.json +1 -1
|
@@ -93,23 +93,27 @@ const validateFileOrBase64 = (input, options, originalFile, validFiles, errors)
|
|
|
93
93
|
};
|
|
94
94
|
const checkTypeAndSize = (input, { allowedMimeTypes, maxFileSize }) => {
|
|
95
95
|
const mimeType = input instanceof File ? input.type : base64MimeType(input);
|
|
96
|
-
|
|
96
|
+
let size;
|
|
97
|
+
if (input instanceof File) {
|
|
98
|
+
size = input.size;
|
|
99
|
+
} else {
|
|
100
|
+
const base64Data = input.split(",")[1];
|
|
101
|
+
if (!base64Data || base64Data.trim() === "") {
|
|
102
|
+
return { isValidType: true, isValidSize: false };
|
|
103
|
+
}
|
|
104
|
+
try {
|
|
105
|
+
size = atob(base64Data).length;
|
|
106
|
+
} catch {
|
|
107
|
+
return { isValidType: true, isValidSize: false };
|
|
108
|
+
}
|
|
109
|
+
}
|
|
97
110
|
const isValidType = allowedMimeTypes.length === 0 || allowedMimeTypes.includes(mimeType) || allowedMimeTypes.includes(`${mimeType.split("/")[0]}/*`);
|
|
98
111
|
const isValidSize = !maxFileSize || size <= maxFileSize;
|
|
99
112
|
return { isValidType, isValidSize };
|
|
100
113
|
};
|
|
101
|
-
const getBase64Size = (input) => {
|
|
102
|
-
if (input.includes(",")) {
|
|
103
|
-
const base64Part = input.split(",")[1];
|
|
104
|
-
if (base64Part) {
|
|
105
|
-
return atob(base64Part).length;
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
return atob(input).length;
|
|
109
|
-
};
|
|
110
114
|
const base64MimeType = (encoded) => {
|
|
111
115
|
const result = encoded.match(/data:([a-zA-Z0-9]+\/[a-zA-Z0-9-.+]+).*,.*/);
|
|
112
|
-
return result && result.length > 1 ? result[1] : "unknown";
|
|
116
|
+
return result && result.length > 1 ? result[1] ?? "unknown" : "unknown";
|
|
113
117
|
};
|
|
114
118
|
const isBase64 = (str) => {
|
|
115
119
|
if (str.startsWith("data:")) {
|
|
@@ -91,23 +91,27 @@ const validateFileOrBase64 = (input, options, originalFile, validFiles, errors)
|
|
|
91
91
|
};
|
|
92
92
|
const checkTypeAndSize = (input, { allowedMimeTypes, maxFileSize }) => {
|
|
93
93
|
const mimeType = input instanceof File ? input.type : base64MimeType(input);
|
|
94
|
-
|
|
94
|
+
let size;
|
|
95
|
+
if (input instanceof File) {
|
|
96
|
+
size = input.size;
|
|
97
|
+
} else {
|
|
98
|
+
const base64Data = input.split(",")[1];
|
|
99
|
+
if (!base64Data || base64Data.trim() === "") {
|
|
100
|
+
return { isValidType: true, isValidSize: false };
|
|
101
|
+
}
|
|
102
|
+
try {
|
|
103
|
+
size = atob(base64Data).length;
|
|
104
|
+
} catch {
|
|
105
|
+
return { isValidType: true, isValidSize: false };
|
|
106
|
+
}
|
|
107
|
+
}
|
|
95
108
|
const isValidType = allowedMimeTypes.length === 0 || allowedMimeTypes.includes(mimeType) || allowedMimeTypes.includes(`${mimeType.split("/")[0]}/*`);
|
|
96
109
|
const isValidSize = !maxFileSize || size <= maxFileSize;
|
|
97
110
|
return { isValidType, isValidSize };
|
|
98
111
|
};
|
|
99
|
-
const getBase64Size = (input) => {
|
|
100
|
-
if (input.includes(",")) {
|
|
101
|
-
const base64Part = input.split(",")[1];
|
|
102
|
-
if (base64Part) {
|
|
103
|
-
return atob(base64Part).length;
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
return atob(input).length;
|
|
107
|
-
};
|
|
108
112
|
const base64MimeType = (encoded) => {
|
|
109
113
|
const result = encoded.match(/data:([a-zA-Z0-9]+\/[a-zA-Z0-9-.+]+).*,.*/);
|
|
110
|
-
return result && result.length > 1 ? result[1] : "unknown";
|
|
114
|
+
return result && result.length > 1 ? result[1] ?? "unknown" : "unknown";
|
|
111
115
|
};
|
|
112
116
|
const isBase64 = (str) => {
|
|
113
117
|
if (str.startsWith("data:")) {
|
|
@@ -38,7 +38,8 @@ const UIBuilder = ({
|
|
|
38
38
|
allowPagesDeletion = true,
|
|
39
39
|
navLeftChildren,
|
|
40
40
|
navRightChildren,
|
|
41
|
-
showExport = true
|
|
41
|
+
showExport = true,
|
|
42
|
+
blocks
|
|
42
43
|
}) => {
|
|
43
44
|
const layerStore$1 = useStore.useStore(layerStore.useLayerStore, (state) => state);
|
|
44
45
|
const editorStore$1 = useStore.useStore(editorStore.useEditorStore, (state) => state);
|
|
@@ -57,7 +58,7 @@ const UIBuilder = ({
|
|
|
57
58
|
}, [userPanelConfig, memoizedDefaultTabsContent, navLeftChildren, navRightChildren, showExport]);
|
|
58
59
|
React.useEffect(() => {
|
|
59
60
|
if (editorStore$1 && componentRegistry && !editorStoreInitialized) {
|
|
60
|
-
editorStore$1.initialize(componentRegistry, persistLayerStore, allowPagesCreation, allowPagesDeletion, allowVariableEditing);
|
|
61
|
+
editorStore$1.initialize(componentRegistry, persistLayerStore, allowPagesCreation, allowPagesDeletion, allowVariableEditing, blocks);
|
|
61
62
|
setEditorStoreInitialized(true);
|
|
62
63
|
}
|
|
63
64
|
}, [
|
|
@@ -67,7 +68,8 @@ const UIBuilder = ({
|
|
|
67
68
|
persistLayerStore,
|
|
68
69
|
allowPagesCreation,
|
|
69
70
|
allowPagesDeletion,
|
|
70
|
-
allowVariableEditing
|
|
71
|
+
allowVariableEditing,
|
|
72
|
+
blocks
|
|
71
73
|
]);
|
|
72
74
|
React.useEffect(() => {
|
|
73
75
|
if (layerStore$1 && editorStore$1) {
|
|
@@ -144,11 +146,11 @@ function MainLayout({ panelConfig }) {
|
|
|
144
146
|
});
|
|
145
147
|
React.useEffect(() => {
|
|
146
148
|
const editorPanel = mainPanels.find((panel) => panel.title === "UI Editor");
|
|
147
|
-
const currentPanel = mainPanels.find((panel) => panel.title === selectedPanel
|
|
149
|
+
const currentPanel = mainPanels.find((panel) => panel.title === selectedPanel?.title);
|
|
148
150
|
if (!currentPanel) {
|
|
149
151
|
setSelectedPanel(editorPanel || mainPanels[0]);
|
|
150
152
|
}
|
|
151
|
-
}, [mainPanels, selectedPanel
|
|
153
|
+
}, [mainPanels, selectedPanel?.title]);
|
|
152
154
|
const handlePanelClickById = React.useCallback((e) => {
|
|
153
155
|
const panelIndex = parseInt(e.currentTarget.dataset.panelIndex || "0");
|
|
154
156
|
setSelectedPanel(mainPanels[panelIndex]);
|
|
@@ -180,11 +182,11 @@ function MainLayout({ panelConfig }) {
|
|
|
180
182
|
}
|
|
181
183
|
) }),
|
|
182
184
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex size-full flex-col md:hidden overflow-hidden ", children: [
|
|
183
|
-
selectedPanel
|
|
185
|
+
selectedPanel?.content,
|
|
184
186
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute bottom-4 left-4 right-4 z-50", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex justify-center rounded-full bg-primary p-2 shadow-lg", children: mainPanels.map((panel, index) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
185
187
|
button.Button,
|
|
186
188
|
{
|
|
187
|
-
variant: selectedPanel
|
|
189
|
+
variant: selectedPanel?.title !== panel.title ? "default" : "secondary",
|
|
188
190
|
size: "sm",
|
|
189
191
|
className: "flex-1",
|
|
190
192
|
"data-panel-index": index,
|
|
@@ -30,7 +30,8 @@ const UIBuilder = ({
|
|
|
30
30
|
allowPagesDeletion = true,
|
|
31
31
|
navLeftChildren,
|
|
32
32
|
navRightChildren,
|
|
33
|
-
showExport = true
|
|
33
|
+
showExport = true,
|
|
34
|
+
blocks
|
|
34
35
|
}) => {
|
|
35
36
|
const layerStore = useStore(useLayerStore, (state) => state);
|
|
36
37
|
const editorStore = useStore(useEditorStore, (state) => state);
|
|
@@ -49,7 +50,7 @@ const UIBuilder = ({
|
|
|
49
50
|
}, [userPanelConfig, memoizedDefaultTabsContent, navLeftChildren, navRightChildren, showExport]);
|
|
50
51
|
useEffect(() => {
|
|
51
52
|
if (editorStore && componentRegistry && !editorStoreInitialized) {
|
|
52
|
-
editorStore.initialize(componentRegistry, persistLayerStore, allowPagesCreation, allowPagesDeletion, allowVariableEditing);
|
|
53
|
+
editorStore.initialize(componentRegistry, persistLayerStore, allowPagesCreation, allowPagesDeletion, allowVariableEditing, blocks);
|
|
53
54
|
setEditorStoreInitialized(true);
|
|
54
55
|
}
|
|
55
56
|
}, [
|
|
@@ -59,7 +60,8 @@ const UIBuilder = ({
|
|
|
59
60
|
persistLayerStore,
|
|
60
61
|
allowPagesCreation,
|
|
61
62
|
allowPagesDeletion,
|
|
62
|
-
allowVariableEditing
|
|
63
|
+
allowVariableEditing,
|
|
64
|
+
blocks
|
|
63
65
|
]);
|
|
64
66
|
useEffect(() => {
|
|
65
67
|
if (layerStore && editorStore) {
|
|
@@ -136,11 +138,11 @@ function MainLayout({ panelConfig }) {
|
|
|
136
138
|
});
|
|
137
139
|
useEffect(() => {
|
|
138
140
|
const editorPanel = mainPanels.find((panel) => panel.title === "UI Editor");
|
|
139
|
-
const currentPanel = mainPanels.find((panel) => panel.title === selectedPanel
|
|
141
|
+
const currentPanel = mainPanels.find((panel) => panel.title === selectedPanel?.title);
|
|
140
142
|
if (!currentPanel) {
|
|
141
143
|
setSelectedPanel(editorPanel || mainPanels[0]);
|
|
142
144
|
}
|
|
143
|
-
}, [mainPanels, selectedPanel
|
|
145
|
+
}, [mainPanels, selectedPanel?.title]);
|
|
144
146
|
const handlePanelClickById = useCallback((e) => {
|
|
145
147
|
const panelIndex = parseInt(e.currentTarget.dataset.panelIndex || "0");
|
|
146
148
|
setSelectedPanel(mainPanels[panelIndex]);
|
|
@@ -172,11 +174,11 @@ function MainLayout({ panelConfig }) {
|
|
|
172
174
|
}
|
|
173
175
|
) }),
|
|
174
176
|
/* @__PURE__ */ jsxs("div", { className: "flex size-full flex-col md:hidden overflow-hidden ", children: [
|
|
175
|
-
selectedPanel
|
|
177
|
+
selectedPanel?.content,
|
|
176
178
|
/* @__PURE__ */ jsx("div", { className: "absolute bottom-4 left-4 right-4 z-50", children: /* @__PURE__ */ jsx("div", { className: "flex justify-center rounded-full bg-primary p-2 shadow-lg", children: mainPanels.map((panel, index) => /* @__PURE__ */ jsx(
|
|
177
179
|
Button,
|
|
178
180
|
{
|
|
179
|
-
variant: selectedPanel
|
|
181
|
+
variant: selectedPanel?.title !== panel.title ? "default" : "secondary",
|
|
180
182
|
size: "sm",
|
|
181
183
|
className: "flex-1",
|
|
182
184
|
"data-panel-index": index,
|
|
@@ -97,7 +97,10 @@ const CopyHostStyles = ({
|
|
|
97
97
|
console.log(
|
|
98
98
|
`Tried to add an element that was already mirrored. Updating instead...`
|
|
99
99
|
);
|
|
100
|
-
elements[index$1]
|
|
100
|
+
const element = elements[index$1];
|
|
101
|
+
if (element?.mirror) {
|
|
102
|
+
element.mirror.innerText = el.innerText;
|
|
103
|
+
}
|
|
101
104
|
return;
|
|
102
105
|
}
|
|
103
106
|
const mirror = await mirrorEl(el);
|
|
@@ -165,13 +168,13 @@ const CopyHostStyles = ({
|
|
|
165
168
|
const parentComputedStyle = win.parent.getComputedStyle(parentHtml);
|
|
166
169
|
for (let i = 0; i < parentComputedStyle.length; i++) {
|
|
167
170
|
const property = parentComputedStyle[i];
|
|
168
|
-
if (property
|
|
171
|
+
if (property?.startsWith("--")) {
|
|
169
172
|
const value = parentComputedStyle.getPropertyValue(property);
|
|
170
173
|
doc.documentElement.style.setProperty(property, value);
|
|
171
174
|
doc.body.style.setProperty(property, value);
|
|
172
175
|
}
|
|
173
176
|
}
|
|
174
|
-
parentBody
|
|
177
|
+
parentBody?.classList.forEach((className) => {
|
|
175
178
|
if (className.includes("font-") || className === "antialiased" || className.includes("__variable")) {
|
|
176
179
|
doc.body.classList.add(className);
|
|
177
180
|
}
|
|
@@ -93,7 +93,10 @@ const CopyHostStyles = ({
|
|
|
93
93
|
console.log(
|
|
94
94
|
`Tried to add an element that was already mirrored. Updating instead...`
|
|
95
95
|
);
|
|
96
|
-
elements[index]
|
|
96
|
+
const element = elements[index];
|
|
97
|
+
if (element?.mirror) {
|
|
98
|
+
element.mirror.innerText = el.innerText;
|
|
99
|
+
}
|
|
97
100
|
return;
|
|
98
101
|
}
|
|
99
102
|
const mirror = await mirrorEl(el);
|
|
@@ -161,13 +164,13 @@ const CopyHostStyles = ({
|
|
|
161
164
|
const parentComputedStyle = win.parent.getComputedStyle(parentHtml);
|
|
162
165
|
for (let i = 0; i < parentComputedStyle.length; i++) {
|
|
163
166
|
const property = parentComputedStyle[i];
|
|
164
|
-
if (property
|
|
167
|
+
if (property?.startsWith("--")) {
|
|
165
168
|
const value = parentComputedStyle.getPropertyValue(property);
|
|
166
169
|
doc.documentElement.style.setProperty(property, value);
|
|
167
170
|
doc.body.style.setProperty(property, value);
|
|
168
171
|
}
|
|
169
172
|
}
|
|
170
|
-
parentBody
|
|
173
|
+
parentBody?.classList.forEach((className) => {
|
|
171
174
|
if (className.includes("font-") || className === "antialiased" || className.includes("__variable")) {
|
|
172
175
|
doc.body.classList.add(className);
|
|
173
176
|
}
|
package/dist/packages/ui/src/components/ui-builder/internal/components/add-component-popover.cjs
CHANGED
|
@@ -10,6 +10,7 @@ const editorStore = require('../../../../lib/ui-builder/store/editor-store.cjs')
|
|
|
10
10
|
const utils = require('../../../../lib/utils.cjs');
|
|
11
11
|
const layerRenderer = require('../../layer-renderer.cjs');
|
|
12
12
|
const layerUtils = require('../../../../lib/ui-builder/store/layer-utils.cjs');
|
|
13
|
+
const draggableNewComponent = require('../dnd/draggable-new-component.cjs');
|
|
13
14
|
|
|
14
15
|
function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'default' in e ? e.default : e; }
|
|
15
16
|
|
|
@@ -17,22 +18,44 @@ const React__default = /*#__PURE__*/_interopDefaultCompat(React);
|
|
|
17
18
|
|
|
18
19
|
const fallback = /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-full h-full bg-muted rounded border animate-pulse" });
|
|
19
20
|
const previewLayerCache = /* @__PURE__ */ new Map();
|
|
21
|
+
function isValidChildOfParent(componentRegistry, componentType, parentType) {
|
|
22
|
+
const def = componentRegistry[componentType];
|
|
23
|
+
if (!def?.childOf) {
|
|
24
|
+
return true;
|
|
25
|
+
}
|
|
26
|
+
if (!parentType) {
|
|
27
|
+
return false;
|
|
28
|
+
}
|
|
29
|
+
return def.childOf.includes(parentType);
|
|
30
|
+
}
|
|
31
|
+
function isChildOnlyComponent(componentRegistry, componentType) {
|
|
32
|
+
const def = componentRegistry[componentType];
|
|
33
|
+
return Boolean(def?.childOf);
|
|
34
|
+
}
|
|
20
35
|
function AddComponentsPopover({
|
|
21
36
|
className,
|
|
22
37
|
children,
|
|
23
38
|
addPosition,
|
|
24
39
|
parentLayerId,
|
|
25
40
|
onOpenChange,
|
|
41
|
+
enableDragToCanvas = false,
|
|
26
42
|
onChange
|
|
27
43
|
}) {
|
|
28
44
|
const [open, setOpen] = React__default.useState(false);
|
|
45
|
+
const [activeView, setActiveView] = React__default.useState("components");
|
|
29
46
|
const componentRegistry = editorStore.useEditorStore((state) => state.registry);
|
|
47
|
+
const blocks = editorStore.useEditorStore((state) => state.blocks);
|
|
48
|
+
const findLayerById = layerStore.useLayerStore((state) => state.findLayerById);
|
|
49
|
+
const parentLayerType = React.useMemo(() => {
|
|
50
|
+
const parentLayer = findLayerById(parentLayerId);
|
|
51
|
+
return parentLayer?.type;
|
|
52
|
+
}, [findLayerById, parentLayerId]);
|
|
30
53
|
const groupedOptions = React.useMemo(() => {
|
|
31
|
-
const componentOptions = Object.keys(componentRegistry).map((name) => ({
|
|
54
|
+
const componentOptions = Object.keys(componentRegistry).filter((name) => isValidChildOfParent(componentRegistry, name, parentLayerType)).map((name) => ({
|
|
32
55
|
value: name,
|
|
33
56
|
label: name,
|
|
34
57
|
type: "component",
|
|
35
|
-
from: componentRegistry[name]
|
|
58
|
+
from: componentRegistry[name]?.from
|
|
36
59
|
}));
|
|
37
60
|
return componentOptions.reduce(
|
|
38
61
|
(acc, option) => {
|
|
@@ -41,16 +64,34 @@ function AddComponentsPopover({
|
|
|
41
64
|
if (!acc[group]) {
|
|
42
65
|
acc[group] = [];
|
|
43
66
|
}
|
|
44
|
-
acc[group]
|
|
67
|
+
acc[group]?.push(option);
|
|
45
68
|
return acc;
|
|
46
69
|
},
|
|
47
70
|
{}
|
|
48
71
|
);
|
|
49
|
-
}, [componentRegistry]);
|
|
72
|
+
}, [componentRegistry, parentLayerType]);
|
|
50
73
|
const categories = React.useMemo(() => {
|
|
51
74
|
return Object.keys(groupedOptions);
|
|
52
75
|
}, [groupedOptions]);
|
|
76
|
+
const groupedBlocks = React.useMemo(() => {
|
|
77
|
+
if (!blocks) return {};
|
|
78
|
+
return Object.values(blocks).reduce(
|
|
79
|
+
(acc, block) => {
|
|
80
|
+
if (!acc[block.category]) {
|
|
81
|
+
acc[block.category] = [];
|
|
82
|
+
}
|
|
83
|
+
acc[block.category]?.push(block);
|
|
84
|
+
return acc;
|
|
85
|
+
},
|
|
86
|
+
{}
|
|
87
|
+
);
|
|
88
|
+
}, [blocks]);
|
|
89
|
+
const blockCategories = React.useMemo(() => {
|
|
90
|
+
return Object.keys(groupedBlocks);
|
|
91
|
+
}, [groupedBlocks]);
|
|
92
|
+
const hasBlocks = blocks && Object.keys(blocks).length > 0;
|
|
53
93
|
const addComponentLayer = layerStore.useLayerStore((state) => state.addComponentLayer);
|
|
94
|
+
const addLayerDirect = layerStore.useLayerStore((state) => state.addLayerDirect);
|
|
54
95
|
const handleSelect = React__default.useCallback(
|
|
55
96
|
(currentValue) => {
|
|
56
97
|
if (onChange) {
|
|
@@ -75,6 +116,15 @@ function AddComponentsPopover({
|
|
|
75
116
|
onChange
|
|
76
117
|
]
|
|
77
118
|
);
|
|
119
|
+
const handleBlockSelect = React__default.useCallback(
|
|
120
|
+
(block) => {
|
|
121
|
+
const clonedTemplate = cloneLayerWithNewIds(block.template);
|
|
122
|
+
addLayerDirect(clonedTemplate, parentLayerId, addPosition);
|
|
123
|
+
setOpen(false);
|
|
124
|
+
onOpenChange?.(false);
|
|
125
|
+
},
|
|
126
|
+
[addLayerDirect, parentLayerId, addPosition, onOpenChange]
|
|
127
|
+
);
|
|
78
128
|
const handleOpenChange = React.useCallback(
|
|
79
129
|
(open2) => {
|
|
80
130
|
setOpen(open2);
|
|
@@ -82,63 +132,180 @@ function AddComponentsPopover({
|
|
|
82
132
|
},
|
|
83
133
|
[onOpenChange]
|
|
84
134
|
);
|
|
135
|
+
const handleDragStart = React.useCallback(() => {
|
|
136
|
+
setOpen(false);
|
|
137
|
+
onOpenChange?.(false);
|
|
138
|
+
}, [onOpenChange]);
|
|
85
139
|
const defaultTab = categories[0] || "";
|
|
140
|
+
const defaultBlockCategory = blockCategories[0] || "";
|
|
86
141
|
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: utils.cn("relative flex justify-center", className), children: /* @__PURE__ */ jsxRuntime.jsxs(popover.Popover, { open, onOpenChange: handleOpenChange, children: [
|
|
87
142
|
/* @__PURE__ */ jsxRuntime.jsx(popover.PopoverTrigger, { asChild: true, children }),
|
|
88
|
-
/* @__PURE__ */ jsxRuntime.
|
|
89
|
-
|
|
90
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
143
|
+
/* @__PURE__ */ jsxRuntime.jsxs(popover.PopoverContent, { className: "w-[320px] p-0", align: "start", children: [
|
|
144
|
+
hasBlocks && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex border-b", children: [
|
|
145
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
146
|
+
"button",
|
|
147
|
+
{
|
|
148
|
+
className: utils.cn(
|
|
149
|
+
"flex-1 px-4 py-2 text-sm font-medium transition-colors",
|
|
150
|
+
activeView === "components" ? "bg-background border-b-2 border-primary" : "bg-muted/50 text-muted-foreground hover:text-foreground"
|
|
151
|
+
),
|
|
152
|
+
onClick: () => setActiveView("components"),
|
|
153
|
+
children: "Components"
|
|
154
|
+
}
|
|
155
|
+
),
|
|
156
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
157
|
+
"button",
|
|
96
158
|
{
|
|
97
|
-
|
|
98
|
-
|
|
159
|
+
className: utils.cn(
|
|
160
|
+
"flex-1 px-4 py-2 text-sm font-medium transition-colors",
|
|
161
|
+
activeView === "blocks" ? "bg-background border-b-2 border-primary" : "bg-muted/50 text-muted-foreground hover:text-foreground"
|
|
162
|
+
),
|
|
163
|
+
onClick: () => setActiveView("blocks"),
|
|
164
|
+
children: "Blocks"
|
|
99
165
|
}
|
|
100
|
-
)
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
166
|
+
)
|
|
167
|
+
] }),
|
|
168
|
+
activeView === "components" && categories.length > 0 ? /* @__PURE__ */ jsxRuntime.jsxs(tabs.Tabs, { defaultValue: defaultTab, className: "w-full", children: [
|
|
169
|
+
/* @__PURE__ */ jsxRuntime.jsx(tabs.TabsList, { className: utils.cn(categories.length > 1 ? "h-14 w-full rounded-none border-b flex flex-row overflow-x-scroll justify-start" : "hidden"), children: categories.map((category) => /* @__PURE__ */ jsxRuntime.jsxs(tabs.TabsTrigger, { value: category, className: "flex flex-col justify-start items-start overflow-hidden px-2 py-1 min-w-24 min-h-11 shrink-0", children: [
|
|
170
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-sm", children: formatCategoryName(category) }),
|
|
171
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-full min-h-[12px] text-[8px] leading-[9px] text-left text-muted-foreground text-wrap", children: category })
|
|
172
|
+
] }, category)) }),
|
|
173
|
+
categories.map((category) => /* @__PURE__ */ jsxRuntime.jsx(tabs.TabsContent, { value: category, className: "m-0", children: /* @__PURE__ */ jsxRuntime.jsxs(command.Command, { className: "border-0", children: [
|
|
174
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center px-3 w-full [&>div:first-child]:w-full", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
175
|
+
command.CommandInput,
|
|
105
176
|
{
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
177
|
+
placeholder: "Find components",
|
|
178
|
+
className: "border-0 focus:ring-0 w-full"
|
|
179
|
+
}
|
|
180
|
+
) }),
|
|
181
|
+
/* @__PURE__ */ jsxRuntime.jsxs(command.CommandList, { className: "max-h-[250px]", children: [
|
|
182
|
+
/* @__PURE__ */ jsxRuntime.jsx(command.CommandEmpty, { children: "No components found" }),
|
|
183
|
+
/* @__PURE__ */ jsxRuntime.jsx(command.CommandGroup, { children: groupedOptions[category]?.map((component) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
184
|
+
GroupedComponentItem,
|
|
185
|
+
{
|
|
186
|
+
component,
|
|
187
|
+
onClick: handleSelect,
|
|
188
|
+
onDragStart: handleDragStart,
|
|
189
|
+
enableDrag: enableDragToCanvas
|
|
190
|
+
},
|
|
191
|
+
component.value
|
|
192
|
+
)) })
|
|
193
|
+
] })
|
|
194
|
+
] }) }, category))
|
|
195
|
+
] }) : activeView === "components" ? /* @__PURE__ */ jsxRuntime.jsxs(command.Command, { children: [
|
|
196
|
+
/* @__PURE__ */ jsxRuntime.jsx(command.CommandInput, { placeholder: "Add component" }),
|
|
197
|
+
/* @__PURE__ */ jsxRuntime.jsx(command.CommandList, { children: /* @__PURE__ */ jsxRuntime.jsx(command.CommandEmpty, { children: "No components found" }) })
|
|
198
|
+
] }) : null,
|
|
199
|
+
activeView === "blocks" && hasBlocks && /* @__PURE__ */ jsxRuntime.jsxs(tabs.Tabs, { defaultValue: defaultBlockCategory, className: "w-full", children: [
|
|
200
|
+
/* @__PURE__ */ jsxRuntime.jsx(tabs.TabsList, { className: "h-14 w-full rounded-none border-b flex flex-row overflow-x-scroll justify-start", children: blockCategories.map((category) => /* @__PURE__ */ jsxRuntime.jsxs(tabs.TabsTrigger, { value: category, className: "flex flex-col justify-start items-start overflow-hidden px-2 py-1 min-w-24 min-h-11 shrink-0", children: [
|
|
201
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-sm", children: formatCategoryName(category) }),
|
|
202
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "w-full min-h-[12px] text-[8px] leading-[9px] text-left text-muted-foreground", children: [
|
|
203
|
+
groupedBlocks[category]?.length,
|
|
204
|
+
" blocks"
|
|
205
|
+
] })
|
|
206
|
+
] }, category)) }),
|
|
207
|
+
blockCategories.map((category) => /* @__PURE__ */ jsxRuntime.jsx(tabs.TabsContent, { value: category, className: "m-0", children: /* @__PURE__ */ jsxRuntime.jsxs(command.Command, { className: "border-0", children: [
|
|
208
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center px-3 w-full [&>div:first-child]:w-full", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
209
|
+
command.CommandInput,
|
|
210
|
+
{
|
|
211
|
+
placeholder: "Find blocks",
|
|
212
|
+
className: "border-0 focus:ring-0 w-full"
|
|
213
|
+
}
|
|
214
|
+
) }),
|
|
215
|
+
/* @__PURE__ */ jsxRuntime.jsxs(command.CommandList, { className: "max-h-[250px]", children: [
|
|
216
|
+
/* @__PURE__ */ jsxRuntime.jsx(command.CommandEmpty, { children: "No blocks found" }),
|
|
217
|
+
/* @__PURE__ */ jsxRuntime.jsx(command.CommandGroup, { children: groupedBlocks[category]?.map((block) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
218
|
+
BlockItem,
|
|
219
|
+
{
|
|
220
|
+
block,
|
|
221
|
+
onClick: handleBlockSelect
|
|
222
|
+
},
|
|
223
|
+
block.name
|
|
224
|
+
)) })
|
|
225
|
+
] })
|
|
226
|
+
] }) }, category))
|
|
227
|
+
] })
|
|
228
|
+
] })
|
|
117
229
|
] }) });
|
|
118
230
|
}
|
|
231
|
+
function cloneLayerWithNewIds(layer) {
|
|
232
|
+
const newId = generateUniqueId();
|
|
233
|
+
let newChildren;
|
|
234
|
+
if (Array.isArray(layer.children)) {
|
|
235
|
+
newChildren = layer.children.map((child) => cloneLayerWithNewIds(child));
|
|
236
|
+
} else if (typeof layer.children === "string") {
|
|
237
|
+
newChildren = layer.children;
|
|
238
|
+
} else if (layer.children && typeof layer.children === "object" && "__variableRef" in layer.children) {
|
|
239
|
+
newChildren = layer.children;
|
|
240
|
+
} else {
|
|
241
|
+
newChildren = layer.children;
|
|
242
|
+
}
|
|
243
|
+
return {
|
|
244
|
+
...layer,
|
|
245
|
+
id: newId,
|
|
246
|
+
children: newChildren
|
|
247
|
+
};
|
|
248
|
+
}
|
|
249
|
+
function generateUniqueId() {
|
|
250
|
+
return Math.random().toString(36).substring(2, 9);
|
|
251
|
+
}
|
|
252
|
+
const BlockItem = React.memo(({
|
|
253
|
+
block,
|
|
254
|
+
onClick
|
|
255
|
+
}) => {
|
|
256
|
+
const handleSelect = React.useCallback(() => {
|
|
257
|
+
onClick(block);
|
|
258
|
+
}, [onClick, block]);
|
|
259
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
260
|
+
command.CommandItem,
|
|
261
|
+
{
|
|
262
|
+
onSelect: handleSelect,
|
|
263
|
+
className: "cursor-pointer flex flex-col items-start gap-1 py-3",
|
|
264
|
+
children: [
|
|
265
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "font-medium", children: formatBlockName(block.name) }),
|
|
266
|
+
block.description && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-xs text-muted-foreground line-clamp-2", children: block.description })
|
|
267
|
+
]
|
|
268
|
+
}
|
|
269
|
+
);
|
|
270
|
+
});
|
|
271
|
+
BlockItem.displayName = "BlockItem";
|
|
272
|
+
function formatBlockName(name) {
|
|
273
|
+
return name.split("-").map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join(" ");
|
|
274
|
+
}
|
|
119
275
|
const GroupedComponentItem = React.memo(({
|
|
120
276
|
component,
|
|
121
|
-
onClick
|
|
277
|
+
onClick,
|
|
278
|
+
onDragStart,
|
|
279
|
+
enableDrag = false
|
|
122
280
|
}) => {
|
|
123
281
|
const handleSelect = React.useCallback(() => {
|
|
124
282
|
onClick(component.value);
|
|
125
283
|
}, [onClick, component.value]);
|
|
126
284
|
const componentRegistry = editorStore.useEditorStore((state) => state.registry);
|
|
127
|
-
|
|
285
|
+
const content = /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3", children: [
|
|
286
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "shrink-0 w-10 h-6 overflow-hidden", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
287
|
+
LazyComponentPreview,
|
|
288
|
+
{
|
|
289
|
+
componentType: component.value,
|
|
290
|
+
componentRegistry
|
|
291
|
+
}
|
|
292
|
+
) }),
|
|
293
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center flex-1 min-w-0", children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "truncate", children: component.label }) })
|
|
294
|
+
] });
|
|
295
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
128
296
|
command.CommandItem,
|
|
129
297
|
{
|
|
130
298
|
onSelect: handleSelect,
|
|
131
|
-
className: "cursor-pointer flex items-center gap-
|
|
132
|
-
children:
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
]
|
|
299
|
+
className: "cursor-pointer flex items-center gap-2 py-2",
|
|
300
|
+
children: enableDrag ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
301
|
+
draggableNewComponent.DraggableNewComponent,
|
|
302
|
+
{
|
|
303
|
+
componentType: component.value,
|
|
304
|
+
onDragStart,
|
|
305
|
+
className: "flex-1",
|
|
306
|
+
children: content
|
|
307
|
+
}
|
|
308
|
+
) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1", children: content })
|
|
142
309
|
},
|
|
143
310
|
component.value
|
|
144
311
|
);
|
|
@@ -156,7 +323,7 @@ const LazyComponentPreview = React.memo(({
|
|
|
156
323
|
let timeoutId = null;
|
|
157
324
|
const observer = new IntersectionObserver(
|
|
158
325
|
([entry]) => {
|
|
159
|
-
if (entry
|
|
326
|
+
if (entry?.isIntersecting) {
|
|
160
327
|
timeoutId = setTimeout(() => setShouldLoad(true), 50);
|
|
161
328
|
}
|
|
162
329
|
},
|
|
@@ -179,12 +346,20 @@ const LazyComponentPreview = React.memo(({
|
|
|
179
346
|
) }) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-full h-full bg-muted rounded border" }) });
|
|
180
347
|
});
|
|
181
348
|
LazyComponentPreview.displayName = "LazyComponentPreview";
|
|
349
|
+
const ChildOnlyPlaceholder = React.memo(({ componentType }) => {
|
|
350
|
+
const initials = componentType.replace(/([A-Z])/g, " $1").trim().split(" ").map((word) => word[0]).slice(0, 2).join("");
|
|
351
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-full h-full bg-muted/50 rounded border border-dashed flex items-center justify-center", children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[8px] font-medium text-muted-foreground", children: initials }) });
|
|
352
|
+
});
|
|
353
|
+
ChildOnlyPlaceholder.displayName = "ChildOnlyPlaceholder";
|
|
182
354
|
const ComponentPreview = React.memo(({
|
|
183
355
|
componentType,
|
|
184
356
|
componentRegistry
|
|
185
357
|
}) => {
|
|
186
|
-
const
|
|
358
|
+
const isChildOnly = isChildOnlyComponent(componentRegistry, componentType);
|
|
187
359
|
const previewLayer = React.useMemo(() => {
|
|
360
|
+
if (isChildOnly) {
|
|
361
|
+
return null;
|
|
362
|
+
}
|
|
188
363
|
const cacheKey = `${componentType}-${JSON.stringify(componentRegistry[componentType]?.schema)}`;
|
|
189
364
|
if (previewLayerCache.has(cacheKey)) {
|
|
190
365
|
return previewLayerCache.get(cacheKey);
|
|
@@ -200,15 +375,20 @@ const ComponentPreview = React.memo(({
|
|
|
200
375
|
console.warn(`Failed to create preview for component ${componentType}:`, error);
|
|
201
376
|
return null;
|
|
202
377
|
}
|
|
203
|
-
}, [componentType, componentRegistry]);
|
|
378
|
+
}, [componentType, componentRegistry, isChildOnly]);
|
|
379
|
+
if (isChildOnly) {
|
|
380
|
+
return /* @__PURE__ */ jsxRuntime.jsx(ChildOnlyPlaceholder, { componentType });
|
|
381
|
+
}
|
|
204
382
|
if (!previewLayer) {
|
|
205
383
|
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-full h-full bg-muted rounded border" });
|
|
206
384
|
}
|
|
385
|
+
const style = { width: "200%", height: "200%" };
|
|
207
386
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
208
387
|
"div",
|
|
209
388
|
{
|
|
210
|
-
className: "w-full h-full bg-background rounded border overflow-hidden transform scale-50 origin-top-left",
|
|
389
|
+
className: "w-full h-full bg-background rounded border overflow-hidden transform scale-50 origin-top-left pointer-events-none",
|
|
211
390
|
style,
|
|
391
|
+
inert: true,
|
|
212
392
|
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
213
393
|
layerRenderer,
|
|
214
394
|
{
|
|
@@ -224,7 +404,7 @@ ComponentPreview.displayName = "ComponentPreview";
|
|
|
224
404
|
function formatCategoryName(name) {
|
|
225
405
|
const words = name.split("/");
|
|
226
406
|
const lastWord = words[words.length - 1];
|
|
227
|
-
return lastWord
|
|
407
|
+
return lastWord?.replace(/-/g, " ").replace(/\b\w/g, (char) => char.toUpperCase()) ?? "";
|
|
228
408
|
}
|
|
229
409
|
|
|
230
410
|
exports.AddComponentsPopover = AddComponentsPopover;
|