neogestify-ui-components 2.0.1 → 2.1.0
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 +108 -19
- package/dist/components/VenueMapEditor/index.d.mts +49 -4
- package/dist/components/VenueMapEditor/index.d.ts +49 -4
- package/dist/components/VenueMapEditor/index.js +124 -31
- package/dist/components/VenueMapEditor/index.js.map +1 -1
- package/dist/components/VenueMapEditor/index.mjs +125 -33
- package/dist/components/VenueMapEditor/index.mjs.map +1 -1
- package/dist/index.d.mts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +124 -31
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +125 -33
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
- package/src/components/VenueMapEditor/VenueMapEditor.tsx +79 -20
- package/src/components/VenueMapEditor/components/ElementNode.tsx +1 -0
- package/src/components/VenueMapEditor/components/Toolbar.tsx +59 -35
- package/src/components/VenueMapEditor/hooks/useLibraryStorage.ts +46 -0
- package/src/components/VenueMapEditor/index.ts +1 -0
- package/src/components/VenueMapEditor/types.ts +34 -2
|
@@ -1,7 +1,34 @@
|
|
|
1
|
-
import { useState, useRef,
|
|
1
|
+
import { useState, useCallback, useRef, useMemo, useEffect } from 'react';
|
|
2
2
|
import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
|
|
3
3
|
|
|
4
4
|
// src/components/VenueMapEditor/VenueMapEditor.tsx
|
|
5
|
+
function useLibraryStorage(storageKey) {
|
|
6
|
+
const [libs, setLibs] = useState(() => {
|
|
7
|
+
if (!storageKey) return {};
|
|
8
|
+
try {
|
|
9
|
+
const raw = localStorage.getItem(storageKey);
|
|
10
|
+
return raw ? JSON.parse(raw) : {};
|
|
11
|
+
} catch {
|
|
12
|
+
return {};
|
|
13
|
+
}
|
|
14
|
+
});
|
|
15
|
+
const setAndPersist = useCallback(
|
|
16
|
+
(newLibs) => {
|
|
17
|
+
setLibs(newLibs);
|
|
18
|
+
if (!storageKey) return;
|
|
19
|
+
try {
|
|
20
|
+
if (Object.keys(newLibs).length === 0) {
|
|
21
|
+
localStorage.removeItem(storageKey);
|
|
22
|
+
} else {
|
|
23
|
+
localStorage.setItem(storageKey, JSON.stringify(newLibs));
|
|
24
|
+
}
|
|
25
|
+
} catch {
|
|
26
|
+
}
|
|
27
|
+
},
|
|
28
|
+
[storageKey]
|
|
29
|
+
);
|
|
30
|
+
return [libs, setAndPersist];
|
|
31
|
+
}
|
|
5
32
|
function IconCursor({ className }) {
|
|
6
33
|
return /* @__PURE__ */ jsx("svg", { viewBox: "0 0 16 16", className, fill: "currentColor", children: /* @__PURE__ */ jsx("path", { d: "M2 1l12 5.5-5.5 1.5L7 13.5 2 1z" }) });
|
|
7
34
|
}
|
|
@@ -128,6 +155,19 @@ function Toolbar({
|
|
|
128
155
|
onLoadLibrary,
|
|
129
156
|
onRemoveLibraryGroup
|
|
130
157
|
}) {
|
|
158
|
+
const [activeGroupId, setActiveGroupId] = useState(
|
|
159
|
+
() => paletteGroups[0]?.id ?? null
|
|
160
|
+
);
|
|
161
|
+
useEffect(() => {
|
|
162
|
+
if (paletteGroups.length === 0) {
|
|
163
|
+
setActiveGroupId(null);
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
if (!paletteGroups.find((g) => g.id === activeGroupId)) {
|
|
167
|
+
setActiveGroupId(paletteGroups[0].id);
|
|
168
|
+
}
|
|
169
|
+
}, [paletteGroups, activeGroupId]);
|
|
170
|
+
const activeGroup = paletteGroups.find((g) => g.id === activeGroupId) ?? null;
|
|
131
171
|
return /* @__PURE__ */ jsxs("div", { className: "flex flex-col bg-white border-b border-slate-200 shadow-sm shrink-0", children: [
|
|
132
172
|
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-0.5 px-2 py-1.5", children: [
|
|
133
173
|
/* @__PURE__ */ jsx(ToolButton, { title: "Seleccionar (V)", active: tool === "SELECT", onClick: () => onToolChange("SELECT"), children: /* @__PURE__ */ jsx(IconCursor, { className: "w-4 h-4" }) }),
|
|
@@ -165,21 +205,34 @@ function Toolbar({
|
|
|
165
205
|
/* @__PURE__ */ jsx(ToolButton, { title: areaShape === "polygon" ? "Cambiar a rect\xE1ngulo" : "Cambiar a pol\xEDgono", onClick: () => onToggleAreaShape?.(), children: /* @__PURE__ */ jsx("span", { className: "text-xs font-medium", children: areaShape === "polygon" ? "Poly" : "Rect" }) })
|
|
166
206
|
] })
|
|
167
207
|
] }),
|
|
168
|
-
tool === "PLACE" && /* @__PURE__ */
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
208
|
+
tool === "PLACE" && paletteGroups.length > 0 && /* @__PURE__ */ jsxs("div", { className: "flex flex-col border-t border-slate-100", children: [
|
|
209
|
+
/* @__PURE__ */ 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__ */ jsx("div", { className: "flex items-center shrink-0", children: /* @__PURE__ */ jsxs(
|
|
210
|
+
"button",
|
|
211
|
+
{
|
|
212
|
+
onClick: () => setActiveGroupId(group.id),
|
|
213
|
+
className: [
|
|
214
|
+
"flex items-center gap-1 px-3 py-1 text-xs font-medium rounded-t border-x border-t transition-colors whitespace-nowrap",
|
|
215
|
+
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"
|
|
216
|
+
].join(" "),
|
|
217
|
+
children: [
|
|
218
|
+
group.name || "Sin nombre",
|
|
219
|
+
!group.isBase && onRemoveLibraryGroup && /* @__PURE__ */ jsx(
|
|
220
|
+
"span",
|
|
221
|
+
{
|
|
222
|
+
role: "button",
|
|
223
|
+
title: `Eliminar "${group.name}"`,
|
|
224
|
+
onClick: (e) => {
|
|
225
|
+
e.stopPropagation();
|
|
226
|
+
onRemoveLibraryGroup(group.id);
|
|
227
|
+
},
|
|
228
|
+
className: "ml-0.5 text-slate-300 hover:text-red-400 transition-colors leading-none",
|
|
229
|
+
children: "\xD7"
|
|
230
|
+
}
|
|
231
|
+
)
|
|
232
|
+
]
|
|
233
|
+
}
|
|
234
|
+
) }, group.id)) }),
|
|
235
|
+
activeGroup && /* @__PURE__ */ 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__ */ jsx(
|
|
183
236
|
TypeChip,
|
|
184
237
|
{
|
|
185
238
|
typeDef,
|
|
@@ -188,7 +241,7 @@ function Toolbar({
|
|
|
188
241
|
},
|
|
189
242
|
typeDef.id
|
|
190
243
|
)) })
|
|
191
|
-
] }
|
|
244
|
+
] })
|
|
192
245
|
] });
|
|
193
246
|
}
|
|
194
247
|
var ZOOM_MIN = 0.1;
|
|
@@ -1128,6 +1181,7 @@ function ElementNode({
|
|
|
1128
1181
|
{
|
|
1129
1182
|
d: typeDef.svgPath,
|
|
1130
1183
|
fill: fillColor,
|
|
1184
|
+
fillRule: typeDef.fillRule ?? "nonzero",
|
|
1131
1185
|
stroke: isSelected ? "#3b82f6" : typeDef.strokeColor,
|
|
1132
1186
|
strokeWidth: isSelected ? customPath.strokeWidth * 1.5 : customPath.strokeWidth,
|
|
1133
1187
|
style: { cursor: bodyCursor },
|
|
@@ -1975,7 +2029,23 @@ function createDefaultMap() {
|
|
|
1975
2029
|
]
|
|
1976
2030
|
};
|
|
1977
2031
|
}
|
|
1978
|
-
var
|
|
2032
|
+
var DEFAULT_LIBRARY_KEY = "venueMapEditor:libraries";
|
|
2033
|
+
function mergeLibraries(existing, incoming) {
|
|
2034
|
+
const result = { ...existing };
|
|
2035
|
+
for (const [groupId, incomingGroup] of Object.entries(incoming)) {
|
|
2036
|
+
if (result[groupId]) {
|
|
2037
|
+
const existingIds = new Set(result[groupId].objects.map((o) => o.id));
|
|
2038
|
+
const newObjects = incomingGroup.objects.filter((o) => !existingIds.has(o.id));
|
|
2039
|
+
result[groupId] = {
|
|
2040
|
+
...result[groupId],
|
|
2041
|
+
objects: [...result[groupId].objects, ...newObjects]
|
|
2042
|
+
};
|
|
2043
|
+
} else {
|
|
2044
|
+
result[groupId] = incomingGroup;
|
|
2045
|
+
}
|
|
2046
|
+
}
|
|
2047
|
+
return result;
|
|
2048
|
+
}
|
|
1979
2049
|
function updateFloor(map, updatedFloor) {
|
|
1980
2050
|
return {
|
|
1981
2051
|
...map,
|
|
@@ -2015,7 +2085,9 @@ function polygonToRect(area) {
|
|
|
2015
2085
|
};
|
|
2016
2086
|
}
|
|
2017
2087
|
function VenueMapEditor({
|
|
2018
|
-
|
|
2088
|
+
domainConfigs,
|
|
2089
|
+
domainConfig,
|
|
2090
|
+
libraryStorageKey = DEFAULT_LIBRARY_KEY,
|
|
2019
2091
|
initialMap,
|
|
2020
2092
|
onChange,
|
|
2021
2093
|
width = "100%",
|
|
@@ -2029,7 +2101,13 @@ function VenueMapEditor({
|
|
|
2029
2101
|
onElementClick,
|
|
2030
2102
|
onElementTypeClick
|
|
2031
2103
|
}) {
|
|
2104
|
+
const effectiveConfigs = useMemo(() => {
|
|
2105
|
+
if (domainConfigs && domainConfigs.length > 0) return domainConfigs;
|
|
2106
|
+
if (domainConfig) return [domainConfig];
|
|
2107
|
+
return [];
|
|
2108
|
+
}, [domainConfigs, domainConfig]);
|
|
2032
2109
|
const initialMapRef = useRef(initialMap ?? createDefaultMap());
|
|
2110
|
+
const [persistedLibs, setPersistedLibs] = useLibraryStorage(libraryStorageKey);
|
|
2033
2111
|
const { map, canUndo, canRedo, push, replace, undo, redo } = useHistory(
|
|
2034
2112
|
initialMapRef.current
|
|
2035
2113
|
);
|
|
@@ -2045,31 +2123,40 @@ function VenueMapEditor({
|
|
|
2045
2123
|
const resetViewRef = useRef(() => void 0);
|
|
2046
2124
|
const importInputRef = useRef(null);
|
|
2047
2125
|
const libraryInputRef = useRef(null);
|
|
2126
|
+
const effectiveLibs = useMemo(() => ({
|
|
2127
|
+
...map.libraries ?? {},
|
|
2128
|
+
...persistedLibs
|
|
2129
|
+
}), [map.libraries, persistedLibs]);
|
|
2048
2130
|
const buildTypeDefs = useCallback(() => {
|
|
2049
|
-
const m = new Map(
|
|
2050
|
-
const
|
|
2051
|
-
|
|
2131
|
+
const m = /* @__PURE__ */ new Map();
|
|
2132
|
+
for (const cfg of effectiveConfigs) {
|
|
2133
|
+
for (const t of cfg.elementTypes) {
|
|
2134
|
+
if (!m.has(t.id)) m.set(t.id, t);
|
|
2135
|
+
}
|
|
2136
|
+
}
|
|
2137
|
+
for (const group of Object.values(effectiveLibs)) {
|
|
2052
2138
|
for (const t of group.objects) {
|
|
2053
2139
|
if (!m.has(t.id)) m.set(t.id, t);
|
|
2054
2140
|
}
|
|
2055
2141
|
}
|
|
2056
2142
|
return m;
|
|
2057
|
-
}, [
|
|
2143
|
+
}, [effectiveConfigs, effectiveLibs]);
|
|
2058
2144
|
const elementTypeDefs = useRef(buildTypeDefs());
|
|
2059
2145
|
useEffect(() => {
|
|
2060
2146
|
elementTypeDefs.current = buildTypeDefs();
|
|
2061
2147
|
}, [buildTypeDefs]);
|
|
2062
2148
|
const paletteGroups = useMemo(() => {
|
|
2063
2149
|
const groups = [];
|
|
2064
|
-
|
|
2065
|
-
|
|
2150
|
+
for (const cfg of effectiveConfigs) {
|
|
2151
|
+
if (cfg.elementTypes.length > 0) {
|
|
2152
|
+
groups.push({ id: cfg.id, name: cfg.name, types: cfg.elementTypes, isBase: true });
|
|
2153
|
+
}
|
|
2066
2154
|
}
|
|
2067
|
-
const
|
|
2068
|
-
for (const [gid, group] of Object.entries(libs)) {
|
|
2155
|
+
for (const [gid, group] of Object.entries(effectiveLibs)) {
|
|
2069
2156
|
groups.push({ id: gid, name: group.name, types: group.objects, isBase: false });
|
|
2070
2157
|
}
|
|
2071
2158
|
return groups;
|
|
2072
|
-
}, [
|
|
2159
|
+
}, [effectiveConfigs, effectiveLibs]);
|
|
2073
2160
|
useEffect(() => {
|
|
2074
2161
|
if (activePlaceTypeId) return;
|
|
2075
2162
|
const firstType = paletteGroups[0]?.types[0];
|
|
@@ -2209,22 +2296,27 @@ function VenueMapEditor({
|
|
|
2209
2296
|
reader.onload = (e) => {
|
|
2210
2297
|
try {
|
|
2211
2298
|
const parsed = JSON.parse(e.target?.result);
|
|
2212
|
-
const
|
|
2213
|
-
|
|
2299
|
+
const mergedPersisted = mergeLibraries(persistedLibs, parsed);
|
|
2300
|
+
setPersistedLibs(mergedPersisted);
|
|
2301
|
+
const mergedMap = mergeLibraries(map.libraries ?? {}, parsed);
|
|
2302
|
+
push({ ...map, libraries: mergedMap });
|
|
2214
2303
|
} catch {
|
|
2215
2304
|
}
|
|
2216
2305
|
};
|
|
2217
2306
|
reader.readAsText(file);
|
|
2218
2307
|
},
|
|
2219
|
-
[map, push]
|
|
2308
|
+
[map, push, persistedLibs, setPersistedLibs]
|
|
2220
2309
|
);
|
|
2221
2310
|
const handleRemoveLibraryGroup = useCallback(
|
|
2222
2311
|
(groupId) => {
|
|
2312
|
+
const newPersistedLibs = { ...persistedLibs };
|
|
2313
|
+
delete newPersistedLibs[groupId];
|
|
2314
|
+
setPersistedLibs(newPersistedLibs);
|
|
2223
2315
|
const libs = { ...map.libraries ?? {} };
|
|
2224
2316
|
delete libs[groupId];
|
|
2225
2317
|
push({ ...map, libraries: Object.keys(libs).length > 0 ? libs : void 0 });
|
|
2226
2318
|
},
|
|
2227
|
-
[map, push]
|
|
2319
|
+
[map, push, persistedLibs, setPersistedLibs]
|
|
2228
2320
|
);
|
|
2229
2321
|
const DEFAULT_WALL_THICKNESS = 8;
|
|
2230
2322
|
const handleAddWall = useCallback(
|
|
@@ -2671,6 +2763,6 @@ function VenueMapViewer({ elementStatus, onElementClick, ...rest }) {
|
|
|
2671
2763
|
);
|
|
2672
2764
|
}
|
|
2673
2765
|
|
|
2674
|
-
export { VenueMapEditor, VenueMapViewer, findNearestNode, genId, snapPoint, snapToGrid, usePanZoom };
|
|
2766
|
+
export { VenueMapEditor, VenueMapViewer, findNearestNode, genId, snapPoint, snapToGrid, useLibraryStorage, usePanZoom };
|
|
2675
2767
|
//# sourceMappingURL=index.mjs.map
|
|
2676
2768
|
//# sourceMappingURL=index.mjs.map
|