neogestify-ui-components 2.0.1 → 2.2.1

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.
Files changed (31) hide show
  1. package/README.md +153 -19
  2. package/dist/components/ElementLibraryBuilder/index.d.mts +5 -0
  3. package/dist/components/ElementLibraryBuilder/index.d.ts +5 -0
  4. package/dist/components/ElementLibraryBuilder/index.js +689 -0
  5. package/dist/components/ElementLibraryBuilder/index.js.map +1 -0
  6. package/dist/components/ElementLibraryBuilder/index.mjs +687 -0
  7. package/dist/components/ElementLibraryBuilder/index.mjs.map +1 -0
  8. package/dist/components/VenueMapEditor/index.d.mts +66 -5
  9. package/dist/components/VenueMapEditor/index.d.ts +66 -5
  10. package/dist/components/VenueMapEditor/index.js +199 -34
  11. package/dist/components/VenueMapEditor/index.js.map +1 -1
  12. package/dist/components/VenueMapEditor/index.mjs +199 -36
  13. package/dist/components/VenueMapEditor/index.mjs.map +1 -1
  14. package/dist/index.d.mts +2 -1
  15. package/dist/index.d.ts +2 -1
  16. package/dist/index.js +592 -34
  17. package/dist/index.js.map +1 -1
  18. package/dist/index.mjs +591 -36
  19. package/dist/index.mjs.map +1 -1
  20. package/package.json +1 -1
  21. package/src/components/ElementLibraryBuilder/builder.tsx +400 -0
  22. package/src/components/ElementLibraryBuilder/index.ts +1 -0
  23. package/src/components/VenueMapEditor/VenueMapEditor.tsx +79 -20
  24. package/src/components/VenueMapEditor/components/ElementNode.tsx +23 -0
  25. package/src/components/VenueMapEditor/components/PropertiesPanel.tsx +17 -4
  26. package/src/components/VenueMapEditor/components/Toolbar.tsx +73 -39
  27. package/src/components/VenueMapEditor/hooks/useLibraryStorage.ts +46 -0
  28. package/src/components/VenueMapEditor/index.ts +3 -0
  29. package/src/components/VenueMapEditor/types.ts +45 -3
  30. package/src/components/VenueMapEditor/utils/svgParser.ts +33 -0
  31. package/src/index.ts +1 -0
