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
package/dist/index.d.mts
CHANGED
|
@@ -2,7 +2,7 @@ export { Button, Form, Input, Loading, Modal, ModalRef, Select, Table, TextArea
|
|
|
2
2
|
export { AddIcon, AnimateSpin, ArchiveIcon, ArrowIcon, ArrowLeftIcon, ArrowRightIcon, BackIcon, BarsChartsIcon, BoxIcon, BuildingIcon, CajasIcon, CalendarIcon, CamaraIcon, CancelIcon, CashIcon, CategorieIcon, ChartIcon, CheckCircleIcon, CheckIcon, ClockIcon, CloseIcon, CloudIcon, CopyIcon, DeleteIcon, DocumentIcon, EditIcon, FacturacionIcon, FilterIcon, FolderIcon, GearIcon, HomeIcon, IconCursor, IconDownload, IconDuplicate, IconErase, IconGrid, IconHand, IconLayers, IconPlace, IconPolygon, IconRedo, IconReset, IconUndo, IconUpload, IconWall, IconZoomIn, IconZoomOut, InfoIcon, LifeGuardIcon, LightingIcon, LocationIcon, LogoutIcon, MenuIcon, MinusIcon, MoneyIcon, MonitorIcon, MoonIcon, NetworkIcon, NotFoundIcon, PasteIcon, PercentIcon, PrinterIcon, QuestionIcon, RestaurantMenuIcon, SaveIcon, SearchIcon, ShieldIcon, SpinnerIcon, StackIcon, SunIcon, TestIcon, TrashIcon, TruckIcon, UsersIcon, WhatsAppIcon } from './components/icons/index.mjs';
|
|
3
3
|
export { Alerta, AlertaAdvertencia, AlertaConfirmacion, AlertaError, AlertaExito, AlertaInfo, AlertaToast, InfoAlert } from './components/alerts/index.mjs';
|
|
4
4
|
export { Theme, ThemeContext, ThemeContextType, ThemeProvider, ThemeToggle, useTheme } from './context/theme/index.mjs';
|
|
5
|
-
export { AreaShape, DomainConfig, ElementGroup, ElementLibrary, ElementShape, ElementStatus, ElementTypeDef, Floor, FloorArea, MapElement, PaletteGroup, PanZoomState, ToolMode, VenueMap, VenueMapEditor, VenueMapEditorProps, VenueMapViewer, VenueMapViewerProps, Wall, WallMaterial, WallNode, findNearestNode, genId, snapPoint, snapToGrid, usePanZoom } from './components/VenueMapEditor/index.mjs';
|
|
5
|
+
export { AreaShape, DomainConfig, ElementGroup, ElementLibrary, ElementShape, ElementStatus, ElementTypeDef, Floor, FloorArea, MapElement, PaletteGroup, PanZoomState, ToolMode, VenueMap, VenueMapEditor, VenueMapEditorProps, VenueMapViewer, VenueMapViewerProps, Wall, WallMaterial, WallNode, findNearestNode, genId, snapPoint, snapToGrid, useLibraryStorage, usePanZoom } from './components/VenueMapEditor/index.mjs';
|
|
6
6
|
import 'react';
|
|
7
7
|
import 'react/jsx-runtime';
|
|
8
8
|
import 'sweetalert2';
|
package/dist/index.d.ts
CHANGED
|
@@ -2,7 +2,7 @@ export { Button, Form, Input, Loading, Modal, ModalRef, Select, Table, TextArea
|
|
|
2
2
|
export { AddIcon, AnimateSpin, ArchiveIcon, ArrowIcon, ArrowLeftIcon, ArrowRightIcon, BackIcon, BarsChartsIcon, BoxIcon, BuildingIcon, CajasIcon, CalendarIcon, CamaraIcon, CancelIcon, CashIcon, CategorieIcon, ChartIcon, CheckCircleIcon, CheckIcon, ClockIcon, CloseIcon, CloudIcon, CopyIcon, DeleteIcon, DocumentIcon, EditIcon, FacturacionIcon, FilterIcon, FolderIcon, GearIcon, HomeIcon, IconCursor, IconDownload, IconDuplicate, IconErase, IconGrid, IconHand, IconLayers, IconPlace, IconPolygon, IconRedo, IconReset, IconUndo, IconUpload, IconWall, IconZoomIn, IconZoomOut, InfoIcon, LifeGuardIcon, LightingIcon, LocationIcon, LogoutIcon, MenuIcon, MinusIcon, MoneyIcon, MonitorIcon, MoonIcon, NetworkIcon, NotFoundIcon, PasteIcon, PercentIcon, PrinterIcon, QuestionIcon, RestaurantMenuIcon, SaveIcon, SearchIcon, ShieldIcon, SpinnerIcon, StackIcon, SunIcon, TestIcon, TrashIcon, TruckIcon, UsersIcon, WhatsAppIcon } from './components/icons/index.js';
|
|
3
3
|
export { Alerta, AlertaAdvertencia, AlertaConfirmacion, AlertaError, AlertaExito, AlertaInfo, AlertaToast, InfoAlert } from './components/alerts/index.js';
|
|
4
4
|
export { Theme, ThemeContext, ThemeContextType, ThemeProvider, ThemeToggle, useTheme } from './context/theme/index.js';
|
|
5
|
-
export { AreaShape, DomainConfig, ElementGroup, ElementLibrary, ElementShape, ElementStatus, ElementTypeDef, Floor, FloorArea, MapElement, PaletteGroup, PanZoomState, ToolMode, VenueMap, VenueMapEditor, VenueMapEditorProps, VenueMapViewer, VenueMapViewerProps, Wall, WallMaterial, WallNode, findNearestNode, genId, snapPoint, snapToGrid, usePanZoom } from './components/VenueMapEditor/index.js';
|
|
5
|
+
export { AreaShape, DomainConfig, ElementGroup, ElementLibrary, ElementShape, ElementStatus, ElementTypeDef, Floor, FloorArea, MapElement, PaletteGroup, PanZoomState, ToolMode, VenueMap, VenueMapEditor, VenueMapEditorProps, VenueMapViewer, VenueMapViewerProps, Wall, WallMaterial, WallNode, findNearestNode, genId, snapPoint, snapToGrid, useLibraryStorage, usePanZoom } from './components/VenueMapEditor/index.js';
|
|
6
6
|
import 'react';
|
|
7
7
|
import 'react/jsx-runtime';
|
|
8
8
|
import 'sweetalert2';
|
package/dist/index.js
CHANGED
|
@@ -908,6 +908,33 @@ 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
|
+
}
|
|
911
938
|
function ToolButton({ active, disabled, title, onClick, children }) {
|
|
912
939
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
913
940
|
"button",
|
|
@@ -972,6 +999,19 @@ function Toolbar({
|
|
|
972
999
|
onLoadLibrary,
|
|
973
1000
|
onRemoveLibraryGroup
|
|
974
1001
|
}) {
|
|
1002
|
+
const [activeGroupId, setActiveGroupId] = react.useState(
|
|
1003
|
+
() => paletteGroups[0]?.id ?? null
|
|
1004
|
+
);
|
|
1005
|
+
react.useEffect(() => {
|
|
1006
|
+
if (paletteGroups.length === 0) {
|
|
1007
|
+
setActiveGroupId(null);
|
|
1008
|
+
return;
|
|
1009
|
+
}
|
|
1010
|
+
if (!paletteGroups.find((g) => g.id === activeGroupId)) {
|
|
1011
|
+
setActiveGroupId(paletteGroups[0].id);
|
|
1012
|
+
}
|
|
1013
|
+
}, [paletteGroups, activeGroupId]);
|
|
1014
|
+
const activeGroup = paletteGroups.find((g) => g.id === activeGroupId) ?? null;
|
|
975
1015
|
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col bg-white border-b border-slate-200 shadow-sm shrink-0", children: [
|
|
976
1016
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-0.5 px-2 py-1.5", children: [
|
|
977
1017
|
/* @__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 +1049,34 @@ function Toolbar({
|
|
|
1009
1049
|
/* @__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
1050
|
] })
|
|
1011
1051
|
] }),
|
|
1012
|
-
tool === "PLACE" && /* @__PURE__ */ jsxRuntime.
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1052
|
+
tool === "PLACE" && paletteGroups.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col border-t border-slate-100", children: [
|
|
1053
|
+
/* @__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(
|
|
1054
|
+
"button",
|
|
1055
|
+
{
|
|
1056
|
+
onClick: () => setActiveGroupId(group.id),
|
|
1057
|
+
className: [
|
|
1058
|
+
"flex items-center gap-1 px-3 py-1 text-xs font-medium rounded-t border-x border-t transition-colors whitespace-nowrap",
|
|
1059
|
+
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"
|
|
1060
|
+
].join(" "),
|
|
1061
|
+
children: [
|
|
1062
|
+
group.name || "Sin nombre",
|
|
1063
|
+
!group.isBase && onRemoveLibraryGroup && /* @__PURE__ */ jsxRuntime.jsx(
|
|
1064
|
+
"span",
|
|
1065
|
+
{
|
|
1066
|
+
role: "button",
|
|
1067
|
+
title: `Eliminar "${group.name}"`,
|
|
1068
|
+
onClick: (e) => {
|
|
1069
|
+
e.stopPropagation();
|
|
1070
|
+
onRemoveLibraryGroup(group.id);
|
|
1071
|
+
},
|
|
1072
|
+
className: "ml-0.5 text-slate-300 hover:text-red-400 transition-colors leading-none",
|
|
1073
|
+
children: "\xD7"
|
|
1074
|
+
}
|
|
1075
|
+
)
|
|
1076
|
+
]
|
|
1077
|
+
}
|
|
1078
|
+
) }, group.id)) }),
|
|
1079
|
+
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
1080
|
TypeChip,
|
|
1028
1081
|
{
|
|
1029
1082
|
typeDef,
|
|
@@ -1032,7 +1085,7 @@ function Toolbar({
|
|
|
1032
1085
|
},
|
|
1033
1086
|
typeDef.id
|
|
1034
1087
|
)) })
|
|
1035
|
-
] }
|
|
1088
|
+
] })
|
|
1036
1089
|
] });
|
|
1037
1090
|
}
|
|
1038
1091
|
var ZOOM_MIN = 0.1;
|
|
@@ -1972,6 +2025,7 @@ function ElementNode({
|
|
|
1972
2025
|
{
|
|
1973
2026
|
d: typeDef.svgPath,
|
|
1974
2027
|
fill: fillColor,
|
|
2028
|
+
fillRule: typeDef.fillRule ?? "nonzero",
|
|
1975
2029
|
stroke: isSelected ? "#3b82f6" : typeDef.strokeColor,
|
|
1976
2030
|
strokeWidth: isSelected ? customPath.strokeWidth * 1.5 : customPath.strokeWidth,
|
|
1977
2031
|
style: { cursor: bodyCursor },
|
|
@@ -2819,7 +2873,23 @@ function createDefaultMap() {
|
|
|
2819
2873
|
]
|
|
2820
2874
|
};
|
|
2821
2875
|
}
|
|
2822
|
-
var
|
|
2876
|
+
var DEFAULT_LIBRARY_KEY = "venueMapEditor:libraries";
|
|
2877
|
+
function mergeLibraries(existing, incoming) {
|
|
2878
|
+
const result = { ...existing };
|
|
2879
|
+
for (const [groupId, incomingGroup] of Object.entries(incoming)) {
|
|
2880
|
+
if (result[groupId]) {
|
|
2881
|
+
const existingIds = new Set(result[groupId].objects.map((o) => o.id));
|
|
2882
|
+
const newObjects = incomingGroup.objects.filter((o) => !existingIds.has(o.id));
|
|
2883
|
+
result[groupId] = {
|
|
2884
|
+
...result[groupId],
|
|
2885
|
+
objects: [...result[groupId].objects, ...newObjects]
|
|
2886
|
+
};
|
|
2887
|
+
} else {
|
|
2888
|
+
result[groupId] = incomingGroup;
|
|
2889
|
+
}
|
|
2890
|
+
}
|
|
2891
|
+
return result;
|
|
2892
|
+
}
|
|
2823
2893
|
function updateFloor(map, updatedFloor) {
|
|
2824
2894
|
return {
|
|
2825
2895
|
...map,
|
|
@@ -2859,7 +2929,9 @@ function polygonToRect(area) {
|
|
|
2859
2929
|
};
|
|
2860
2930
|
}
|
|
2861
2931
|
function VenueMapEditor({
|
|
2862
|
-
|
|
2932
|
+
domainConfigs,
|
|
2933
|
+
domainConfig,
|
|
2934
|
+
libraryStorageKey = DEFAULT_LIBRARY_KEY,
|
|
2863
2935
|
initialMap,
|
|
2864
2936
|
onChange,
|
|
2865
2937
|
width = "100%",
|
|
@@ -2873,7 +2945,13 @@ function VenueMapEditor({
|
|
|
2873
2945
|
onElementClick,
|
|
2874
2946
|
onElementTypeClick
|
|
2875
2947
|
}) {
|
|
2948
|
+
const effectiveConfigs = react.useMemo(() => {
|
|
2949
|
+
if (domainConfigs && domainConfigs.length > 0) return domainConfigs;
|
|
2950
|
+
if (domainConfig) return [domainConfig];
|
|
2951
|
+
return [];
|
|
2952
|
+
}, [domainConfigs, domainConfig]);
|
|
2876
2953
|
const initialMapRef = react.useRef(initialMap ?? createDefaultMap());
|
|
2954
|
+
const [persistedLibs, setPersistedLibs] = useLibraryStorage(libraryStorageKey);
|
|
2877
2955
|
const { map, canUndo, canRedo, push, replace, undo, redo } = useHistory(
|
|
2878
2956
|
initialMapRef.current
|
|
2879
2957
|
);
|
|
@@ -2889,31 +2967,40 @@ function VenueMapEditor({
|
|
|
2889
2967
|
const resetViewRef = react.useRef(() => void 0);
|
|
2890
2968
|
const importInputRef = react.useRef(null);
|
|
2891
2969
|
const libraryInputRef = react.useRef(null);
|
|
2970
|
+
const effectiveLibs = react.useMemo(() => ({
|
|
2971
|
+
...map.libraries ?? {},
|
|
2972
|
+
...persistedLibs
|
|
2973
|
+
}), [map.libraries, persistedLibs]);
|
|
2892
2974
|
const buildTypeDefs = react.useCallback(() => {
|
|
2893
|
-
const m = new Map(
|
|
2894
|
-
const
|
|
2895
|
-
|
|
2975
|
+
const m = /* @__PURE__ */ new Map();
|
|
2976
|
+
for (const cfg of effectiveConfigs) {
|
|
2977
|
+
for (const t of cfg.elementTypes) {
|
|
2978
|
+
if (!m.has(t.id)) m.set(t.id, t);
|
|
2979
|
+
}
|
|
2980
|
+
}
|
|
2981
|
+
for (const group of Object.values(effectiveLibs)) {
|
|
2896
2982
|
for (const t of group.objects) {
|
|
2897
2983
|
if (!m.has(t.id)) m.set(t.id, t);
|
|
2898
2984
|
}
|
|
2899
2985
|
}
|
|
2900
2986
|
return m;
|
|
2901
|
-
}, [
|
|
2987
|
+
}, [effectiveConfigs, effectiveLibs]);
|
|
2902
2988
|
const elementTypeDefs = react.useRef(buildTypeDefs());
|
|
2903
2989
|
react.useEffect(() => {
|
|
2904
2990
|
elementTypeDefs.current = buildTypeDefs();
|
|
2905
2991
|
}, [buildTypeDefs]);
|
|
2906
2992
|
const paletteGroups = react.useMemo(() => {
|
|
2907
2993
|
const groups = [];
|
|
2908
|
-
|
|
2909
|
-
|
|
2994
|
+
for (const cfg of effectiveConfigs) {
|
|
2995
|
+
if (cfg.elementTypes.length > 0) {
|
|
2996
|
+
groups.push({ id: cfg.id, name: cfg.name, types: cfg.elementTypes, isBase: true });
|
|
2997
|
+
}
|
|
2910
2998
|
}
|
|
2911
|
-
const
|
|
2912
|
-
for (const [gid, group] of Object.entries(libs)) {
|
|
2999
|
+
for (const [gid, group] of Object.entries(effectiveLibs)) {
|
|
2913
3000
|
groups.push({ id: gid, name: group.name, types: group.objects, isBase: false });
|
|
2914
3001
|
}
|
|
2915
3002
|
return groups;
|
|
2916
|
-
}, [
|
|
3003
|
+
}, [effectiveConfigs, effectiveLibs]);
|
|
2917
3004
|
react.useEffect(() => {
|
|
2918
3005
|
if (activePlaceTypeId) return;
|
|
2919
3006
|
const firstType = paletteGroups[0]?.types[0];
|
|
@@ -3053,22 +3140,27 @@ function VenueMapEditor({
|
|
|
3053
3140
|
reader.onload = (e) => {
|
|
3054
3141
|
try {
|
|
3055
3142
|
const parsed = JSON.parse(e.target?.result);
|
|
3056
|
-
const
|
|
3057
|
-
|
|
3143
|
+
const mergedPersisted = mergeLibraries(persistedLibs, parsed);
|
|
3144
|
+
setPersistedLibs(mergedPersisted);
|
|
3145
|
+
const mergedMap = mergeLibraries(map.libraries ?? {}, parsed);
|
|
3146
|
+
push({ ...map, libraries: mergedMap });
|
|
3058
3147
|
} catch {
|
|
3059
3148
|
}
|
|
3060
3149
|
};
|
|
3061
3150
|
reader.readAsText(file);
|
|
3062
3151
|
},
|
|
3063
|
-
[map, push]
|
|
3152
|
+
[map, push, persistedLibs, setPersistedLibs]
|
|
3064
3153
|
);
|
|
3065
3154
|
const handleRemoveLibraryGroup = react.useCallback(
|
|
3066
3155
|
(groupId) => {
|
|
3156
|
+
const newPersistedLibs = { ...persistedLibs };
|
|
3157
|
+
delete newPersistedLibs[groupId];
|
|
3158
|
+
setPersistedLibs(newPersistedLibs);
|
|
3067
3159
|
const libs = { ...map.libraries ?? {} };
|
|
3068
3160
|
delete libs[groupId];
|
|
3069
3161
|
push({ ...map, libraries: Object.keys(libs).length > 0 ? libs : void 0 });
|
|
3070
3162
|
},
|
|
3071
|
-
[map, push]
|
|
3163
|
+
[map, push, persistedLibs, setPersistedLibs]
|
|
3072
3164
|
);
|
|
3073
3165
|
const DEFAULT_WALL_THICKNESS = 8;
|
|
3074
3166
|
const handleAddWall = react.useCallback(
|
|
@@ -3615,6 +3707,7 @@ exports.findNearestNode = findNearestNode;
|
|
|
3615
3707
|
exports.genId = genId;
|
|
3616
3708
|
exports.snapPoint = snapPoint;
|
|
3617
3709
|
exports.snapToGrid = snapToGrid;
|
|
3710
|
+
exports.useLibraryStorage = useLibraryStorage;
|
|
3618
3711
|
exports.usePanZoom = usePanZoom;
|
|
3619
3712
|
exports.useTheme = useTheme;
|
|
3620
3713
|
//# sourceMappingURL=index.js.map
|