neogestify-ui-components 2.0.1 → 2.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (31) hide show
  1. package/README.md +153 -19
  2. package/dist/components/ElementLibraryBuilder/index.d.mts +5 -0
  3. package/dist/components/ElementLibraryBuilder/index.d.ts +5 -0
  4. package/dist/components/ElementLibraryBuilder/index.js +689 -0
  5. package/dist/components/ElementLibraryBuilder/index.js.map +1 -0
  6. package/dist/components/ElementLibraryBuilder/index.mjs +687 -0
  7. package/dist/components/ElementLibraryBuilder/index.mjs.map +1 -0
  8. package/dist/components/VenueMapEditor/index.d.mts +66 -5
  9. package/dist/components/VenueMapEditor/index.d.ts +66 -5
  10. package/dist/components/VenueMapEditor/index.js +199 -34
  11. package/dist/components/VenueMapEditor/index.js.map +1 -1
  12. package/dist/components/VenueMapEditor/index.mjs +199 -36
  13. package/dist/components/VenueMapEditor/index.mjs.map +1 -1
  14. package/dist/index.d.mts +2 -1
  15. package/dist/index.d.ts +2 -1
  16. package/dist/index.js +592 -34
  17. package/dist/index.js.map +1 -1
  18. package/dist/index.mjs +591 -36
  19. package/dist/index.mjs.map +1 -1
  20. package/package.json +1 -1
  21. package/src/components/ElementLibraryBuilder/builder.tsx +400 -0
  22. package/src/components/ElementLibraryBuilder/index.ts +1 -0
  23. package/src/components/VenueMapEditor/VenueMapEditor.tsx +79 -20
  24. package/src/components/VenueMapEditor/components/ElementNode.tsx +23 -0
  25. package/src/components/VenueMapEditor/components/PropertiesPanel.tsx +17 -4
  26. package/src/components/VenueMapEditor/components/Toolbar.tsx +73 -39
  27. package/src/components/VenueMapEditor/hooks/useLibraryStorage.ts +46 -0
  28. package/src/components/VenueMapEditor/index.ts +3 -0
  29. package/src/components/VenueMapEditor/types.ts +45 -3
  30. package/src/components/VenueMapEditor/utils/svgParser.ts +33 -0
  31. package/src/index.ts +1 -0
@@ -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.jsx("div", { className: "flex items-stretch gap-0 border-t border-slate-100 bg-slate-50 overflow-x-auto", children: paletteGroups.map((group, gi) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center shrink-0", children: [
171
- gi > 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-px self-stretch bg-slate-200 mx-1" }),
172
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-0.5 px-1.5 shrink-0", children: [
173
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[10px] text-slate-400 font-medium whitespace-nowrap select-none", children: group.name }),
174
- !group.isBase && onRemoveLibraryGroup && /* @__PURE__ */ jsxRuntime.jsx(
175
- "button",
176
- {
177
- title: `Eliminar grupo "${group.name}"`,
178
- onClick: () => onRemoveLibraryGroup(group.id),
179
- className: "text-slate-300 hover:text-red-400 leading-none text-xs ml-0.5 transition-colors",
180
- children: "\xD7"
181
- }
182
- )
183
- ] }),
184
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center gap-1 px-1 py-1.5", children: group.types.map((typeDef) => /* @__PURE__ */ jsxRuntime.jsx(
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
- ] }, group.id)) })
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 EMPTY_DOMAIN_CONFIG = { id: "__empty__", name: "", elementTypes: [] };
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
- domainConfig = EMPTY_DOMAIN_CONFIG,
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(domainConfig.elementTypes.map((t) => [t.id, t]));
2052
- const libs = map.libraries ?? {};
2053
- for (const group of Object.values(libs)) {
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
- }, [domainConfig, map.libraries]);
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
- if (domainConfig.elementTypes.length > 0) {
2067
- groups.push({ id: domainConfig.id, name: domainConfig.name, types: domainConfig.elementTypes, isBase: true });
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 libs = map.libraries ?? {};
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
- }, [domainConfig, map.libraries]);
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 merged = { ...map.libraries ?? {}, ...parsed };
2215
- push({ ...map, libraries: merged });
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