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
|
@@ -4,6 +4,33 @@ var react = require('react');
|
|
|
4
4
|
var jsxRuntime = require('react/jsx-runtime');
|
|
5
5
|
|
|
6
6
|
// src/components/VenueMapEditor/VenueMapEditor.tsx
|
|
7
|
+
function useLibraryStorage(storageKey) {
|
|
8
|
+
const [libs, setLibs] = react.useState(() => {
|
|
9
|
+
if (!storageKey) return {};
|
|
10
|
+
try {
|
|
11
|
+
const raw = localStorage.getItem(storageKey);
|
|
12
|
+
return raw ? JSON.parse(raw) : {};
|
|
13
|
+
} catch {
|
|
14
|
+
return {};
|
|
15
|
+
}
|
|
16
|
+
});
|
|
17
|
+
const setAndPersist = react.useCallback(
|
|
18
|
+
(newLibs) => {
|
|
19
|
+
setLibs(newLibs);
|
|
20
|
+
if (!storageKey) return;
|
|
21
|
+
try {
|
|
22
|
+
if (Object.keys(newLibs).length === 0) {
|
|
23
|
+
localStorage.removeItem(storageKey);
|
|
24
|
+
} else {
|
|
25
|
+
localStorage.setItem(storageKey, JSON.stringify(newLibs));
|
|
26
|
+
}
|
|
27
|
+
} catch {
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
[storageKey]
|
|
31
|
+
);
|
|
32
|
+
return [libs, setAndPersist];
|
|
33
|
+
}
|
|
7
34
|
function IconCursor({ className }) {
|
|
8
35
|
return /* @__PURE__ */ jsxRuntime.jsx("svg", { viewBox: "0 0 16 16", className, fill: "currentColor", children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M2 1l12 5.5-5.5 1.5L7 13.5 2 1z" }) });
|
|
9
36
|
}
|
|
@@ -66,6 +93,26 @@ function IconUpload({ className }) {
|
|
|
66
93
|
function IconLayers({ className }) {
|
|
67
94
|
return /* @__PURE__ */ jsxRuntime.jsx("svg", { viewBox: "0 0 16 16", className, fill: "currentColor", children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M8.235 1.559a.5.5 0 0 0-.47 0l-7.5 4a.5.5 0 0 0 0 .882L3.188 8 .265 9.559a.5.5 0 0 0 0 .882l7.5 4a.5.5 0 0 0 .47 0l7.5-4a.5.5 0 0 0 0-.882L12.813 8l2.922-1.559a.5.5 0 0 0 0-.882l-7.5-4zm3.515 7.008L14.438 10 8 13.433 1.562 10 4.25 8.567l3.515 1.874a.5.5 0 0 0 .47 0l3.515-1.874zM8 9.433 1.562 6 8 2.567 14.438 6 8 9.433z" }) });
|
|
68
95
|
}
|
|
96
|
+
|
|
97
|
+
// src/components/VenueMapEditor/utils/svgParser.ts
|
|
98
|
+
var DANGEROUS_TAGS = /\b(script|iframe|object|embed|link|style|meta)\b/gi;
|
|
99
|
+
var DANGEROUS_ATTRS = /\bon\w+\s*=/gi;
|
|
100
|
+
var DANGEROUS_HREF = /\bhref\s*=\s*["']?\s*javascript:/gi;
|
|
101
|
+
var DANGEROUS_XLINK = /\bxlink:href\s*=\s*["']?\s*javascript:/gi;
|
|
102
|
+
function sanitize(html) {
|
|
103
|
+
return html.replace(DANGEROUS_TAGS, "").replace(DANGEROUS_ATTRS, "").replace(DANGEROUS_HREF, "").replace(DANGEROUS_XLINK, "");
|
|
104
|
+
}
|
|
105
|
+
var VIEWBOX_RE = /viewBox\s*=\s*"([^"]+)"/i;
|
|
106
|
+
var SVG_OPEN_END_RE = /<svg[^>]*>/i;
|
|
107
|
+
function parseSvgMarkup(markup) {
|
|
108
|
+
const viewBoxMatch = markup.match(VIEWBOX_RE);
|
|
109
|
+
const viewBox = viewBoxMatch?.[1] ?? "0 0 100 100";
|
|
110
|
+
const svgOpenMatch = markup.match(SVG_OPEN_END_RE);
|
|
111
|
+
const afterOpen = svgOpenMatch ? markup.slice(svgOpenMatch.index + svgOpenMatch[0].length) : markup;
|
|
112
|
+
const closeIdx = afterOpen.lastIndexOf("</svg>");
|
|
113
|
+
const inner = closeIdx >= 0 ? afterOpen.slice(0, closeIdx) : afterOpen;
|
|
114
|
+
return { viewBox, innerHtml: sanitize(inner) };
|
|
115
|
+
}
|
|
69
116
|
function ToolButton({ active, disabled, title, onClick, children }) {
|
|
70
117
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
71
118
|
"button",
|
|
@@ -95,7 +142,15 @@ function TypeChip({ typeDef, active, onClick }) {
|
|
|
95
142
|
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"
|
|
96
143
|
].join(" "),
|
|
97
144
|
children: [
|
|
98
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
145
|
+
typeDef.shape === "svg" && typeDef.svgMarkup ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
146
|
+
"svg",
|
|
147
|
+
{
|
|
148
|
+
viewBox: parseSvgMarkup(typeDef.svgMarkup).viewBox,
|
|
149
|
+
className: "w-2.5 h-2.5 shrink-0",
|
|
150
|
+
style: { color: typeDef.strokeColor },
|
|
151
|
+
dangerouslySetInnerHTML: { __html: parseSvgMarkup(typeDef.svgMarkup).innerHtml }
|
|
152
|
+
}
|
|
153
|
+
) : /* @__PURE__ */ jsxRuntime.jsx(
|
|
99
154
|
"span",
|
|
100
155
|
{
|
|
101
156
|
className: "w-2.5 h-2.5 rounded-sm shrink-0",
|
|
@@ -130,6 +185,19 @@ function Toolbar({
|
|
|
130
185
|
onLoadLibrary,
|
|
131
186
|
onRemoveLibraryGroup
|
|
132
187
|
}) {
|
|
188
|
+
const [activeGroupId, setActiveGroupId] = react.useState(
|
|
189
|
+
() => paletteGroups[0]?.id ?? null
|
|
190
|
+
);
|
|
191
|
+
react.useEffect(() => {
|
|
192
|
+
if (paletteGroups.length === 0) {
|
|
193
|
+
setActiveGroupId(null);
|
|
194
|
+
return;
|
|
195
|
+
}
|
|
196
|
+
if (!paletteGroups.find((g) => g.id === activeGroupId)) {
|
|
197
|
+
setActiveGroupId(paletteGroups[0].id);
|
|
198
|
+
}
|
|
199
|
+
}, [paletteGroups, activeGroupId]);
|
|
200
|
+
const activeGroup = paletteGroups.find((g) => g.id === activeGroupId) ?? null;
|
|
133
201
|
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col bg-white border-b border-slate-200 shadow-sm shrink-0", children: [
|
|
134
202
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-0.5 px-2 py-1.5", children: [
|
|
135
203
|
/* @__PURE__ */ jsxRuntime.jsx(ToolButton, { title: "Seleccionar (V)", active: tool === "SELECT", onClick: () => onToolChange("SELECT"), children: /* @__PURE__ */ jsxRuntime.jsx(IconCursor, { className: "w-4 h-4" }) }),
|
|
@@ -167,21 +235,34 @@ function Toolbar({
|
|
|
167
235
|
/* @__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" }) })
|
|
168
236
|
] })
|
|
169
237
|
] }),
|
|
170
|
-
tool === "PLACE" && /* @__PURE__ */ jsxRuntime.
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
238
|
+
tool === "PLACE" && paletteGroups.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col border-t border-slate-100", children: [
|
|
239
|
+
/* @__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(
|
|
240
|
+
"button",
|
|
241
|
+
{
|
|
242
|
+
onClick: () => setActiveGroupId(group.id),
|
|
243
|
+
className: [
|
|
244
|
+
"flex items-center gap-1 px-3 py-1 text-xs font-medium rounded-t border-x border-t transition-colors whitespace-nowrap",
|
|
245
|
+
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"
|
|
246
|
+
].join(" "),
|
|
247
|
+
children: [
|
|
248
|
+
group.name || "Sin nombre",
|
|
249
|
+
!group.isBase && onRemoveLibraryGroup && /* @__PURE__ */ jsxRuntime.jsx(
|
|
250
|
+
"span",
|
|
251
|
+
{
|
|
252
|
+
role: "button",
|
|
253
|
+
title: `Eliminar "${group.name}"`,
|
|
254
|
+
onClick: (e) => {
|
|
255
|
+
e.stopPropagation();
|
|
256
|
+
onRemoveLibraryGroup(group.id);
|
|
257
|
+
},
|
|
258
|
+
className: "ml-0.5 text-slate-300 hover:text-red-400 transition-colors leading-none",
|
|
259
|
+
children: "\xD7"
|
|
260
|
+
}
|
|
261
|
+
)
|
|
262
|
+
]
|
|
263
|
+
}
|
|
264
|
+
) }, group.id)) }),
|
|
265
|
+
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(
|
|
185
266
|
TypeChip,
|
|
186
267
|
{
|
|
187
268
|
typeDef,
|
|
@@ -190,7 +271,7 @@ function Toolbar({
|
|
|
190
271
|
},
|
|
191
272
|
typeDef.id
|
|
192
273
|
)) })
|
|
193
|
-
] }
|
|
274
|
+
] })
|
|
194
275
|
] });
|
|
195
276
|
}
|
|
196
277
|
var ZOOM_MIN = 0.1;
|
|
@@ -1130,6 +1211,7 @@ function ElementNode({
|
|
|
1130
1211
|
{
|
|
1131
1212
|
d: typeDef.svgPath,
|
|
1132
1213
|
fill: fillColor,
|
|
1214
|
+
fillRule: typeDef.fillRule ?? "nonzero",
|
|
1133
1215
|
stroke: isSelected ? "#3b82f6" : typeDef.strokeColor,
|
|
1134
1216
|
strokeWidth: isSelected ? customPath.strokeWidth * 1.5 : customPath.strokeWidth,
|
|
1135
1217
|
style: { cursor: bodyCursor },
|
|
@@ -1137,6 +1219,28 @@ function ElementNode({
|
|
|
1137
1219
|
onClick: handleBodyClick
|
|
1138
1220
|
}
|
|
1139
1221
|
) }),
|
|
1222
|
+
typeDef.shape === "svg" && typeDef.svgMarkup && (() => {
|
|
1223
|
+
const parsed = parseSvgMarkup(typeDef.svgMarkup);
|
|
1224
|
+
const parts = parsed.viewBox.split(/[\s,]+/).map(Number);
|
|
1225
|
+
const vw = parts[2] ?? 100;
|
|
1226
|
+
const vh = parts[3] ?? 100;
|
|
1227
|
+
const sx = vw > 0 ? w / vw : 1;
|
|
1228
|
+
const sy = vh > 0 ? h / vh : 1;
|
|
1229
|
+
const avgScale = Math.sqrt(Math.abs(sx * sy)) || 1;
|
|
1230
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
1231
|
+
"g",
|
|
1232
|
+
{
|
|
1233
|
+
transform: `translate(${x}, ${y}) scale(${sx}, ${sy})`,
|
|
1234
|
+
fill: fillColor,
|
|
1235
|
+
stroke: isSelected ? "#3b82f6" : typeDef.strokeColor,
|
|
1236
|
+
strokeWidth: isSelected ? sw / avgScale * 1.5 : sw / avgScale,
|
|
1237
|
+
style: { cursor: bodyCursor },
|
|
1238
|
+
onMouseDown: tool === "SELECT" && !onViewerClick ? handleBodyDown : void 0,
|
|
1239
|
+
onClick: handleBodyClick,
|
|
1240
|
+
dangerouslySetInnerHTML: { __html: parsed.innerHtml }
|
|
1241
|
+
}
|
|
1242
|
+
);
|
|
1243
|
+
})(),
|
|
1140
1244
|
(element.label ?? typeDef.label) && /* @__PURE__ */ jsxRuntime.jsx(
|
|
1141
1245
|
"text",
|
|
1142
1246
|
{
|
|
@@ -1635,14 +1739,35 @@ function PropertiesPanel({
|
|
|
1635
1739
|
/* @__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" }),
|
|
1636
1740
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-1 flex flex-col gap-4 p-3", children: [
|
|
1637
1741
|
typeDef && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
|
|
1638
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1742
|
+
typeDef.shape === "svg" ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
1743
|
+
"svg",
|
|
1744
|
+
{
|
|
1745
|
+
viewBox: (() => {
|
|
1746
|
+
try {
|
|
1747
|
+
return typeDef.svgMarkup ? parseSvgMarkup(typeDef.svgMarkup).viewBox : "0 0 100 100";
|
|
1748
|
+
} catch {
|
|
1749
|
+
return "0 0 100 100";
|
|
1750
|
+
}
|
|
1751
|
+
})(),
|
|
1752
|
+
className: "w-3.5 h-3.5 shrink-0 border border-slate-300 rounded-sm",
|
|
1753
|
+
style: { color: typeDef.strokeColor },
|
|
1754
|
+
dangerouslySetInnerHTML: { __html: (() => {
|
|
1755
|
+
try {
|
|
1756
|
+
return typeDef.svgMarkup ? parseSvgMarkup(typeDef.svgMarkup).innerHtml : "";
|
|
1757
|
+
} catch {
|
|
1758
|
+
return "";
|
|
1759
|
+
}
|
|
1760
|
+
})() }
|
|
1761
|
+
}
|
|
1762
|
+
) : /* @__PURE__ */ jsxRuntime.jsx(
|
|
1639
1763
|
"span",
|
|
1640
1764
|
{
|
|
1641
1765
|
className: "w-3.5 h-3.5 rounded-sm shrink-0 border",
|
|
1642
1766
|
style: { background: typeDef.color, borderColor: typeDef.strokeColor }
|
|
1643
1767
|
}
|
|
1644
1768
|
),
|
|
1645
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs font-medium text-slate-700 truncate", children: typeDef.label })
|
|
1769
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs font-medium text-slate-700 truncate", children: typeDef.label }),
|
|
1770
|
+
typeDef.shape === "svg" && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[9px] uppercase tracking-wide text-slate-400 font-medium ml-auto", children: "SVG" })
|
|
1646
1771
|
] }),
|
|
1647
1772
|
/* @__PURE__ */ jsxRuntime.jsxs("label", { className: "flex flex-col gap-0.5", children: [
|
|
1648
1773
|
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[10px] font-medium text-slate-400 uppercase tracking-wide", children: "Etiqueta" }),
|
|
@@ -1977,7 +2102,23 @@ function createDefaultMap() {
|
|
|
1977
2102
|
]
|
|
1978
2103
|
};
|
|
1979
2104
|
}
|
|
1980
|
-
var
|
|
2105
|
+
var DEFAULT_LIBRARY_KEY = "venueMapEditor:libraries";
|
|
2106
|
+
function mergeLibraries(existing, incoming) {
|
|
2107
|
+
const result = { ...existing };
|
|
2108
|
+
for (const [groupId, incomingGroup] of Object.entries(incoming)) {
|
|
2109
|
+
if (result[groupId]) {
|
|
2110
|
+
const existingIds = new Set(result[groupId].objects.map((o) => o.id));
|
|
2111
|
+
const newObjects = incomingGroup.objects.filter((o) => !existingIds.has(o.id));
|
|
2112
|
+
result[groupId] = {
|
|
2113
|
+
...result[groupId],
|
|
2114
|
+
objects: [...result[groupId].objects, ...newObjects]
|
|
2115
|
+
};
|
|
2116
|
+
} else {
|
|
2117
|
+
result[groupId] = incomingGroup;
|
|
2118
|
+
}
|
|
2119
|
+
}
|
|
2120
|
+
return result;
|
|
2121
|
+
}
|
|
1981
2122
|
function updateFloor(map, updatedFloor) {
|
|
1982
2123
|
return {
|
|
1983
2124
|
...map,
|
|
@@ -2017,7 +2158,9 @@ function polygonToRect(area) {
|
|
|
2017
2158
|
};
|
|
2018
2159
|
}
|
|
2019
2160
|
function VenueMapEditor({
|
|
2020
|
-
|
|
2161
|
+
domainConfigs,
|
|
2162
|
+
domainConfig,
|
|
2163
|
+
libraryStorageKey = DEFAULT_LIBRARY_KEY,
|
|
2021
2164
|
initialMap,
|
|
2022
2165
|
onChange,
|
|
2023
2166
|
width = "100%",
|
|
@@ -2031,7 +2174,13 @@ function VenueMapEditor({
|
|
|
2031
2174
|
onElementClick,
|
|
2032
2175
|
onElementTypeClick
|
|
2033
2176
|
}) {
|
|
2177
|
+
const effectiveConfigs = react.useMemo(() => {
|
|
2178
|
+
if (domainConfigs && domainConfigs.length > 0) return domainConfigs;
|
|
2179
|
+
if (domainConfig) return [domainConfig];
|
|
2180
|
+
return [];
|
|
2181
|
+
}, [domainConfigs, domainConfig]);
|
|
2034
2182
|
const initialMapRef = react.useRef(initialMap ?? createDefaultMap());
|
|
2183
|
+
const [persistedLibs, setPersistedLibs] = useLibraryStorage(libraryStorageKey);
|
|
2035
2184
|
const { map, canUndo, canRedo, push, replace, undo, redo } = useHistory(
|
|
2036
2185
|
initialMapRef.current
|
|
2037
2186
|
);
|
|
@@ -2047,31 +2196,40 @@ function VenueMapEditor({
|
|
|
2047
2196
|
const resetViewRef = react.useRef(() => void 0);
|
|
2048
2197
|
const importInputRef = react.useRef(null);
|
|
2049
2198
|
const libraryInputRef = react.useRef(null);
|
|
2199
|
+
const effectiveLibs = react.useMemo(() => ({
|
|
2200
|
+
...map.libraries ?? {},
|
|
2201
|
+
...persistedLibs
|
|
2202
|
+
}), [map.libraries, persistedLibs]);
|
|
2050
2203
|
const buildTypeDefs = react.useCallback(() => {
|
|
2051
|
-
const m = new Map(
|
|
2052
|
-
const
|
|
2053
|
-
|
|
2204
|
+
const m = /* @__PURE__ */ new Map();
|
|
2205
|
+
for (const cfg of effectiveConfigs) {
|
|
2206
|
+
for (const t of cfg.elementTypes) {
|
|
2207
|
+
if (!m.has(t.id)) m.set(t.id, t);
|
|
2208
|
+
}
|
|
2209
|
+
}
|
|
2210
|
+
for (const group of Object.values(effectiveLibs)) {
|
|
2054
2211
|
for (const t of group.objects) {
|
|
2055
2212
|
if (!m.has(t.id)) m.set(t.id, t);
|
|
2056
2213
|
}
|
|
2057
2214
|
}
|
|
2058
2215
|
return m;
|
|
2059
|
-
}, [
|
|
2216
|
+
}, [effectiveConfigs, effectiveLibs]);
|
|
2060
2217
|
const elementTypeDefs = react.useRef(buildTypeDefs());
|
|
2061
2218
|
react.useEffect(() => {
|
|
2062
2219
|
elementTypeDefs.current = buildTypeDefs();
|
|
2063
2220
|
}, [buildTypeDefs]);
|
|
2064
2221
|
const paletteGroups = react.useMemo(() => {
|
|
2065
2222
|
const groups = [];
|
|
2066
|
-
|
|
2067
|
-
|
|
2223
|
+
for (const cfg of effectiveConfigs) {
|
|
2224
|
+
if (cfg.elementTypes.length > 0) {
|
|
2225
|
+
groups.push({ id: cfg.id, name: cfg.name, types: cfg.elementTypes, isBase: true });
|
|
2226
|
+
}
|
|
2068
2227
|
}
|
|
2069
|
-
const
|
|
2070
|
-
for (const [gid, group] of Object.entries(libs)) {
|
|
2228
|
+
for (const [gid, group] of Object.entries(effectiveLibs)) {
|
|
2071
2229
|
groups.push({ id: gid, name: group.name, types: group.objects, isBase: false });
|
|
2072
2230
|
}
|
|
2073
2231
|
return groups;
|
|
2074
|
-
}, [
|
|
2232
|
+
}, [effectiveConfigs, effectiveLibs]);
|
|
2075
2233
|
react.useEffect(() => {
|
|
2076
2234
|
if (activePlaceTypeId) return;
|
|
2077
2235
|
const firstType = paletteGroups[0]?.types[0];
|
|
@@ -2211,22 +2369,27 @@ function VenueMapEditor({
|
|
|
2211
2369
|
reader.onload = (e) => {
|
|
2212
2370
|
try {
|
|
2213
2371
|
const parsed = JSON.parse(e.target?.result);
|
|
2214
|
-
const
|
|
2215
|
-
|
|
2372
|
+
const mergedPersisted = mergeLibraries(persistedLibs, parsed);
|
|
2373
|
+
setPersistedLibs(mergedPersisted);
|
|
2374
|
+
const mergedMap = mergeLibraries(map.libraries ?? {}, parsed);
|
|
2375
|
+
push({ ...map, libraries: mergedMap });
|
|
2216
2376
|
} catch {
|
|
2217
2377
|
}
|
|
2218
2378
|
};
|
|
2219
2379
|
reader.readAsText(file);
|
|
2220
2380
|
},
|
|
2221
|
-
[map, push]
|
|
2381
|
+
[map, push, persistedLibs, setPersistedLibs]
|
|
2222
2382
|
);
|
|
2223
2383
|
const handleRemoveLibraryGroup = react.useCallback(
|
|
2224
2384
|
(groupId) => {
|
|
2385
|
+
const newPersistedLibs = { ...persistedLibs };
|
|
2386
|
+
delete newPersistedLibs[groupId];
|
|
2387
|
+
setPersistedLibs(newPersistedLibs);
|
|
2225
2388
|
const libs = { ...map.libraries ?? {} };
|
|
2226
2389
|
delete libs[groupId];
|
|
2227
2390
|
push({ ...map, libraries: Object.keys(libs).length > 0 ? libs : void 0 });
|
|
2228
2391
|
},
|
|
2229
|
-
[map, push]
|
|
2392
|
+
[map, push, persistedLibs, setPersistedLibs]
|
|
2230
2393
|
);
|
|
2231
2394
|
const DEFAULT_WALL_THICKNESS = 8;
|
|
2232
2395
|
const handleAddWall = react.useCallback(
|
|
@@ -2677,8 +2840,10 @@ exports.VenueMapEditor = VenueMapEditor;
|
|
|
2677
2840
|
exports.VenueMapViewer = VenueMapViewer;
|
|
2678
2841
|
exports.findNearestNode = findNearestNode;
|
|
2679
2842
|
exports.genId = genId;
|
|
2843
|
+
exports.parseSvgMarkup = parseSvgMarkup;
|
|
2680
2844
|
exports.snapPoint = snapPoint;
|
|
2681
2845
|
exports.snapToGrid = snapToGrid;
|
|
2846
|
+
exports.useLibraryStorage = useLibraryStorage;
|
|
2682
2847
|
exports.usePanZoom = usePanZoom;
|
|
2683
2848
|
//# sourceMappingURL=index.js.map
|
|
2684
2849
|
//# sourceMappingURL=index.js.map
|