@@ -0,0 +1,689 @@
1
+ 'use strict';
2
+
3
+ var react = require('react');
4
+ var jsxRuntime = require('react/jsx-runtime');
5
+
6
+ // src/components/ElementLibraryBuilder/builder.tsx
7
+ function AnimateSpin({ className }) {
8
+ return /* @__PURE__ */ jsxRuntime.jsxs(
9
+ "svg",
10
+ {
11
+ className: `animate-spin ${className}`,
12
+ xmlns: "http://www.w3.org/2000/svg",
13
+ fill: "none",
14
+ viewBox: "0 0 24 24",
15
+ "aria-hidden": "true",
16
+ children: [
17
+ /* @__PURE__ */ jsxRuntime.jsx("circle", { className: "opacity-25", cx: "12", cy: "12", r: "10", stroke: "currentColor", strokeWidth: "4" }),
18
+ /* @__PURE__ */ jsxRuntime.jsx(
19
+ "path",
20
+ {
21
+ className: "opacity-75",
22
+ fill: "currentColor",
23
+ d: "M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
24
+ }
25
+ )
26
+ ]
27
+ }
28
+ );
29
+ }
30
+ function CloseIcon({ className }) {
31
+ return /* @__PURE__ */ jsxRuntime.jsx("svg", { className, fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", xmlns: "http://www.w3.org/2000/svg", "aria-hidden": "true", children: /* @__PURE__ */ jsxRuntime.jsx(
32
+ "path",
33
+ {
34
+ strokeLinecap: "round",
35
+ strokeLinejoin: "round",
36
+ strokeWidth: 2,
37
+ d: "M6 18L18 6M6 6l12 12"
38
+ }
39
+ ) });
40
+ }
41
+ var Button = ({
42
+ variant = "primary",
43
+ children,
44
+ isLoading = false,
45
+ loadingText,
46
+ isActive = false,
47
+ className = "",
48
+ disabled,
49
+ ...props
50
+ }) => {
51
+ const baseClasses = "transition-all duration-200 disabled:opacity-50 disabled:cursor-not-allowed cursor-pointer";
52
+ const variantClasses = {
53
+ primary: "py-2 px-2 border border-indigo-600/20 dark:border-indigo-500/20 text-sm font-medium rounded-md text-white bg-indigo-600 hover:bg-indigo-700 dark:bg-indigo-500 dark:hover:bg-indigo-600 focus:outline-none focus:ring-2 focus:ring-indigo-500 dark:focus:ring-indigo-400 focus:ring-offset-2 focus:ring-offset-gray-50 dark:focus:ring-offset-gray-900",
54
+ secondary: "p-2 text-gray-600 dark:text-gray-400 hover:text-gray-900 dark:hover:text-white hover:bg-gray-200 dark:hover:bg-gray-800 rounded-md border border-gray-300 dark:border-gray-600 shadow-sm hover:shadow-md focus:outline-none focus:ring-2 focus:ring-indigo-500 dark:focus:ring-indigo-400 focus:ring-offset-2 focus:ring-offset-gray-50 dark:focus:ring-offset-gray-900",
55
+ icon: "p-2 text-gray-600 dark:text-gray-400 hover:text-gray-900 dark:hover:text-white hover:bg-gray-200 dark:hover:bg-gray-800 rounded-full focus:outline-none focus:ring-2 focus:ring-indigo-500 dark:focus:ring-indigo-400 focus:ring-offset-2 focus:ring-offset-gray-50 dark:focus:ring-offset-gray-900",
56
+ danger: "py-2 px-2 border border-red-600/20 dark:border-red-500/20 text-sm font-medium rounded-md text-white bg-red-600 hover:bg-red-700 dark:bg-red-500 dark:hover:bg-red-600 focus:outline-none focus:ring-2 focus:ring-red-500 dark:focus:ring-red-400 focus:ring-offset-2 focus:ring-offset-gray-50 dark:focus:ring-offset-gray-900",
57
+ success: "py-2 px-2 border border-green-600/20 dark:border-green-500/20 text-sm font-medium rounded-md text-white bg-green-600 hover:bg-green-700 dark:bg-green-500 dark:hover:bg-green-600 focus:outline-none focus:ring-2 focus:ring-green-500 dark:focus:ring-green-400 focus:ring-offset-2 focus:ring-offset-gray-50 dark:focus:ring-offset-gray-900",
58
+ outline: "py-2 px-2 border border-gray-300 dark:border-gray-600 text-sm font-medium rounded-md text-gray-700 dark:text-gray-300 bg-transparent hover:bg-gray-100 dark:hover:bg-gray-700 focus:outline-none focus:ring-2 focus:ring-indigo-500 dark:focus:ring-indigo-400 focus:ring-offset-2 focus:ring-offset-gray-50 dark:focus:ring-offset-gray-900",
59
+ nav: "w-full flex items-center px-4 py-2 text-sm font-medium rounded-md transition-all duration-200 hover:scale-105 text-gray-700 dark:text-gray-300 dark:hover:text-white hover:shadow-lg focus:outline-none focus:ring-2 focus:ring-indigo-500 dark:focus:ring-indigo-400 focus:ring-offset-2 focus:ring-offset-gray-50 dark:focus:ring-offset-gray-900",
60
+ custom: "",
61
+ link: "text-sm text-indigo-600 dark:text-indigo-400 hover:text-indigo-700 dark:hover:text-indigo-300 transition-colors duration-200 focus:outline-none focus:ring-2 focus:ring-indigo-500 dark:focus:ring-indigo-400 focus:ring-offset-2 focus:ring-offset-gray-50 dark:focus:ring-offset-gray-900 rounded-lg px-2",
62
+ warning: "py-2 px-2 border border-yellow-600/20 dark:border-yellow-500/20 text-sm font-medium rounded-md text-white bg-yellow-600 hover:bg-yellow-700 dark:bg-yellow-500 dark:hover:bg-yellow-600 focus:outline-none focus:ring-2 focus:ring-yellow-500 dark:focus:ring-yellow-400 focus:ring-offset-2 focus:ring-offset-gray-50 dark:focus:ring-offset-gray-900",
63
+ toggle: "px-2 py-2 rounded-lg font-medium transition-all duration-200 disabled:cursor-not-allowed border-2 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2 focus:ring-offset-gray-50 dark:focus:ring-offset-gray-900"
64
+ };
65
+ let classes = `${baseClasses} ${variantClasses[variant]} ${className}`;
66
+ if (variant === "nav" && isActive) {
67
+ classes += " bg-indigo-600 dark:bg-indigo-500 hover:bg-indigo-700 dark:hover:bg-indigo-600 text-white shadow-lg scale-105";
68
+ }
69
+ if (variant === "nav" && !isActive) {
70
+ classes += " hover:bg-white dark:hover:bg-gray-700 hover:shadow-lg";
71
+ }
72
+ if (variant === "toggle") {
73
+ if (isActive) {
74
+ classes += " bg-indigo-600 text-white border-indigo-600/30 dark:border-indigo-500/30 hover:bg-indigo-700";
75
+ } else {
76
+ classes += " bg-gray-200 dark:bg-gray-700 text-gray-700 dark:text-gray-300 border-gray-300 dark:border-gray-600 hover:bg-gray-300 dark:hover:bg-gray-600 hover:border-gray-400 dark:hover:border-gray-500";
77
+ }
78
+ }
79
+ return /* @__PURE__ */ jsxRuntime.jsx(
80
+ "button",
81
+ {
82
+ className: classes,
83
+ disabled: disabled || isLoading,
84
+ ...props,
85
+ children: isLoading ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
86
+ /* @__PURE__ */ jsxRuntime.jsx(AnimateSpin, { className: "h-5 w-5 mr-2 inline-block text-current" }),
87
+ loadingText || "Cargando..."
88
+ ] }) : children
89
+ }
90
+ );
91
+ };
92
+ var Input = ({
93
+ label,
94
+ error,
95
+ helperText,
96
+ icon,
97
+ iconSide = "left",
98
+ className = "",
99
+ id,
100
+ type,
101
+ ...props
102
+ }) => {
103
+ const inputId = id || `input-${Math.random().toString(36).substring(2, 9)}`;
104
+ const baseClasses = "appearance-none relative block w-full px-3 py-2 border placeholder-gray-500 dark:placeholder-gray-400 text-gray-900 dark:text-white bg-white dark:bg-gray-800 rounded-md focus:outline-none focus:ring-2 focus:ring-indigo-500 dark:focus:ring-indigo-400 focus:border-indigo-500 focus:z-10 sm:text-sm disabled:opacity-50 disabled:cursor-not-allowed transition-colors duration-200";
105
+ const errorClasses = error ? "border-red-300 dark:border-red-600 focus:ring-red-500 dark:focus:ring-red-400 focus:border-red-500" : "border-gray-300 dark:border-gray-600";
106
+ const iconPaddingLeft = icon && iconSide === "left" ? "pl-9" : "";
107
+ const iconPaddingRight = icon && iconSide === "right" ? "pr-9" : "";
108
+ const classes = `${baseClasses} ${errorClasses} ${iconPaddingLeft} ${iconPaddingRight} ${className}`.trim();
109
+ const toggleShape = type === "radio" ? "rounded-full" : "rounded";
110
+ const toggleBaseClasses = `h-4 w-4 ${toggleShape} border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-800 text-indigo-600 dark:text-indigo-400 focus:outline-none focus:ring-2 focus:ring-indigo-500 dark:focus:ring-indigo-400 focus:ring-offset-2 focus:ring-offset-white dark:focus:ring-offset-gray-900 disabled:opacity-50 disabled:cursor-not-allowed transition-colors duration-200 cursor-pointer`;
111
+ const toggleErrorClasses = error ? "border-red-300 dark:border-red-600 focus:ring-red-500 dark:focus:ring-red-400" : "";
112
+ const toggleClasses = `${toggleBaseClasses} ${toggleErrorClasses}`.trim();
113
+ const fileBaseClasses = "block w-full sm:text-sm text-gray-500 dark:text-gray-400 bg-white dark:bg-gray-800 border rounded-md focus:outline-none focus:ring-2 focus:ring-indigo-500 dark:focus:ring-indigo-400 focus:border-indigo-500 disabled:opacity-50 disabled:cursor-not-allowed transition-colors duration-200 file:mr-4 file:py-2 file:px-4 file:rounded-l-md file:border-0 file:text-sm file:font-medium file:bg-indigo-50 file:text-indigo-700 dark:file:bg-indigo-900/50 dark:file:text-indigo-300 hover:file:bg-indigo-100 dark:hover:file:bg-indigo-800/50 file:transition-colors file:duration-200 file:cursor-pointer";
114
+ const fileErrorClasses = error ? "border-red-300 dark:border-red-600 focus:ring-red-500 dark:focus:ring-red-400" : "border-gray-300 dark:border-gray-600";
115
+ const fileClasses = `${fileBaseClasses} ${fileErrorClasses} ${className}`.trim();
116
+ const hasHidden = Boolean(className && /\bhidden\b/.test(className));
117
+ const wrapperBase = "space-y-1 w-full";
118
+ const wrapperClasses = hasHidden ? `${wrapperBase} hidden` : wrapperBase;
119
+ const labelNode = label && (typeof label === "string" ? /* @__PURE__ */ jsxRuntime.jsx("label", { htmlFor: inputId, className: "block text-sm font-medium text-gray-700 dark:text-gray-300", children: label }) : label);
120
+ const errorNode = error && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-red-600 dark:text-red-400", role: "alert", children: error });
121
+ const helperNode = helperText && !error && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-gray-500 dark:text-gray-400", children: helperText });
122
+ if (type === "checkbox" || type === "radio") {
123
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: wrapperClasses, children: [
124
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center space-x-2", children: [
125
+ /* @__PURE__ */ jsxRuntime.jsx("input", { id: inputId, type, className: toggleClasses, ...props }),
126
+ labelNode
127
+ ] }),
128
+ errorNode,
129
+ helperNode
130
+ ] });
131
+ }
132
+ if (type === "file") {
133
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: wrapperClasses, children: [
134
+ labelNode,
135
+ /* @__PURE__ */ jsxRuntime.jsx("input", { id: inputId, type: "file", className: fileClasses, ...props }),
136
+ errorNode,
137
+ helperNode
138
+ ] });
139
+ }
140
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: wrapperClasses, children: [
141
+ labelNode,
142
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative", children: [
143
+ icon && iconSide === "left" && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "pointer-events-none absolute inset-y-0 left-0 z-10 flex items-center pl-3 text-gray-400 dark:text-gray-500", children: icon }),
144
+ /* @__PURE__ */ jsxRuntime.jsx("input", { id: inputId, className: classes, type, ...props }),
145
+ icon && iconSide === "right" && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "pointer-events-none absolute inset-y-0 right-0 z-10 flex items-center pr-3 text-gray-400 dark:text-gray-500", children: icon })
146
+ ] }),
147
+ errorNode,
148
+ helperNode
149
+ ] });
150
+ };
151
+ var TextArea = ({
152
+ label,
153
+ error,
154
+ helperText,
155
+ variant = "default",
156
+ size = "medium",
157
+ className = "",
158
+ id,
159
+ ...props
160
+ }) => {
161
+ const textAreaId = id || `textarea-${Math.random().toString(36).substring(2, 9)}`;
162
+ const sizeClasses = {
163
+ small: "px-2 py-1 text-xs",
164
+ medium: "px-3 py-2 text-sm",
165
+ large: "px-4 py-3 text-base"
166
+ };
167
+ const variantClasses = {
168
+ default: "border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-800",
169
+ outline: "border-2 border-indigo-300 dark:border-indigo-600 bg-transparent",
170
+ filled: "border-gray-300 dark:border-gray-600 bg-gray-100 dark:bg-gray-700",
171
+ minimal: "border-0 bg-transparent focus:ring-0 focus:border-0"
172
+ };
173
+ const baseClasses = "appearance-none relative block w-full placeholder-gray-500 dark:placeholder-gray-400 text-gray-900 dark:text-white rounded-md border focus:outline-none focus:ring-2 focus:ring-indigo-500 dark:focus:ring-indigo-400 focus:border-indigo-500 focus:z-10 disabled:opacity-50 disabled:cursor-not-allowed transition-colors duration-200 resize-y";
174
+ const errorClasses = error ? "border-red-300 dark:border-red-600 focus:ring-red-500 dark:focus:ring-red-400 focus:border-red-500" : "";
175
+ const classes = `${baseClasses} ${sizeClasses[size]} ${variantClasses[variant]} ${errorClasses} ${className}`;
176
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-1 w-full", children: [
177
+ label && typeof label === "string" ? /* @__PURE__ */ jsxRuntime.jsx(
178
+ "label",
179
+ {
180
+ htmlFor: textAreaId,
181
+ className: "block text-sm font-medium text-gray-700 dark:text-gray-300",
182
+ children: label
183
+ }
184
+ ) : label,
185
+ /* @__PURE__ */ jsxRuntime.jsx(
186
+ "textarea",
187
+ {
188
+ id: textAreaId,
189
+ className: classes,
190
+ ...props
191
+ }
192
+ ),
193
+ error && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-red-600 dark:text-red-400", role: "alert", children: error }),
194
+ helperText && !error && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-gray-500 dark:text-gray-400", children: helperText })
195
+ ] });
196
+ };
197
+ var Select = ({
198
+ options,
199
+ placeholder,
200
+ variant = "default",
201
+ error = false,
202
+ helperText,
203
+ label,
204
+ className = "",
205
+ id,
206
+ ...props
207
+ }) => {
208
+ const selectId = id || `select-${Math.random().toString(36).substring(2, 9)}`;
209
+ const getVariantClasses = () => {
210
+ const baseClasses = "appearance-none relative block w-full px-3 py-2 border placeholder-gray-500 dark:placeholder-gray-400 text-gray-900 dark:text-white bg-white dark:bg-gray-800 rounded-md focus:outline-none focus:ring-2 focus:ring-indigo-500 dark:focus:ring-indigo-400 focus:border-indigo-500 focus:z-10 sm:text-sm disabled:opacity-50 disabled:cursor-not-allowed transition-colors duration-200";
211
+ if (variant === "small") {
212
+ return `${baseClasses.replace("px-3 py-2", "px-2.5 py-1.5 text-sm")} border-gray-300 dark:border-gray-600`;
213
+ }
214
+ return `${baseClasses} border-gray-300 dark:border-gray-600 ${error ? "border-red-300 dark:border-red-600 focus:ring-red-500 dark:focus:ring-red-400 focus:border-red-500" : ""}`;
215
+ };
216
+ const getOptionClasses = (option) => {
217
+ return `bg-white dark:bg-gray-800 text-gray-900 dark:text-white py-2 ${option.disabled ? "opacity-50 cursor-not-allowed" : ""}`;
218
+ };
219
+ const combinedClassName = `${getVariantClasses()} ${className}`.trim();
220
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-1 w-full", children: [
221
+ label && typeof label === "string" ? /* @__PURE__ */ jsxRuntime.jsx("label", { htmlFor: selectId, className: "block text-sm font-medium text-gray-700 dark:text-gray-300", children: label }) : label,
222
+ /* @__PURE__ */ jsxRuntime.jsxs("select", { id: selectId, className: combinedClassName, ...props, children: [
223
+ placeholder && placeholder.trim() && /* @__PURE__ */ jsxRuntime.jsx("option", { value: "", disabled: true, selected: true, className: "bg-white dark:bg-gray-800 text-gray-500 dark:text-gray-400 py-2", children: placeholder }),
224
+ options.map((option) => /* @__PURE__ */ jsxRuntime.jsx(
225
+ "option",
226
+ {
227
+ value: option.value,
228
+ disabled: option.disabled,
229
+ selected: option.selected,
230
+ className: getOptionClasses(option),
231
+ children: option.label
232
+ },
233
+ option.value
234
+ ))
235
+ ] }),
236
+ helperText && /* @__PURE__ */ jsxRuntime.jsx("p", { className: `text-sm ${error ? "text-red-600 dark:text-red-400" : "text-gray-500 dark:text-gray-400"}`, children: helperText })
237
+ ] });
238
+ };
239
+ var Modal = react.forwardRef(({
240
+ onClose,
241
+ title,
242
+ children,
243
+ footer,
244
+ maxWidth = "max-w-2xl",
245
+ showCloseButton = true,
246
+ zIndex = 50
247
+ }, ref) => {
248
+ const [show, setShow] = react.useState(false);
249
+ react.useEffect(() => {
250
+ setShow(true);
251
+ }, []);
252
+ const handleClose = () => {
253
+ setShow(false);
254
+ setTimeout(() => {
255
+ onClose();
256
+ }, 300);
257
+ };
258
+ react.useImperativeHandle(ref, () => ({
259
+ handleClose
260
+ }));
261
+ return /* @__PURE__ */ jsxRuntime.jsx(
262
+ "dialog",
263
+ {
264
+ open: show,
265
+ className: `fixed inset-0 w-full h-full flex items-center justify-center p-4 ${show && "opacity-100"} transition-opacity opacity-0 duration-300 bg-gray-900/60 backdrop-blur-sm`,
266
+ style: { zIndex: zIndex - 10 },
267
+ children: /* @__PURE__ */ jsxRuntime.jsxs(
268
+ "article",
269
+ {
270
+ className: `relative bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-lg shadow-2xl w-full ${maxWidth} max-h-[90vh] flex flex-col overflow-hidden`,
271
+ style: { zIndex },
272
+ children: [
273
+ /* @__PURE__ */ jsxRuntime.jsxs("header", { className: "shrink-0 bg-gray-50 dark:bg-gray-800 border-b border-gray-200 dark:border-gray-700 px-6 py-4 flex items-center justify-between", children: [
274
+ /* @__PURE__ */ jsxRuntime.jsx("h2", { className: "text-2xl font-bold text-gray-900 dark:text-white", children: title }),
275
+ showCloseButton && /* @__PURE__ */ jsxRuntime.jsx(
276
+ Button,
277
+ {
278
+ variant: "icon",
279
+ onClick: handleClose,
280
+ className: "text-gray-400 hover:text-gray-600 dark:hover:text-gray-300",
281
+ children: /* @__PURE__ */ jsxRuntime.jsx(CloseIcon, { className: "w-5 h-5" })
282
+ }
283
+ )
284
+ ] }),
285
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1 overflow-y-auto p-6", children }),
286
+ footer && /* @__PURE__ */ jsxRuntime.jsx("footer", { className: "shrink-0 bg-gray-50 dark:bg-gray-800 border-t border-gray-200 dark:border-gray-700 px-6 py-4 flex justify-end gap-3", children: footer })
287
+ ]
288
+ }
289
+ )
290
+ }
291
+ );
292
+ });
293
+ Modal.displayName = "Modal";
294
+ var DEFAULT_ELEMENT = {
295
+ id: "",
296
+ label: "",
297
+ shape: "rect",
298
+ defaultWidth: 100,
299
+ defaultHeight: 100,
300
+ color: "#cccccc",
301
+ strokeColor: "#000000"
302
+ };
303
+ var SHAPE_OPTIONS = [
304
+ { value: "rect", label: "Rectangle" },
305
+ { value: "circle", label: "Circle" },
306
+ { value: "arrow", label: "Arrow" },
307
+ { value: "path", label: "Path" },
308
+ { value: "svg", label: "SVG Markup" }
309
+ ];
310
+ var ElementLibraryBuilder = () => {
311
+ const [groups, setGroups] = react.useState([
312
+ { internalId: crypto.randomUUID(), name: "defaultGroup", objects: [] }
313
+ ]);
314
+ const [activeGroupId, setActiveGroupId] = react.useState(groups[0].internalId);
315
+ const [editingGroupId, setEditingGroupId] = react.useState(null);
316
+ const [activeElementIndex, setActiveElementIndex] = react.useState(null);
317
+ const [currentElement, setCurrentElement] = react.useState({ ...DEFAULT_ELEMENT, id: "rect_1", label: "New Rect" });
318
+ const [downloadFileName, setDownloadFileName] = react.useState("libraries");
319
+ const handleAddGroup = () => {
320
+ const newGroupId = crypto.randomUUID();
321
+ const newName = `group_${groups.length + 1}`;
322
+ setGroups([...groups, { internalId: newGroupId, name: newName, objects: [] }]);
323
+ setActiveGroupId(newGroupId);
324
+ setActiveElementIndex(null);
325
+ };
326
+ const handleRemoveGroup = (id) => {
327
+ const newGroups = groups.filter((g) => g.internalId !== id);
328
+ setGroups(newGroups);
329
+ if (activeGroupId === id) {
330
+ if (newGroups.length > 0) {
331
+ setActiveGroupId(newGroups[0].internalId);
332
+ } else {
333
+ setActiveGroupId("");
334
+ }
335
+ setActiveElementIndex(null);
336
+ }
337
+ };
338
+ const activeGroup = groups.find((g) => g.internalId === activeGroupId);
339
+ const handleSelectGroup = (gId) => {
340
+ setActiveGroupId(gId);
341
+ setActiveElementIndex(null);
342
+ };
343
+ const handleAddElement = () => {
344
+ if (!activeGroup) return;
345
+ const newEl = { ...DEFAULT_ELEMENT, id: `shape_${activeGroup.objects.length + 1}`, label: `Shape ${activeGroup.objects.length + 1}` };
346
+ const updatedGroups = groups.map((g) => {
347
+ if (g.internalId === activeGroupId) {
348
+ return { ...g, objects: [...g.objects, newEl] };
349
+ }
350
+ return g;
351
+ });
352
+ setGroups(updatedGroups);
353
+ setActiveElementIndex(activeGroup.objects.length);
354
+ setCurrentElement(newEl);
355
+ };
356
+ const handleSelectElement = (idx) => {
357
+ if (!activeGroup) return;
358
+ setActiveElementIndex(idx);
359
+ setCurrentElement(activeGroup.objects[idx]);
360
+ };
361
+ const handleRemoveElement = (idx) => {
362
+ if (!activeGroup) return;
363
+ const updatedGroups = groups.map((g) => {
364
+ if (g.internalId === activeGroupId) {
365
+ const newObjs = [...g.objects];
366
+ newObjs.splice(idx, 1);
367
+ return { ...g, objects: newObjs };
368
+ }
369
+ return g;
370
+ });
371
+ setGroups(updatedGroups);
372
+ if (activeElementIndex === idx) {
373
+ setActiveElementIndex(null);
374
+ } else if (activeElementIndex !== null && activeElementIndex > idx) {
375
+ setActiveElementIndex(activeElementIndex - 1);
376
+ }
377
+ };
378
+ const handleSaveElement = () => {
379
+ if (!activeGroup || activeElementIndex === null) return;
380
+ const updatedGroups = groups.map((g) => {
381
+ if (g.internalId === activeGroupId) {
382
+ const newObjs = [...g.objects];
383
+ newObjs[activeElementIndex] = { ...currentElement };
384
+ return { ...g, objects: newObjs };
385
+ }
386
+ return g;
387
+ });
388
+ setGroups(updatedGroups);
389
+ };
390
+ const handleFieldChange = (field, value) => {
391
+ setCurrentElement((prev) => ({ ...prev, [field]: value }));
392
+ };
393
+ const handleSvgMarkupChange = (value) => {
394
+ handleFieldChange("svgMarkup", value);
395
+ };
396
+ const generatedLib = react.useMemo(() => {
397
+ const lib = {};
398
+ groups.forEach((g) => {
399
+ lib[g.name] = {
400
+ name: g.name,
401
+ objects: g.objects
402
+ };
403
+ });
404
+ return JSON.stringify(lib, null, 2);
405
+ }, [groups]);
406
+ const handleDownload = () => {
407
+ const blob = new Blob([generatedLib], { type: "application/json" });
408
+ const url = URL.createObjectURL(blob);
409
+ const a = document.createElement("a");
410
+ a.href = url;
411
+ a.download = `${downloadFileName}.json`;
412
+ document.body.appendChild(a);
413
+ a.click();
414
+ document.body.removeChild(a);
415
+ URL.revokeObjectURL(url);
416
+ };
417
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex gap-4 p-4 h-full min-h-[600px] text-sm text-gray-900 dark:text-gray-100 bg-white dark:bg-gray-900", children: [
418
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "w-1/4 flex flex-col gap-4 border-r dark:border-gray-700 pr-4", children: [
419
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-2", children: [
420
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between", children: [
421
+ /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "font-bold", children: "Libraries (Groups)" }),
422
+ /* @__PURE__ */ jsxRuntime.jsx(Button, { variant: "primary", onClick: handleAddGroup, children: "+ Group" })
423
+ ] }),
424
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-col gap-1 max-h-48 overflow-y-auto pr-1", children: groups.map((group) => /* @__PURE__ */ jsxRuntime.jsxs(
425
+ "div",
426
+ {
427
+ className: `flex items-center justify-between p-2 rounded cursor-pointer ${activeGroupId === group.internalId ? "bg-indigo-100 text-indigo-900 dark:bg-indigo-900/50 dark:text-indigo-100 font-semibold" : "hover:bg-gray-100 dark:hover:bg-gray-800"}`,
428
+ onClick: () => handleSelectGroup(group.internalId),
429
+ children: [
430
+ editingGroupId === group.internalId ? /* @__PURE__ */ jsxRuntime.jsx(
431
+ Input,
432
+ {
433
+ autoFocus: true,
434
+ value: group.name,
435
+ onChange: (e) => {
436
+ setGroups(groups.map((g) => g.internalId === group.internalId ? { ...g, name: e.target.value } : g));
437
+ },
438
+ onBlur: () => setEditingGroupId(null),
439
+ onKeyDown: (e) => e.key === "Enter" && setEditingGroupId(null)
440
+ }
441
+ ) : /* @__PURE__ */ jsxRuntime.jsx("span", { onDoubleClick: () => setEditingGroupId(group.internalId), children: group.name }),
442
+ groups.length > 1 && /* @__PURE__ */ jsxRuntime.jsx("button", { onClick: (e) => {
443
+ e.stopPropagation();
444
+ handleRemoveGroup(group.internalId);
445
+ }, className: "text-red-500 text-xs", children: "x" })
446
+ ]
447
+ },
448
+ group.internalId
449
+ )) })
450
+ ] }),
451
+ /* @__PURE__ */ jsxRuntime.jsx("hr", { className: "dark:border-gray-700" }),
452
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-2 flex-grow overflow-hidden", children: [
453
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between", children: [
454
+ /* @__PURE__ */ jsxRuntime.jsxs("h3", { className: "font-bold", children: [
455
+ "Elements in ",
456
+ activeGroup?.name || "?"
457
+ ] }),
458
+ /* @__PURE__ */ jsxRuntime.jsx(Button, { variant: "secondary", onClick: handleAddElement, disabled: !activeGroup, children: "+ Element" })
459
+ ] }),
460
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-1 overflow-y-auto flex-grow pr-1", children: [
461
+ activeGroup?.objects.map((el, i) => /* @__PURE__ */ jsxRuntime.jsxs(
462
+ "div",
463
+ {
464
+ className: `flex items-center justify-between p-2 rounded cursor-pointer ${activeElementIndex === i ? "bg-indigo-100 text-indigo-900 dark:bg-indigo-900/50 dark:text-indigo-100 font-semibold" : "hover:bg-gray-100 dark:hover:bg-gray-800"}`,
465
+ onClick: () => handleSelectElement(i),
466
+ children: [
467
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
468
+ el.id,
469
+ " (",
470
+ el.shape,
471
+ ")"
472
+ ] }),
473
+ /* @__PURE__ */ jsxRuntime.jsx("button", { onClick: (e) => {
474
+ e.stopPropagation();
475
+ handleRemoveElement(i);
476
+ }, className: "text-red-500 text-xs", children: "x" })
477
+ ]
478
+ },
479
+ i
480
+ )),
481
+ (!activeGroup || activeGroup.objects.length === 0) && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-gray-400 dark:text-gray-500 italic text-xs", children: "No elements yet" })
482
+ ] })
483
+ ] })
484
+ ] }),
485
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-1 flex flex-col gap-4 px-2 overflow-y-auto", children: [
486
+ /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "font-bold text-lg", children: "Element Editor" }),
487
+ activeElementIndex !== null ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-4 w-full max-w-2xl", children: [
488
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid grid-cols-2 gap-4", children: [
489
+ /* @__PURE__ */ jsxRuntime.jsx(
490
+ Input,
491
+ {
492
+ label: "Element ID (unique)",
493
+ value: currentElement.id,
494
+ onChange: (e) => handleFieldChange("id", e.target.value)
495
+ }
496
+ ),
497
+ /* @__PURE__ */ jsxRuntime.jsx(
498
+ Input,
499
+ {
500
+ label: "Label (display name)",
501
+ value: currentElement.label,
502
+ onChange: (e) => handleFieldChange("label", e.target.value)
503
+ }
504
+ )
505
+ ] }),
506
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid grid-cols-2 gap-4", children: [
507
+ /* @__PURE__ */ jsxRuntime.jsx(
508
+ Select,
509
+ {
510
+ label: "Shape",
511
+ options: SHAPE_OPTIONS,
512
+ value: currentElement.shape,
513
+ onChange: (e) => handleFieldChange("shape", e.target.value)
514
+ }
515
+ ),
516
+ /* @__PURE__ */ jsxRuntime.jsx(
517
+ Input,
518
+ {
519
+ label: "Icon (emoji or class)",
520
+ value: currentElement.icon || "",
521
+ onChange: (e) => handleFieldChange("icon", e.target.value)
522
+ }
523
+ )
524
+ ] }),
525
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid grid-cols-2 gap-4", children: [
526
+ /* @__PURE__ */ jsxRuntime.jsx(
527
+ Input,
528
+ {
529
+ type: "number",
530
+ label: "Default Width",
531
+ value: currentElement.defaultWidth,
532
+ onChange: (e) => handleFieldChange("defaultWidth", parseFloat(e.target.value) || 0)
533
+ }
534
+ ),
535
+ /* @__PURE__ */ jsxRuntime.jsx(
536
+ Input,
537
+ {
538
+ type: "number",
539
+ label: "Default Height",
540
+ value: currentElement.defaultHeight,
541
+ onChange: (e) => handleFieldChange("defaultHeight", parseFloat(e.target.value) || 0)
542
+ }
543
+ )
544
+ ] }),
545
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid grid-cols-2 gap-4", children: [
546
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-1", children: [
547
+ /* @__PURE__ */ jsxRuntime.jsx("label", { className: "text-xs font-semibold text-gray-700", children: "Fill Color" }),
548
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex gap-2", children: [
549
+ /* @__PURE__ */ jsxRuntime.jsx(
550
+ "input",
551
+ {
552
+ type: "color",
553
+ className: "w-8 h-8 cursor-pointer rounded",
554
+ value: currentElement.color,
555
+ onChange: (e) => handleFieldChange("color", e.target.value)
556
+ }
557
+ ),
558
+ /* @__PURE__ */ jsxRuntime.jsx(
559
+ Input,
560
+ {
561
+ value: currentElement.color,
562
+ onChange: (e) => handleFieldChange("color", e.target.value)
563
+ }
564
+ )
565
+ ] })
566
+ ] }),
567
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-1", children: [
568
+ /* @__PURE__ */ jsxRuntime.jsx("label", { className: "text-xs font-semibold text-gray-700", children: "Stroke Color" }),
569
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex gap-2", children: [
570
+ /* @__PURE__ */ jsxRuntime.jsx(
571
+ "input",
572
+ {
573
+ type: "color",
574
+ className: "w-8 h-8 cursor-pointer rounded",
575
+ value: currentElement.strokeColor,
576
+ onChange: (e) => handleFieldChange("strokeColor", e.target.value)
577
+ }
578
+ ),
579
+ /* @__PURE__ */ jsxRuntime.jsx(
580
+ Input,
581
+ {
582
+ value: currentElement.strokeColor,
583
+ onChange: (e) => handleFieldChange("strokeColor", e.target.value)
584
+ }
585
+ )
586
+ ] })
587
+ ] })
588
+ ] }),
589
+ currentElement.shape === "path" && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-4 border dark:border-gray-700 p-4 rounded bg-gray-50 dark:bg-gray-800/50", children: [
590
+ /* @__PURE__ */ jsxRuntime.jsx("h4", { className: "font-semibold text-sm", children: "Path Config" }),
591
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid grid-cols-2 gap-4", children: [
592
+ /* @__PURE__ */ jsxRuntime.jsx(
593
+ Input,
594
+ {
595
+ label: "ViewBox",
596
+ placeholder: "0 0 100 100",
597
+ value: currentElement.viewBox || "",
598
+ onChange: (e) => handleFieldChange("viewBox", e.target.value)
599
+ }
600
+ ),
601
+ /* @__PURE__ */ jsxRuntime.jsx(
602
+ Select,
603
+ {
604
+ label: "Fill Rule",
605
+ options: [
606
+ { value: "nonzero", label: "nonzero" },
607
+ { value: "evenodd", label: "evenodd" }
608
+ ],
609
+ value: currentElement.fillRule || "nonzero",
610
+ onChange: (e) => handleFieldChange("fillRule", e.target.value)
611
+ }
612
+ )
613
+ ] }),
614
+ /* @__PURE__ */ jsxRuntime.jsx(
615
+ TextArea,
616
+ {
617
+ label: "SVG Path (d attribute)",
618
+ placeholder: "M10 10 H 90 V 90 H 10 Z",
619
+ value: currentElement.svgPath || "",
620
+ onChange: (e) => handleFieldChange("svgPath", e.target.value),
621
+ rows: 4
622
+ }
623
+ )
624
+ ] }),
625
+ currentElement.shape === "svg" && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-4 border dark:border-amber-700/50 p-4 rounded bg-amber-50 dark:bg-amber-900/10", children: [
626
+ /* @__PURE__ */ jsxRuntime.jsx("h4", { className: "font-semibold text-sm", children: "SVG Markup (Autosanitized)" }),
627
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-amber-800 dark:text-amber-400", children: "Paste your raw SVG here. Double quotes will be converted to single quotes automatically to safely embed the string in JSON." }),
628
+ /* @__PURE__ */ jsxRuntime.jsx(
629
+ TextArea,
630
+ {
631
+ label: "raw <svg>...</svg>",
632
+ value: currentElement.svgMarkup || "",
633
+ onChange: (e) => handleSvgMarkupChange(e.target.value),
634
+ rows: 6,
635
+ placeholder: "<svg viewBox='0 0 100 100'><circle cx='50' cy='50' r='50'/></svg>"
636
+ }
637
+ )
638
+ ] }),
639
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex justify-end gap-2 mt-4 pt-4 border-t dark:border-gray-700", children: /* @__PURE__ */ jsxRuntime.jsx(Button, { onClick: handleSaveElement, children: "Save Changes to Element" }) })
640
+ ] }) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center justify-center h-full text-gray-400", children: "Select an element to edit or add a new one." })
641
+ ] }),
642
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "w-1/3 flex flex-col gap-2 border-l dark:border-gray-700 pl-4 h-full max-h-full", children: [
643
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between shrink-0", children: [
644
+ /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "font-bold", children: "Output JSON" }),
645
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
646
+ /* @__PURE__ */ jsxRuntime.jsx(
647
+ Input,
648
+ {
649
+ value: downloadFileName,
650
+ onChange: (e) => setDownloadFileName(e.target.value),
651
+ placeholder: "filename",
652
+ title: "Filename without extension"
653
+ }
654
+ ),
655
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs text-gray-500", children: ".json" }),
656
+ /* @__PURE__ */ jsxRuntime.jsx(
657
+ Button,
658
+ {
659
+ variant: "secondary",
660
+ onClick: handleDownload,
661
+ title: "Download JSON file",
662
+ children: "Descargar"
663
+ }
664
+ ),
665
+ /* @__PURE__ */ jsxRuntime.jsx(
666
+ Button,
667
+ {
668
+ variant: "secondary",
669
+ onClick: () => navigator.clipboard.writeText(generatedLib),
670
+ children: "Copy"
671
+ }
672
+ )
673
+ ] })
674
+ ] }),
675
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1 overflow-hidden h-full pb-4", children: /* @__PURE__ */ jsxRuntime.jsx(
676
+ TextArea,
677
+ {
678
+ readOnly: true,
679
+ className: "h-full resize-none font-mono text-xs text-green-600 dark:text-green-400 bg-gray-50 dark:bg-gray-900 border-gray-200 dark:border-gray-800",
680
+ value: generatedLib
681
+ }
682
+ ) })
683
+ ] })
684
+ ] });
685
+ };
686
+
687
+ exports.ElementLibraryBuilder = ElementLibraryBuilder;
688
+ //# sourceMappingURL=index.js.map
689
+ //# sourceMappingURL=index.js.map