neogestify-ui-components 2.1.0 → 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.
- package/README.md +46 -1
- package/dist/components/ElementLibraryBuilder/index.d.mts +5 -0
- package/dist/components/ElementLibraryBuilder/index.d.ts +5 -0
- package/dist/components/ElementLibraryBuilder/index.js +689 -0
- package/dist/components/ElementLibraryBuilder/index.js.map +1 -0
- package/dist/components/ElementLibraryBuilder/index.mjs +687 -0
- package/dist/components/ElementLibraryBuilder/index.mjs.map +1 -0
- package/dist/components/VenueMapEditor/index.d.mts +18 -2
- package/dist/components/VenueMapEditor/index.d.ts +18 -2
- package/dist/components/VenueMapEditor/index.js +75 -3
- package/dist/components/VenueMapEditor/index.js.map +1 -1
- package/dist/components/VenueMapEditor/index.mjs +75 -4
- package/dist/components/VenueMapEditor/index.mjs.map +1 -1
- package/dist/index.d.mts +2 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js +468 -3
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +467 -4
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
- package/src/components/ElementLibraryBuilder/builder.tsx +400 -0
- package/src/components/ElementLibraryBuilder/index.ts +1 -0
- package/src/components/VenueMapEditor/components/ElementNode.tsx +22 -0
- package/src/components/VenueMapEditor/components/PropertiesPanel.tsx +17 -4
- package/src/components/VenueMapEditor/components/Toolbar.tsx +14 -4
- package/src/components/VenueMapEditor/index.ts +2 -0
- package/src/components/VenueMapEditor/types.ts +11 -1
- package/src/components/VenueMapEditor/utils/svgParser.ts +33 -0
- package/src/index.ts +1 -0
package/dist/index.d.mts
CHANGED
|
@@ -2,7 +2,8 @@ export { Button, Form, Input, Loading, Modal, ModalRef, Select, Table, TextArea
|
|
|
2
2
|
export { AddIcon, AnimateSpin, ArchiveIcon, ArrowIcon, ArrowLeftIcon, ArrowRightIcon, BackIcon, BarsChartsIcon, BoxIcon, BuildingIcon, CajasIcon, CalendarIcon, CamaraIcon, CancelIcon, CashIcon, CategorieIcon, ChartIcon, CheckCircleIcon, CheckIcon, ClockIcon, CloseIcon, CloudIcon, CopyIcon, DeleteIcon, DocumentIcon, EditIcon, FacturacionIcon, FilterIcon, FolderIcon, GearIcon, HomeIcon, IconCursor, IconDownload, IconDuplicate, IconErase, IconGrid, IconHand, IconLayers, IconPlace, IconPolygon, IconRedo, IconReset, IconUndo, IconUpload, IconWall, IconZoomIn, IconZoomOut, InfoIcon, LifeGuardIcon, LightingIcon, LocationIcon, LogoutIcon, MenuIcon, MinusIcon, MoneyIcon, MonitorIcon, MoonIcon, NetworkIcon, NotFoundIcon, PasteIcon, PercentIcon, PrinterIcon, QuestionIcon, RestaurantMenuIcon, SaveIcon, SearchIcon, ShieldIcon, SpinnerIcon, StackIcon, SunIcon, TestIcon, TrashIcon, TruckIcon, UsersIcon, WhatsAppIcon } from './components/icons/index.mjs';
|
|
3
3
|
export { Alerta, AlertaAdvertencia, AlertaConfirmacion, AlertaError, AlertaExito, AlertaInfo, AlertaToast, InfoAlert } from './components/alerts/index.mjs';
|
|
4
4
|
export { Theme, ThemeContext, ThemeContextType, ThemeProvider, ThemeToggle, useTheme } from './context/theme/index.mjs';
|
|
5
|
-
export { AreaShape, DomainConfig, ElementGroup, ElementLibrary, ElementShape, ElementStatus, ElementTypeDef, Floor, FloorArea, MapElement, PaletteGroup, PanZoomState, ToolMode, VenueMap, VenueMapEditor, VenueMapEditorProps, VenueMapViewer, VenueMapViewerProps, Wall, WallMaterial, WallNode, findNearestNode, genId, snapPoint, snapToGrid, useLibraryStorage, usePanZoom } from './components/VenueMapEditor/index.mjs';
|
|
5
|
+
export { AreaShape, DomainConfig, ElementGroup, ElementLibrary, ElementShape, ElementStatus, ElementTypeDef, Floor, FloorArea, MapElement, PaletteGroup, PanZoomState, ParsedSvgMarkup, ToolMode, VenueMap, VenueMapEditor, VenueMapEditorProps, VenueMapViewer, VenueMapViewerProps, Wall, WallMaterial, WallNode, findNearestNode, genId, parseSvgMarkup, snapPoint, snapToGrid, useLibraryStorage, usePanZoom } from './components/VenueMapEditor/index.mjs';
|
|
6
|
+
export { ElementLibraryBuilder } from './components/ElementLibraryBuilder/index.mjs';
|
|
6
7
|
import 'react';
|
|
7
8
|
import 'react/jsx-runtime';
|
|
8
9
|
import 'sweetalert2';
|
package/dist/index.d.ts
CHANGED
|
@@ -2,7 +2,8 @@ export { Button, Form, Input, Loading, Modal, ModalRef, Select, Table, TextArea
|
|
|
2
2
|
export { AddIcon, AnimateSpin, ArchiveIcon, ArrowIcon, ArrowLeftIcon, ArrowRightIcon, BackIcon, BarsChartsIcon, BoxIcon, BuildingIcon, CajasIcon, CalendarIcon, CamaraIcon, CancelIcon, CashIcon, CategorieIcon, ChartIcon, CheckCircleIcon, CheckIcon, ClockIcon, CloseIcon, CloudIcon, CopyIcon, DeleteIcon, DocumentIcon, EditIcon, FacturacionIcon, FilterIcon, FolderIcon, GearIcon, HomeIcon, IconCursor, IconDownload, IconDuplicate, IconErase, IconGrid, IconHand, IconLayers, IconPlace, IconPolygon, IconRedo, IconReset, IconUndo, IconUpload, IconWall, IconZoomIn, IconZoomOut, InfoIcon, LifeGuardIcon, LightingIcon, LocationIcon, LogoutIcon, MenuIcon, MinusIcon, MoneyIcon, MonitorIcon, MoonIcon, NetworkIcon, NotFoundIcon, PasteIcon, PercentIcon, PrinterIcon, QuestionIcon, RestaurantMenuIcon, SaveIcon, SearchIcon, ShieldIcon, SpinnerIcon, StackIcon, SunIcon, TestIcon, TrashIcon, TruckIcon, UsersIcon, WhatsAppIcon } from './components/icons/index.js';
|
|
3
3
|
export { Alerta, AlertaAdvertencia, AlertaConfirmacion, AlertaError, AlertaExito, AlertaInfo, AlertaToast, InfoAlert } from './components/alerts/index.js';
|
|
4
4
|
export { Theme, ThemeContext, ThemeContextType, ThemeProvider, ThemeToggle, useTheme } from './context/theme/index.js';
|
|
5
|
-
export { AreaShape, DomainConfig, ElementGroup, ElementLibrary, ElementShape, ElementStatus, ElementTypeDef, Floor, FloorArea, MapElement, PaletteGroup, PanZoomState, ToolMode, VenueMap, VenueMapEditor, VenueMapEditorProps, VenueMapViewer, VenueMapViewerProps, Wall, WallMaterial, WallNode, findNearestNode, genId, snapPoint, snapToGrid, useLibraryStorage, usePanZoom } from './components/VenueMapEditor/index.js';
|
|
5
|
+
export { AreaShape, DomainConfig, ElementGroup, ElementLibrary, ElementShape, ElementStatus, ElementTypeDef, Floor, FloorArea, MapElement, PaletteGroup, PanZoomState, ParsedSvgMarkup, ToolMode, VenueMap, VenueMapEditor, VenueMapEditorProps, VenueMapViewer, VenueMapViewerProps, Wall, WallMaterial, WallNode, findNearestNode, genId, parseSvgMarkup, snapPoint, snapToGrid, useLibraryStorage, usePanZoom } from './components/VenueMapEditor/index.js';
|
|
6
|
+
export { ElementLibraryBuilder } from './components/ElementLibraryBuilder/index.js';
|
|
6
7
|
import 'react';
|
|
7
8
|
import 'react/jsx-runtime';
|
|
8
9
|
import 'sweetalert2';
|
package/dist/index.js
CHANGED
|
@@ -935,6 +935,26 @@ function useLibraryStorage(storageKey) {
|
|
|
935
935
|
);
|
|
936
936
|
return [libs, setAndPersist];
|
|
937
937
|
}
|
|
938
|
+
|
|
939
|
+
// src/components/VenueMapEditor/utils/svgParser.ts
|
|
940
|
+
var DANGEROUS_TAGS = /\b(script|iframe|object|embed|link|style|meta)\b/gi;
|
|
941
|
+
var DANGEROUS_ATTRS = /\bon\w+\s*=/gi;
|
|
942
|
+
var DANGEROUS_HREF = /\bhref\s*=\s*["']?\s*javascript:/gi;
|
|
943
|
+
var DANGEROUS_XLINK = /\bxlink:href\s*=\s*["']?\s*javascript:/gi;
|
|
944
|
+
function sanitize(html) {
|
|
945
|
+
return html.replace(DANGEROUS_TAGS, "").replace(DANGEROUS_ATTRS, "").replace(DANGEROUS_HREF, "").replace(DANGEROUS_XLINK, "");
|
|
946
|
+
}
|
|
947
|
+
var VIEWBOX_RE = /viewBox\s*=\s*"([^"]+)"/i;
|
|
948
|
+
var SVG_OPEN_END_RE = /<svg[^>]*>/i;
|
|
949
|
+
function parseSvgMarkup(markup) {
|
|
950
|
+
const viewBoxMatch = markup.match(VIEWBOX_RE);
|
|
951
|
+
const viewBox = viewBoxMatch?.[1] ?? "0 0 100 100";
|
|
952
|
+
const svgOpenMatch = markup.match(SVG_OPEN_END_RE);
|
|
953
|
+
const afterOpen = svgOpenMatch ? markup.slice(svgOpenMatch.index + svgOpenMatch[0].length) : markup;
|
|
954
|
+
const closeIdx = afterOpen.lastIndexOf("</svg>");
|
|
955
|
+
const inner = closeIdx >= 0 ? afterOpen.slice(0, closeIdx) : afterOpen;
|
|
956
|
+
return { viewBox, innerHtml: sanitize(inner) };
|
|
957
|
+
}
|
|
938
958
|
function ToolButton({ active, disabled, title, onClick, children }) {
|
|
939
959
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
940
960
|
"button",
|
|
@@ -964,7 +984,15 @@ function TypeChip({ typeDef, active, onClick }) {
|
|
|
964
984
|
active ? "border-blue-400 bg-blue-50 text-blue-700 font-medium" : "border-slate-200 bg-white text-slate-600 hover:border-slate-300 hover:bg-slate-50"
|
|
965
985
|
].join(" "),
|
|
966
986
|
children: [
|
|
967
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
987
|
+
typeDef.shape === "svg" && typeDef.svgMarkup ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
988
|
+
"svg",
|
|
989
|
+
{
|
|
990
|
+
viewBox: parseSvgMarkup(typeDef.svgMarkup).viewBox,
|
|
991
|
+
className: "w-2.5 h-2.5 shrink-0",
|
|
992
|
+
style: { color: typeDef.strokeColor },
|
|
993
|
+
dangerouslySetInnerHTML: { __html: parseSvgMarkup(typeDef.svgMarkup).innerHtml }
|
|
994
|
+
}
|
|
995
|
+
) : /* @__PURE__ */ jsxRuntime.jsx(
|
|
968
996
|
"span",
|
|
969
997
|
{
|
|
970
998
|
className: "w-2.5 h-2.5 rounded-sm shrink-0",
|
|
@@ -2033,6 +2061,28 @@ function ElementNode({
|
|
|
2033
2061
|
onClick: handleBodyClick
|
|
2034
2062
|
}
|
|
2035
2063
|
) }),
|
|
2064
|
+
typeDef.shape === "svg" && typeDef.svgMarkup && (() => {
|
|
2065
|
+
const parsed = parseSvgMarkup(typeDef.svgMarkup);
|
|
2066
|
+
const parts = parsed.viewBox.split(/[\s,]+/).map(Number);
|
|
2067
|
+
const vw = parts[2] ?? 100;
|
|
2068
|
+
const vh = parts[3] ?? 100;
|
|
2069
|
+
const sx = vw > 0 ? w / vw : 1;
|
|
2070
|
+
const sy = vh > 0 ? h / vh : 1;
|
|
2071
|
+
const avgScale = Math.sqrt(Math.abs(sx * sy)) || 1;
|
|
2072
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
2073
|
+
"g",
|
|
2074
|
+
{
|
|
2075
|
+
transform: `translate(${x}, ${y}) scale(${sx}, ${sy})`,
|
|
2076
|
+
fill: fillColor,
|
|
2077
|
+
stroke: isSelected ? "#3b82f6" : typeDef.strokeColor,
|
|
2078
|
+
strokeWidth: isSelected ? sw / avgScale * 1.5 : sw / avgScale,
|
|
2079
|
+
style: { cursor: bodyCursor },
|
|
2080
|
+
onMouseDown: tool === "SELECT" && !onViewerClick ? handleBodyDown : void 0,
|
|
2081
|
+
onClick: handleBodyClick,
|
|
2082
|
+
dangerouslySetInnerHTML: { __html: parsed.innerHtml }
|
|
2083
|
+
}
|
|
2084
|
+
);
|
|
2085
|
+
})(),
|
|
2036
2086
|
(element.label ?? typeDef.label) && /* @__PURE__ */ jsxRuntime.jsx(
|
|
2037
2087
|
"text",
|
|
2038
2088
|
{
|
|
@@ -2531,14 +2581,35 @@ function PropertiesPanel({
|
|
|
2531
2581
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-3 py-2 border-b border-slate-100 text-xs font-semibold text-slate-500 uppercase tracking-wide", children: "Propiedades" }),
|
|
2532
2582
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-1 flex flex-col gap-4 p-3", children: [
|
|
2533
2583
|
typeDef && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
|
|
2534
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2584
|
+
typeDef.shape === "svg" ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
2585
|
+
"svg",
|
|
2586
|
+
{
|
|
2587
|
+
viewBox: (() => {
|
|
2588
|
+
try {
|
|
2589
|
+
return typeDef.svgMarkup ? parseSvgMarkup(typeDef.svgMarkup).viewBox : "0 0 100 100";
|
|
2590
|
+
} catch {
|
|
2591
|
+
return "0 0 100 100";
|
|
2592
|
+
}
|
|
2593
|
+
})(),
|
|
2594
|
+
className: "w-3.5 h-3.5 shrink-0 border border-slate-300 rounded-sm",
|
|
2595
|
+
style: { color: typeDef.strokeColor },
|
|
2596
|
+
dangerouslySetInnerHTML: { __html: (() => {
|
|
2597
|
+
try {
|
|
2598
|
+
return typeDef.svgMarkup ? parseSvgMarkup(typeDef.svgMarkup).innerHtml : "";
|
|
2599
|
+
} catch {
|
|
2600
|
+
return "";
|
|
2601
|
+
}
|
|
2602
|
+
})() }
|
|
2603
|
+
}
|
|
2604
|
+
) : /* @__PURE__ */ jsxRuntime.jsx(
|
|
2535
2605
|
"span",
|
|
2536
2606
|
{
|
|
2537
2607
|
className: "w-3.5 h-3.5 rounded-sm shrink-0 border",
|
|
2538
2608
|
style: { background: typeDef.color, borderColor: typeDef.strokeColor }
|
|
2539
2609
|
}
|
|
2540
2610
|
),
|
|
2541
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs font-medium text-slate-700 truncate", children: typeDef.label })
|
|
2611
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs font-medium text-slate-700 truncate", children: typeDef.label }),
|
|
2612
|
+
typeDef.shape === "svg" && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[9px] uppercase tracking-wide text-slate-400 font-medium ml-auto", children: "SVG" })
|
|
2542
2613
|
] }),
|
|
2543
2614
|
/* @__PURE__ */ jsxRuntime.jsxs("label", { className: "flex flex-col gap-0.5", children: [
|
|
2544
2615
|
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[10px] font-medium text-slate-400 uppercase tracking-wide", children: "Etiqueta" }),
|
|
@@ -3606,6 +3677,398 @@ function VenueMapViewer({ elementStatus, onElementClick, ...rest }) {
|
|
|
3606
3677
|
}
|
|
3607
3678
|
);
|
|
3608
3679
|
}
|
|
3680
|
+
var DEFAULT_ELEMENT = {
|
|
3681
|
+
id: "",
|
|
3682
|
+
label: "",
|
|
3683
|
+
shape: "rect",
|
|
3684
|
+
defaultWidth: 100,
|
|
3685
|
+
defaultHeight: 100,
|
|
3686
|
+
color: "#cccccc",
|
|
3687
|
+
strokeColor: "#000000"
|
|
3688
|
+
};
|
|
3689
|
+
var SHAPE_OPTIONS = [
|
|
3690
|
+
{ value: "rect", label: "Rectangle" },
|
|
3691
|
+
{ value: "circle", label: "Circle" },
|
|
3692
|
+
{ value: "arrow", label: "Arrow" },
|
|
3693
|
+
{ value: "path", label: "Path" },
|
|
3694
|
+
{ value: "svg", label: "SVG Markup" }
|
|
3695
|
+
];
|
|
3696
|
+
var ElementLibraryBuilder = () => {
|
|
3697
|
+
const [groups, setGroups] = react.useState([
|
|
3698
|
+
{ internalId: crypto.randomUUID(), name: "defaultGroup", objects: [] }
|
|
3699
|
+
]);
|
|
3700
|
+
const [activeGroupId, setActiveGroupId] = react.useState(groups[0].internalId);
|
|
3701
|
+
const [editingGroupId, setEditingGroupId] = react.useState(null);
|
|
3702
|
+
const [activeElementIndex, setActiveElementIndex] = react.useState(null);
|
|
3703
|
+
const [currentElement, setCurrentElement] = react.useState({ ...DEFAULT_ELEMENT, id: "rect_1", label: "New Rect" });
|
|
3704
|
+
const [downloadFileName, setDownloadFileName] = react.useState("libraries");
|
|
3705
|
+
const handleAddGroup = () => {
|
|
3706
|
+
const newGroupId = crypto.randomUUID();
|
|
3707
|
+
const newName = `group_${groups.length + 1}`;
|
|
3708
|
+
setGroups([...groups, { internalId: newGroupId, name: newName, objects: [] }]);
|
|
3709
|
+
setActiveGroupId(newGroupId);
|
|
3710
|
+
setActiveElementIndex(null);
|
|
3711
|
+
};
|
|
3712
|
+
const handleRemoveGroup = (id) => {
|
|
3713
|
+
const newGroups = groups.filter((g) => g.internalId !== id);
|
|
3714
|
+
setGroups(newGroups);
|
|
3715
|
+
if (activeGroupId === id) {
|
|
3716
|
+
if (newGroups.length > 0) {
|
|
3717
|
+
setActiveGroupId(newGroups[0].internalId);
|
|
3718
|
+
} else {
|
|
3719
|
+
setActiveGroupId("");
|
|
3720
|
+
}
|
|
3721
|
+
setActiveElementIndex(null);
|
|
3722
|
+
}
|
|
3723
|
+
};
|
|
3724
|
+
const activeGroup = groups.find((g) => g.internalId === activeGroupId);
|
|
3725
|
+
const handleSelectGroup = (gId) => {
|
|
3726
|
+
setActiveGroupId(gId);
|
|
3727
|
+
setActiveElementIndex(null);
|
|
3728
|
+
};
|
|
3729
|
+
const handleAddElement = () => {
|
|
3730
|
+
if (!activeGroup) return;
|
|
3731
|
+
const newEl = { ...DEFAULT_ELEMENT, id: `shape_${activeGroup.objects.length + 1}`, label: `Shape ${activeGroup.objects.length + 1}` };
|
|
3732
|
+
const updatedGroups = groups.map((g) => {
|
|
3733
|
+
if (g.internalId === activeGroupId) {
|
|
3734
|
+
return { ...g, objects: [...g.objects, newEl] };
|
|
3735
|
+
}
|
|
3736
|
+
return g;
|
|
3737
|
+
});
|
|
3738
|
+
setGroups(updatedGroups);
|
|
3739
|
+
setActiveElementIndex(activeGroup.objects.length);
|
|
3740
|
+
setCurrentElement(newEl);
|
|
3741
|
+
};
|
|
3742
|
+
const handleSelectElement = (idx) => {
|
|
3743
|
+
if (!activeGroup) return;
|
|
3744
|
+
setActiveElementIndex(idx);
|
|
3745
|
+
setCurrentElement(activeGroup.objects[idx]);
|
|
3746
|
+
};
|
|
3747
|
+
const handleRemoveElement = (idx) => {
|
|
3748
|
+
if (!activeGroup) return;
|
|
3749
|
+
const updatedGroups = groups.map((g) => {
|
|
3750
|
+
if (g.internalId === activeGroupId) {
|
|
3751
|
+
const newObjs = [...g.objects];
|
|
3752
|
+
newObjs.splice(idx, 1);
|
|
3753
|
+
return { ...g, objects: newObjs };
|
|
3754
|
+
}
|
|
3755
|
+
return g;
|
|
3756
|
+
});
|
|
3757
|
+
setGroups(updatedGroups);
|
|
3758
|
+
if (activeElementIndex === idx) {
|
|
3759
|
+
setActiveElementIndex(null);
|
|
3760
|
+
} else if (activeElementIndex !== null && activeElementIndex > idx) {
|
|
3761
|
+
setActiveElementIndex(activeElementIndex - 1);
|
|
3762
|
+
}
|
|
3763
|
+
};
|
|
3764
|
+
const handleSaveElement = () => {
|
|
3765
|
+
if (!activeGroup || activeElementIndex === null) return;
|
|
3766
|
+
const updatedGroups = groups.map((g) => {
|
|
3767
|
+
if (g.internalId === activeGroupId) {
|
|
3768
|
+
const newObjs = [...g.objects];
|
|
3769
|
+
newObjs[activeElementIndex] = { ...currentElement };
|
|
3770
|
+
return { ...g, objects: newObjs };
|
|
3771
|
+
}
|
|
3772
|
+
return g;
|
|
3773
|
+
});
|
|
3774
|
+
setGroups(updatedGroups);
|
|
3775
|
+
};
|
|
3776
|
+
const handleFieldChange = (field, value) => {
|
|
3777
|
+
setCurrentElement((prev) => ({ ...prev, [field]: value }));
|
|
3778
|
+
};
|
|
3779
|
+
const handleSvgMarkupChange = (value) => {
|
|
3780
|
+
handleFieldChange("svgMarkup", value);
|
|
3781
|
+
};
|
|
3782
|
+
const generatedLib = react.useMemo(() => {
|
|
3783
|
+
const lib = {};
|
|
3784
|
+
groups.forEach((g) => {
|
|
3785
|
+
lib[g.name] = {
|
|
3786
|
+
name: g.name,
|
|
3787
|
+
objects: g.objects
|
|
3788
|
+
};
|
|
3789
|
+
});
|
|
3790
|
+
return JSON.stringify(lib, null, 2);
|
|
3791
|
+
}, [groups]);
|
|
3792
|
+
const handleDownload = () => {
|
|
3793
|
+
const blob = new Blob([generatedLib], { type: "application/json" });
|
|
3794
|
+
const url = URL.createObjectURL(blob);
|
|
3795
|
+
const a = document.createElement("a");
|
|
3796
|
+
a.href = url;
|
|
3797
|
+
a.download = `${downloadFileName}.json`;
|
|
3798
|
+
document.body.appendChild(a);
|
|
3799
|
+
a.click();
|
|
3800
|
+
document.body.removeChild(a);
|
|
3801
|
+
URL.revokeObjectURL(url);
|
|
3802
|
+
};
|
|
3803
|
+
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: [
|
|
3804
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "w-1/4 flex flex-col gap-4 border-r dark:border-gray-700 pr-4", children: [
|
|
3805
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-2", children: [
|
|
3806
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between", children: [
|
|
3807
|
+
/* @__PURE__ */ jsxRuntime.jsx("h3", { className: "font-bold", children: "Libraries (Groups)" }),
|
|
3808
|
+
/* @__PURE__ */ jsxRuntime.jsx(Button, { variant: "primary", onClick: handleAddGroup, children: "+ Group" })
|
|
3809
|
+
] }),
|
|
3810
|
+
/* @__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(
|
|
3811
|
+
"div",
|
|
3812
|
+
{
|
|
3813
|
+
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"}`,
|
|
3814
|
+
onClick: () => handleSelectGroup(group.internalId),
|
|
3815
|
+
children: [
|
|
3816
|
+
editingGroupId === group.internalId ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
3817
|
+
Input,
|
|
3818
|
+
{
|
|
3819
|
+
autoFocus: true,
|
|
3820
|
+
value: group.name,
|
|
3821
|
+
onChange: (e) => {
|
|
3822
|
+
setGroups(groups.map((g) => g.internalId === group.internalId ? { ...g, name: e.target.value } : g));
|
|
3823
|
+
},
|
|
3824
|
+
onBlur: () => setEditingGroupId(null),
|
|
3825
|
+
onKeyDown: (e) => e.key === "Enter" && setEditingGroupId(null)
|
|
3826
|
+
}
|
|
3827
|
+
) : /* @__PURE__ */ jsxRuntime.jsx("span", { onDoubleClick: () => setEditingGroupId(group.internalId), children: group.name }),
|
|
3828
|
+
groups.length > 1 && /* @__PURE__ */ jsxRuntime.jsx("button", { onClick: (e) => {
|
|
3829
|
+
e.stopPropagation();
|
|
3830
|
+
handleRemoveGroup(group.internalId);
|
|
3831
|
+
}, className: "text-red-500 text-xs", children: "x" })
|
|
3832
|
+
]
|
|
3833
|
+
},
|
|
3834
|
+
group.internalId
|
|
3835
|
+
)) })
|
|
3836
|
+
] }),
|
|
3837
|
+
/* @__PURE__ */ jsxRuntime.jsx("hr", { className: "dark:border-gray-700" }),
|
|
3838
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-2 flex-grow overflow-hidden", children: [
|
|
3839
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between", children: [
|
|
3840
|
+
/* @__PURE__ */ jsxRuntime.jsxs("h3", { className: "font-bold", children: [
|
|
3841
|
+
"Elements in ",
|
|
3842
|
+
activeGroup?.name || "?"
|
|
3843
|
+
] }),
|
|
3844
|
+
/* @__PURE__ */ jsxRuntime.jsx(Button, { variant: "secondary", onClick: handleAddElement, disabled: !activeGroup, children: "+ Element" })
|
|
3845
|
+
] }),
|
|
3846
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-1 overflow-y-auto flex-grow pr-1", children: [
|
|
3847
|
+
activeGroup?.objects.map((el, i) => /* @__PURE__ */ jsxRuntime.jsxs(
|
|
3848
|
+
"div",
|
|
3849
|
+
{
|
|
3850
|
+
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"}`,
|
|
3851
|
+
onClick: () => handleSelectElement(i),
|
|
3852
|
+
children: [
|
|
3853
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
|
|
3854
|
+
el.id,
|
|
3855
|
+
" (",
|
|
3856
|
+
el.shape,
|
|
3857
|
+
")"
|
|
3858
|
+
] }),
|
|
3859
|
+
/* @__PURE__ */ jsxRuntime.jsx("button", { onClick: (e) => {
|
|
3860
|
+
e.stopPropagation();
|
|
3861
|
+
handleRemoveElement(i);
|
|
3862
|
+
}, className: "text-red-500 text-xs", children: "x" })
|
|
3863
|
+
]
|
|
3864
|
+
},
|
|
3865
|
+
i
|
|
3866
|
+
)),
|
|
3867
|
+
(!activeGroup || activeGroup.objects.length === 0) && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-gray-400 dark:text-gray-500 italic text-xs", children: "No elements yet" })
|
|
3868
|
+
] })
|
|
3869
|
+
] })
|
|
3870
|
+
] }),
|
|
3871
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-1 flex flex-col gap-4 px-2 overflow-y-auto", children: [
|
|
3872
|
+
/* @__PURE__ */ jsxRuntime.jsx("h3", { className: "font-bold text-lg", children: "Element Editor" }),
|
|
3873
|
+
activeElementIndex !== null ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-4 w-full max-w-2xl", children: [
|
|
3874
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid grid-cols-2 gap-4", children: [
|
|
3875
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3876
|
+
Input,
|
|
3877
|
+
{
|
|
3878
|
+
label: "Element ID (unique)",
|
|
3879
|
+
value: currentElement.id,
|
|
3880
|
+
onChange: (e) => handleFieldChange("id", e.target.value)
|
|
3881
|
+
}
|
|
3882
|
+
),
|
|
3883
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3884
|
+
Input,
|
|
3885
|
+
{
|
|
3886
|
+
label: "Label (display name)",
|
|
3887
|
+
value: currentElement.label,
|
|
3888
|
+
onChange: (e) => handleFieldChange("label", e.target.value)
|
|
3889
|
+
}
|
|
3890
|
+
)
|
|
3891
|
+
] }),
|
|
3892
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid grid-cols-2 gap-4", children: [
|
|
3893
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3894
|
+
Select,
|
|
3895
|
+
{
|
|
3896
|
+
label: "Shape",
|
|
3897
|
+
options: SHAPE_OPTIONS,
|
|
3898
|
+
value: currentElement.shape,
|
|
3899
|
+
onChange: (e) => handleFieldChange("shape", e.target.value)
|
|
3900
|
+
}
|
|
3901
|
+
),
|
|
3902
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3903
|
+
Input,
|
|
3904
|
+
{
|
|
3905
|
+
label: "Icon (emoji or class)",
|
|
3906
|
+
value: currentElement.icon || "",
|
|
3907
|
+
onChange: (e) => handleFieldChange("icon", e.target.value)
|
|
3908
|
+
}
|
|
3909
|
+
)
|
|
3910
|
+
] }),
|
|
3911
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid grid-cols-2 gap-4", children: [
|
|
3912
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3913
|
+
Input,
|
|
3914
|
+
{
|
|
3915
|
+
type: "number",
|
|
3916
|
+
label: "Default Width",
|
|
3917
|
+
value: currentElement.defaultWidth,
|
|
3918
|
+
onChange: (e) => handleFieldChange("defaultWidth", parseFloat(e.target.value) || 0)
|
|
3919
|
+
}
|
|
3920
|
+
),
|
|
3921
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3922
|
+
Input,
|
|
3923
|
+
{
|
|
3924
|
+
type: "number",
|
|
3925
|
+
label: "Default Height",
|
|
3926
|
+
value: currentElement.defaultHeight,
|
|
3927
|
+
onChange: (e) => handleFieldChange("defaultHeight", parseFloat(e.target.value) || 0)
|
|
3928
|
+
}
|
|
3929
|
+
)
|
|
3930
|
+
] }),
|
|
3931
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid grid-cols-2 gap-4", children: [
|
|
3932
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-1", children: [
|
|
3933
|
+
/* @__PURE__ */ jsxRuntime.jsx("label", { className: "text-xs font-semibold text-gray-700", children: "Fill Color" }),
|
|
3934
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex gap-2", children: [
|
|
3935
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3936
|
+
"input",
|
|
3937
|
+
{
|
|
3938
|
+
type: "color",
|
|
3939
|
+
className: "w-8 h-8 cursor-pointer rounded",
|
|
3940
|
+
value: currentElement.color,
|
|
3941
|
+
onChange: (e) => handleFieldChange("color", e.target.value)
|
|
3942
|
+
}
|
|
3943
|
+
),
|
|
3944
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3945
|
+
Input,
|
|
3946
|
+
{
|
|
3947
|
+
value: currentElement.color,
|
|
3948
|
+
onChange: (e) => handleFieldChange("color", e.target.value)
|
|
3949
|
+
}
|
|
3950
|
+
)
|
|
3951
|
+
] })
|
|
3952
|
+
] }),
|
|
3953
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-1", children: [
|
|
3954
|
+
/* @__PURE__ */ jsxRuntime.jsx("label", { className: "text-xs font-semibold text-gray-700", children: "Stroke Color" }),
|
|
3955
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex gap-2", children: [
|
|
3956
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3957
|
+
"input",
|
|
3958
|
+
{
|
|
3959
|
+
type: "color",
|
|
3960
|
+
className: "w-8 h-8 cursor-pointer rounded",
|
|
3961
|
+
value: currentElement.strokeColor,
|
|
3962
|
+
onChange: (e) => handleFieldChange("strokeColor", e.target.value)
|
|
3963
|
+
}
|
|
3964
|
+
),
|
|
3965
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3966
|
+
Input,
|
|
3967
|
+
{
|
|
3968
|
+
value: currentElement.strokeColor,
|
|
3969
|
+
onChange: (e) => handleFieldChange("strokeColor", e.target.value)
|
|
3970
|
+
}
|
|
3971
|
+
)
|
|
3972
|
+
] })
|
|
3973
|
+
] })
|
|
3974
|
+
] }),
|
|
3975
|
+
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: [
|
|
3976
|
+
/* @__PURE__ */ jsxRuntime.jsx("h4", { className: "font-semibold text-sm", children: "Path Config" }),
|
|
3977
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid grid-cols-2 gap-4", children: [
|
|
3978
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3979
|
+
Input,
|
|
3980
|
+
{
|
|
3981
|
+
label: "ViewBox",
|
|
3982
|
+
placeholder: "0 0 100 100",
|
|
3983
|
+
value: currentElement.viewBox || "",
|
|
3984
|
+
onChange: (e) => handleFieldChange("viewBox", e.target.value)
|
|
3985
|
+
}
|
|
3986
|
+
),
|
|
3987
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3988
|
+
Select,
|
|
3989
|
+
{
|
|
3990
|
+
label: "Fill Rule",
|
|
3991
|
+
options: [
|
|
3992
|
+
{ value: "nonzero", label: "nonzero" },
|
|
3993
|
+
{ value: "evenodd", label: "evenodd" }
|
|
3994
|
+
],
|
|
3995
|
+
value: currentElement.fillRule || "nonzero",
|
|
3996
|
+
onChange: (e) => handleFieldChange("fillRule", e.target.value)
|
|
3997
|
+
}
|
|
3998
|
+
)
|
|
3999
|
+
] }),
|
|
4000
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
4001
|
+
TextArea,
|
|
4002
|
+
{
|
|
4003
|
+
label: "SVG Path (d attribute)",
|
|
4004
|
+
placeholder: "M10 10 H 90 V 90 H 10 Z",
|
|
4005
|
+
value: currentElement.svgPath || "",
|
|
4006
|
+
onChange: (e) => handleFieldChange("svgPath", e.target.value),
|
|
4007
|
+
rows: 4
|
|
4008
|
+
}
|
|
4009
|
+
)
|
|
4010
|
+
] }),
|
|
4011
|
+
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: [
|
|
4012
|
+
/* @__PURE__ */ jsxRuntime.jsx("h4", { className: "font-semibold text-sm", children: "SVG Markup (Autosanitized)" }),
|
|
4013
|
+
/* @__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." }),
|
|
4014
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
4015
|
+
TextArea,
|
|
4016
|
+
{
|
|
4017
|
+
label: "raw <svg>...</svg>",
|
|
4018
|
+
value: currentElement.svgMarkup || "",
|
|
4019
|
+
onChange: (e) => handleSvgMarkupChange(e.target.value),
|
|
4020
|
+
rows: 6,
|
|
4021
|
+
placeholder: "<svg viewBox='0 0 100 100'><circle cx='50' cy='50' r='50'/></svg>"
|
|
4022
|
+
}
|
|
4023
|
+
)
|
|
4024
|
+
] }),
|
|
4025
|
+
/* @__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" }) })
|
|
4026
|
+
] }) : /* @__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." })
|
|
4027
|
+
] }),
|
|
4028
|
+
/* @__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: [
|
|
4029
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between shrink-0", children: [
|
|
4030
|
+
/* @__PURE__ */ jsxRuntime.jsx("h3", { className: "font-bold", children: "Output JSON" }),
|
|
4031
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
|
|
4032
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
4033
|
+
Input,
|
|
4034
|
+
{
|
|
4035
|
+
value: downloadFileName,
|
|
4036
|
+
onChange: (e) => setDownloadFileName(e.target.value),
|
|
4037
|
+
placeholder: "filename",
|
|
4038
|
+
title: "Filename without extension"
|
|
4039
|
+
}
|
|
4040
|
+
),
|
|
4041
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs text-gray-500", children: ".json" }),
|
|
4042
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
4043
|
+
Button,
|
|
4044
|
+
{
|
|
4045
|
+
variant: "secondary",
|
|
4046
|
+
onClick: handleDownload,
|
|
4047
|
+
title: "Download JSON file",
|
|
4048
|
+
children: "Descargar"
|
|
4049
|
+
}
|
|
4050
|
+
),
|
|
4051
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
4052
|
+
Button,
|
|
4053
|
+
{
|
|
4054
|
+
variant: "secondary",
|
|
4055
|
+
onClick: () => navigator.clipboard.writeText(generatedLib),
|
|
4056
|
+
children: "Copy"
|
|
4057
|
+
}
|
|
4058
|
+
)
|
|
4059
|
+
] })
|
|
4060
|
+
] }),
|
|
4061
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1 overflow-hidden h-full pb-4", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
4062
|
+
TextArea,
|
|
4063
|
+
{
|
|
4064
|
+
readOnly: true,
|
|
4065
|
+
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",
|
|
4066
|
+
value: generatedLib
|
|
4067
|
+
}
|
|
4068
|
+
) })
|
|
4069
|
+
] })
|
|
4070
|
+
] });
|
|
4071
|
+
};
|
|
3609
4072
|
|
|
3610
4073
|
exports.AddIcon = AddIcon;
|
|
3611
4074
|
exports.Alerta = Alerta;
|
|
@@ -3641,6 +4104,7 @@ exports.CopyIcon = CopyIcon;
|
|
|
3641
4104
|
exports.DeleteIcon = DeleteIcon;
|
|
3642
4105
|
exports.DocumentIcon = DocumentIcon;
|
|
3643
4106
|
exports.EditIcon = EditIcon;
|
|
4107
|
+
exports.ElementLibraryBuilder = ElementLibraryBuilder;
|
|
3644
4108
|
exports.FacturacionIcon = FacturacionIcon;
|
|
3645
4109
|
exports.FilterIcon = FilterIcon;
|
|
3646
4110
|
exports.FolderIcon = FolderIcon;
|
|
@@ -3705,6 +4169,7 @@ exports.VenueMapViewer = VenueMapViewer;
|
|
|
3705
4169
|
exports.WhatsAppIcon = WhatsAppIcon;
|
|
3706
4170
|
exports.findNearestNode = findNearestNode;
|
|
3707
4171
|
exports.genId = genId;
|
|
4172
|
+
exports.parseSvgMarkup = parseSvgMarkup;
|
|
3708
4173
|
exports.snapPoint = snapPoint;
|
|
3709
4174
|
exports.snapToGrid = snapToGrid;
|
|
3710
4175
|
exports.useLibraryStorage = useLibraryStorage;
|