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.
- package/README.md +153 -19
- 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 +66 -5
- package/dist/components/VenueMapEditor/index.d.ts +66 -5
- package/dist/components/VenueMapEditor/index.js +199 -34
- package/dist/components/VenueMapEditor/index.js.map +1 -1
- package/dist/components/VenueMapEditor/index.mjs +199 -36
- 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 +592 -34
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +591 -36
- 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/VenueMapEditor.tsx +79 -20
- package/src/components/VenueMapEditor/components/ElementNode.tsx +23 -0
- package/src/components/VenueMapEditor/components/PropertiesPanel.tsx +17 -4
- package/src/components/VenueMapEditor/components/Toolbar.tsx +73 -39
- package/src/components/VenueMapEditor/hooks/useLibraryStorage.ts +46 -0
- package/src/components/VenueMapEditor/index.ts +3 -0
- package/src/components/VenueMapEditor/types.ts +45 -3
- package/src/components/VenueMapEditor/utils/svgParser.ts +33 -0
- package/src/index.ts +1 -0
package/dist/index.js
CHANGED
|
@@ -908,6 +908,53 @@ function ThemeToggle() {
|
|
|
908
908
|
}
|
|
909
909
|
);
|
|
910
910
|
}
|
|
911
|
+
function useLibraryStorage(storageKey) {
|
|
912
|
+
const [libs, setLibs] = react.useState(() => {
|
|
913
|
+
if (!storageKey) return {};
|
|
914
|
+
try {
|
|
915
|
+
const raw = localStorage.getItem(storageKey);
|
|
916
|
+
return raw ? JSON.parse(raw) : {};
|
|
917
|
+
} catch {
|
|
918
|
+
return {};
|
|
919
|
+
}
|
|
920
|
+
});
|
|
921
|
+
const setAndPersist = react.useCallback(
|
|
922
|
+
(newLibs) => {
|
|
923
|
+
setLibs(newLibs);
|
|
924
|
+
if (!storageKey) return;
|
|
925
|
+
try {
|
|
926
|
+
if (Object.keys(newLibs).length === 0) {
|
|
927
|
+
localStorage.removeItem(storageKey);
|
|
928
|
+
} else {
|
|
929
|
+
localStorage.setItem(storageKey, JSON.stringify(newLibs));
|
|
930
|
+
}
|
|
931
|
+
} catch {
|
|
932
|
+
}
|
|
933
|
+
},
|
|
934
|
+
[storageKey]
|
|
935
|
+
);
|
|
936
|
+
return [libs, setAndPersist];
|
|
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
|
+
}
|
|
911
958
|
function ToolButton({ active, disabled, title, onClick, children }) {
|
|
912
959
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
913
960
|
"button",
|
|
@@ -937,7 +984,15 @@ function TypeChip({ typeDef, active, onClick }) {
|
|
|
937
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"
|
|
938
985
|
].join(" "),
|
|
939
986
|
children: [
|
|
940
|
-
/* @__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(
|
|
941
996
|
"span",
|
|
942
997
|
{
|
|
943
998
|
className: "w-2.5 h-2.5 rounded-sm shrink-0",
|
|
@@ -972,6 +1027,19 @@ function Toolbar({
|
|
|
972
1027
|
onLoadLibrary,
|
|
973
1028
|
onRemoveLibraryGroup
|
|
974
1029
|
}) {
|
|
1030
|
+
const [activeGroupId, setActiveGroupId] = react.useState(
|
|
1031
|
+
() => paletteGroups[0]?.id ?? null
|
|
1032
|
+
);
|
|
1033
|
+
react.useEffect(() => {
|
|
1034
|
+
if (paletteGroups.length === 0) {
|
|
1035
|
+
setActiveGroupId(null);
|
|
1036
|
+
return;
|
|
1037
|
+
}
|
|
1038
|
+
if (!paletteGroups.find((g) => g.id === activeGroupId)) {
|
|
1039
|
+
setActiveGroupId(paletteGroups[0].id);
|
|
1040
|
+
}
|
|
1041
|
+
}, [paletteGroups, activeGroupId]);
|
|
1042
|
+
const activeGroup = paletteGroups.find((g) => g.id === activeGroupId) ?? null;
|
|
975
1043
|
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col bg-white border-b border-slate-200 shadow-sm shrink-0", children: [
|
|
976
1044
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-0.5 px-2 py-1.5", children: [
|
|
977
1045
|
/* @__PURE__ */ jsxRuntime.jsx(ToolButton, { title: "Seleccionar (V)", active: tool === "SELECT", onClick: () => onToolChange("SELECT"), children: /* @__PURE__ */ jsxRuntime.jsx(IconCursor, { className: "w-4 h-4" }) }),
|
|
@@ -1009,21 +1077,34 @@ function Toolbar({
|
|
|
1009
1077
|
/* @__PURE__ */ jsxRuntime.jsx(ToolButton, { title: areaShape === "polygon" ? "Cambiar a rect\xE1ngulo" : "Cambiar a pol\xEDgono", onClick: () => onToggleAreaShape?.(), children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs font-medium", children: areaShape === "polygon" ? "Poly" : "Rect" }) })
|
|
1010
1078
|
] })
|
|
1011
1079
|
] }),
|
|
1012
|
-
tool === "PLACE" && /* @__PURE__ */ jsxRuntime.
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1080
|
+
tool === "PLACE" && paletteGroups.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col border-t border-slate-100", children: [
|
|
1081
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-end gap-0 overflow-x-auto bg-slate-50 border-b border-slate-200 px-2 pt-1", children: paletteGroups.map((group) => /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center shrink-0", children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
1082
|
+
"button",
|
|
1083
|
+
{
|
|
1084
|
+
onClick: () => setActiveGroupId(group.id),
|
|
1085
|
+
className: [
|
|
1086
|
+
"flex items-center gap-1 px-3 py-1 text-xs font-medium rounded-t border-x border-t transition-colors whitespace-nowrap",
|
|
1087
|
+
group.id === activeGroupId ? "bg-white border-slate-200 text-slate-800 -mb-px pb-[5px]" : "bg-slate-50 border-transparent text-slate-400 hover:text-slate-600 hover:bg-slate-100"
|
|
1088
|
+
].join(" "),
|
|
1089
|
+
children: [
|
|
1090
|
+
group.name || "Sin nombre",
|
|
1091
|
+
!group.isBase && onRemoveLibraryGroup && /* @__PURE__ */ jsxRuntime.jsx(
|
|
1092
|
+
"span",
|
|
1093
|
+
{
|
|
1094
|
+
role: "button",
|
|
1095
|
+
title: `Eliminar "${group.name}"`,
|
|
1096
|
+
onClick: (e) => {
|
|
1097
|
+
e.stopPropagation();
|
|
1098
|
+
onRemoveLibraryGroup(group.id);
|
|
1099
|
+
},
|
|
1100
|
+
className: "ml-0.5 text-slate-300 hover:text-red-400 transition-colors leading-none",
|
|
1101
|
+
children: "\xD7"
|
|
1102
|
+
}
|
|
1103
|
+
)
|
|
1104
|
+
]
|
|
1105
|
+
}
|
|
1106
|
+
) }, group.id)) }),
|
|
1107
|
+
activeGroup && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center gap-1 flex-wrap px-2 py-1.5 bg-white min-h-[36px]", children: activeGroup.types.map((typeDef) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
1027
1108
|
TypeChip,
|
|
1028
1109
|
{
|
|
1029
1110
|
typeDef,
|
|
@@ -1032,7 +1113,7 @@ function Toolbar({
|
|
|
1032
1113
|
},
|
|
1033
1114
|
typeDef.id
|
|
1034
1115
|
)) })
|
|
1035
|
-
] }
|
|
1116
|
+
] })
|
|
1036
1117
|
] });
|
|
1037
1118
|
}
|
|
1038
1119
|
var ZOOM_MIN = 0.1;
|
|
@@ -1972,6 +2053,7 @@ function ElementNode({
|
|
|
1972
2053
|
{
|
|
1973
2054
|
d: typeDef.svgPath,
|
|
1974
2055
|
fill: fillColor,
|
|
2056
|
+
fillRule: typeDef.fillRule ?? "nonzero",
|
|
1975
2057
|
stroke: isSelected ? "#3b82f6" : typeDef.strokeColor,
|
|
1976
2058
|
strokeWidth: isSelected ? customPath.strokeWidth * 1.5 : customPath.strokeWidth,
|
|
1977
2059
|
style: { cursor: bodyCursor },
|
|
@@ -1979,6 +2061,28 @@ function ElementNode({
|
|
|
1979
2061
|
onClick: handleBodyClick
|
|
1980
2062
|
}
|
|
1981
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
|
+
})(),
|
|
1982
2086
|
(element.label ?? typeDef.label) && /* @__PURE__ */ jsxRuntime.jsx(
|
|
1983
2087
|
"text",
|
|
1984
2088
|
{
|
|
@@ -2477,14 +2581,35 @@ function PropertiesPanel({
|
|
|
2477
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" }),
|
|
2478
2582
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-1 flex flex-col gap-4 p-3", children: [
|
|
2479
2583
|
typeDef && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
|
|
2480
|
-
/* @__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(
|
|
2481
2605
|
"span",
|
|
2482
2606
|
{
|
|
2483
2607
|
className: "w-3.5 h-3.5 rounded-sm shrink-0 border",
|
|
2484
2608
|
style: { background: typeDef.color, borderColor: typeDef.strokeColor }
|
|
2485
2609
|
}
|
|
2486
2610
|
),
|
|
2487
|
-
/* @__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" })
|
|
2488
2613
|
] }),
|
|
2489
2614
|
/* @__PURE__ */ jsxRuntime.jsxs("label", { className: "flex flex-col gap-0.5", children: [
|
|
2490
2615
|
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[10px] font-medium text-slate-400 uppercase tracking-wide", children: "Etiqueta" }),
|
|
@@ -2819,7 +2944,23 @@ function createDefaultMap() {
|
|
|
2819
2944
|
]
|
|
2820
2945
|
};
|
|
2821
2946
|
}
|
|
2822
|
-
var
|
|
2947
|
+
var DEFAULT_LIBRARY_KEY = "venueMapEditor:libraries";
|
|
2948
|
+
function mergeLibraries(existing, incoming) {
|
|
2949
|
+
const result = { ...existing };
|
|
2950
|
+
for (const [groupId, incomingGroup] of Object.entries(incoming)) {
|
|
2951
|
+
if (result[groupId]) {
|
|
2952
|
+
const existingIds = new Set(result[groupId].objects.map((o) => o.id));
|
|
2953
|
+
const newObjects = incomingGroup.objects.filter((o) => !existingIds.has(o.id));
|
|
2954
|
+
result[groupId] = {
|
|
2955
|
+
...result[groupId],
|
|
2956
|
+
objects: [...result[groupId].objects, ...newObjects]
|
|
2957
|
+
};
|
|
2958
|
+
} else {
|
|
2959
|
+
result[groupId] = incomingGroup;
|
|
2960
|
+
}
|
|
2961
|
+
}
|
|
2962
|
+
return result;
|
|
2963
|
+
}
|
|
2823
2964
|
function updateFloor(map, updatedFloor) {
|
|
2824
2965
|
return {
|
|
2825
2966
|
...map,
|
|
@@ -2859,7 +3000,9 @@ function polygonToRect(area) {
|
|
|
2859
3000
|
};
|
|
2860
3001
|
}
|
|
2861
3002
|
function VenueMapEditor({
|
|
2862
|
-
|
|
3003
|
+
domainConfigs,
|
|
3004
|
+
domainConfig,
|
|
3005
|
+
libraryStorageKey = DEFAULT_LIBRARY_KEY,
|
|
2863
3006
|
initialMap,
|
|
2864
3007
|
onChange,
|
|
2865
3008
|
width = "100%",
|
|
@@ -2873,7 +3016,13 @@ function VenueMapEditor({
|
|
|
2873
3016
|
onElementClick,
|
|
2874
3017
|
onElementTypeClick
|
|
2875
3018
|
}) {
|
|
3019
|
+
const effectiveConfigs = react.useMemo(() => {
|
|
3020
|
+
if (domainConfigs && domainConfigs.length > 0) return domainConfigs;
|
|
3021
|
+
if (domainConfig) return [domainConfig];
|
|
3022
|
+
return [];
|
|
3023
|
+
}, [domainConfigs, domainConfig]);
|
|
2876
3024
|
const initialMapRef = react.useRef(initialMap ?? createDefaultMap());
|
|
3025
|
+
const [persistedLibs, setPersistedLibs] = useLibraryStorage(libraryStorageKey);
|
|
2877
3026
|
const { map, canUndo, canRedo, push, replace, undo, redo } = useHistory(
|
|
2878
3027
|
initialMapRef.current
|
|
2879
3028
|
);
|
|
@@ -2889,31 +3038,40 @@ function VenueMapEditor({
|
|
|
2889
3038
|
const resetViewRef = react.useRef(() => void 0);
|
|
2890
3039
|
const importInputRef = react.useRef(null);
|
|
2891
3040
|
const libraryInputRef = react.useRef(null);
|
|
3041
|
+
const effectiveLibs = react.useMemo(() => ({
|
|
3042
|
+
...map.libraries ?? {},
|
|
3043
|
+
...persistedLibs
|
|
3044
|
+
}), [map.libraries, persistedLibs]);
|
|
2892
3045
|
const buildTypeDefs = react.useCallback(() => {
|
|
2893
|
-
const m = new Map(
|
|
2894
|
-
const
|
|
2895
|
-
|
|
3046
|
+
const m = /* @__PURE__ */ new Map();
|
|
3047
|
+
for (const cfg of effectiveConfigs) {
|
|
3048
|
+
for (const t of cfg.elementTypes) {
|
|
3049
|
+
if (!m.has(t.id)) m.set(t.id, t);
|
|
3050
|
+
}
|
|
3051
|
+
}
|
|
3052
|
+
for (const group of Object.values(effectiveLibs)) {
|
|
2896
3053
|
for (const t of group.objects) {
|
|
2897
3054
|
if (!m.has(t.id)) m.set(t.id, t);
|
|
2898
3055
|
}
|
|
2899
3056
|
}
|
|
2900
3057
|
return m;
|
|
2901
|
-
}, [
|
|
3058
|
+
}, [effectiveConfigs, effectiveLibs]);
|
|
2902
3059
|
const elementTypeDefs = react.useRef(buildTypeDefs());
|
|
2903
3060
|
react.useEffect(() => {
|
|
2904
3061
|
elementTypeDefs.current = buildTypeDefs();
|
|
2905
3062
|
}, [buildTypeDefs]);
|
|
2906
3063
|
const paletteGroups = react.useMemo(() => {
|
|
2907
3064
|
const groups = [];
|
|
2908
|
-
|
|
2909
|
-
|
|
3065
|
+
for (const cfg of effectiveConfigs) {
|
|
3066
|
+
if (cfg.elementTypes.length > 0) {
|
|
3067
|
+
groups.push({ id: cfg.id, name: cfg.name, types: cfg.elementTypes, isBase: true });
|
|
3068
|
+
}
|
|
2910
3069
|
}
|
|
2911
|
-
const
|
|
2912
|
-
for (const [gid, group] of Object.entries(libs)) {
|
|
3070
|
+
for (const [gid, group] of Object.entries(effectiveLibs)) {
|
|
2913
3071
|
groups.push({ id: gid, name: group.name, types: group.objects, isBase: false });
|
|
2914
3072
|
}
|
|
2915
3073
|
return groups;
|
|
2916
|
-
}, [
|
|
3074
|
+
}, [effectiveConfigs, effectiveLibs]);
|
|
2917
3075
|
react.useEffect(() => {
|
|
2918
3076
|
if (activePlaceTypeId) return;
|
|
2919
3077
|
const firstType = paletteGroups[0]?.types[0];
|
|
@@ -3053,22 +3211,27 @@ function VenueMapEditor({
|
|
|
3053
3211
|
reader.onload = (e) => {
|
|
3054
3212
|
try {
|
|
3055
3213
|
const parsed = JSON.parse(e.target?.result);
|
|
3056
|
-
const
|
|
3057
|
-
|
|
3214
|
+
const mergedPersisted = mergeLibraries(persistedLibs, parsed);
|
|
3215
|
+
setPersistedLibs(mergedPersisted);
|
|
3216
|
+
const mergedMap = mergeLibraries(map.libraries ?? {}, parsed);
|
|
3217
|
+
push({ ...map, libraries: mergedMap });
|
|
3058
3218
|
} catch {
|
|
3059
3219
|
}
|
|
3060
3220
|
};
|
|
3061
3221
|
reader.readAsText(file);
|
|
3062
3222
|
},
|
|
3063
|
-
[map, push]
|
|
3223
|
+
[map, push, persistedLibs, setPersistedLibs]
|
|
3064
3224
|
);
|
|
3065
3225
|
const handleRemoveLibraryGroup = react.useCallback(
|
|
3066
3226
|
(groupId) => {
|
|
3227
|
+
const newPersistedLibs = { ...persistedLibs };
|
|
3228
|
+
delete newPersistedLibs[groupId];
|
|
3229
|
+
setPersistedLibs(newPersistedLibs);
|
|
3067
3230
|
const libs = { ...map.libraries ?? {} };
|
|
3068
3231
|
delete libs[groupId];
|
|
3069
3232
|
push({ ...map, libraries: Object.keys(libs).length > 0 ? libs : void 0 });
|
|
3070
3233
|
},
|
|
3071
|
-
[map, push]
|
|
3234
|
+
[map, push, persistedLibs, setPersistedLibs]
|
|
3072
3235
|
);
|
|
3073
3236
|
const DEFAULT_WALL_THICKNESS = 8;
|
|
3074
3237
|
const handleAddWall = react.useCallback(
|
|
@@ -3514,6 +3677,398 @@ function VenueMapViewer({ elementStatus, onElementClick, ...rest }) {
|
|
|
3514
3677
|
}
|
|
3515
3678
|
);
|
|
3516
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
|
+
};
|
|
3517
4072
|
|
|
3518
4073
|
exports.AddIcon = AddIcon;
|
|
3519
4074
|
exports.Alerta = Alerta;
|
|
@@ -3549,6 +4104,7 @@ exports.CopyIcon = CopyIcon;
|
|
|
3549
4104
|
exports.DeleteIcon = DeleteIcon;
|
|
3550
4105
|
exports.DocumentIcon = DocumentIcon;
|
|
3551
4106
|
exports.EditIcon = EditIcon;
|
|
4107
|
+
exports.ElementLibraryBuilder = ElementLibraryBuilder;
|
|
3552
4108
|
exports.FacturacionIcon = FacturacionIcon;
|
|
3553
4109
|
exports.FilterIcon = FilterIcon;
|
|
3554
4110
|
exports.FolderIcon = FolderIcon;
|
|
@@ -3613,8 +4169,10 @@ exports.VenueMapViewer = VenueMapViewer;
|
|
|
3613
4169
|
exports.WhatsAppIcon = WhatsAppIcon;
|
|
3614
4170
|
exports.findNearestNode = findNearestNode;
|
|
3615
4171
|
exports.genId = genId;
|
|
4172
|
+
exports.parseSvgMarkup = parseSvgMarkup;
|
|
3616
4173
|
exports.snapPoint = snapPoint;
|
|
3617
4174
|
exports.snapToGrid = snapToGrid;
|
|
4175
|
+
exports.useLibraryStorage = useLibraryStorage;
|
|
3618
4176
|
exports.usePanZoom = usePanZoom;
|
|
3619
4177
|
exports.useTheme = useTheme;
|
|
3620
4178
|
//# sourceMappingURL=index.js.map
|