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/README.md
CHANGED
|
@@ -380,7 +380,9 @@ function App() {
|
|
|
380
380
|
|------|------|---------|-------------|
|
|
381
381
|
| `initialMap` | `VenueMap` | mapa vacío | Mapa inicial. Se puede actualizar desde fuera para recargar el editor. |
|
|
382
382
|
| `onChange` | `(map: VenueMap) => void` | — | Se llama en cada cambio del estado interno. |
|
|
383
|
-
| `
|
|
383
|
+
| `domainConfigs` | `DomainConfig[]` | `[]` | Array de catálogos de tipos predefinidos. Cada uno aparece como una pestaña separada en la paleta. |
|
|
384
|
+
| `domainConfig` | `DomainConfig` | — | **Obsoleto** — usa `domainConfigs`. Catálogo único (se convierte internamente a un array de un elemento). |
|
|
385
|
+
| `libraryStorageKey` | `string` | `'venueMapEditor:libraries'` | Clave de `localStorage` donde se persisten las librerías importadas. Pasa `''` para deshabilitar la persistencia. |
|
|
384
386
|
| `width` | `string \| number` | `'100%'` | Ancho del componente. |
|
|
385
387
|
| `height` | `string \| number` | `'600px'` | Alto del componente. |
|
|
386
388
|
| `gridSize` | `number` | `20` | Tamaño de la cuadrícula en unidades de canvas. |
|
|
@@ -434,9 +436,60 @@ const estados: ElementStatus[] = [
|
|
|
434
436
|
|
|
435
437
|
---
|
|
436
438
|
|
|
439
|
+
### Múltiples catálogos de elementos (domainConfigs)
|
|
440
|
+
|
|
441
|
+
Pasa varios `DomainConfig` vía la prop `domainConfigs`. Cada catálogo aparece como una **pestaña separada** en la paleta — los tipos nunca se mezclan entre tabs.
|
|
442
|
+
|
|
443
|
+
```tsx
|
|
444
|
+
import { VenueMapEditor } from 'neogestify-ui-components/VenueMapEditor';
|
|
445
|
+
import type { DomainConfig } from 'neogestify-ui-components/VenueMapEditor';
|
|
446
|
+
|
|
447
|
+
const mobiliario: DomainConfig = {
|
|
448
|
+
id: 'furniture',
|
|
449
|
+
name: 'Mobiliario',
|
|
450
|
+
elementTypes: [
|
|
451
|
+
{ id: 'CHAIR', label: 'Silla', shape: 'circle', defaultWidth: 30, defaultHeight: 30, color: '#fef3c7', strokeColor: '#d97706' },
|
|
452
|
+
{ id: 'TABLE_RECT', label: 'Mesa rect.', shape: 'rect', defaultWidth: 100, defaultHeight: 60, color: '#fef3c7', strokeColor: '#d97706' },
|
|
453
|
+
],
|
|
454
|
+
};
|
|
455
|
+
|
|
456
|
+
const iluminacion: DomainConfig = {
|
|
457
|
+
id: 'lighting',
|
|
458
|
+
name: 'Iluminación',
|
|
459
|
+
elementTypes: [
|
|
460
|
+
{ id: 'SPOT_LIGHT', label: 'Foco', shape: 'circle', defaultWidth: 40, defaultHeight: 40, color: '#fef9c3', strokeColor: '#ca8a04' },
|
|
461
|
+
{ id: 'STRIP_LIGHT', label: 'Tira LED', shape: 'rect', defaultWidth: 120, defaultHeight: 15, color: '#fef9c3', strokeColor: '#ca8a04' },
|
|
462
|
+
],
|
|
463
|
+
};
|
|
464
|
+
|
|
465
|
+
<VenueMapEditor domainConfigs={[mobiliario, iluminacion]} />
|
|
466
|
+
```
|
|
467
|
+
|
|
468
|
+
La paleta mostrará:
|
|
469
|
+
|
|
470
|
+
```
|
|
471
|
+
[ Mobiliario ] [ Iluminación ]
|
|
472
|
+
─────────────────────────────
|
|
473
|
+
[Silla] [Mesa rect.]
|
|
474
|
+
```
|
|
475
|
+
|
|
476
|
+
---
|
|
477
|
+
|
|
437
478
|
### Crear una librería de elementos (JSON)
|
|
438
479
|
|
|
439
|
-
Los elementos que aparecen en la paleta
|
|
480
|
+
Los elementos que aparecen en la paleta también se pueden definir en archivos JSON que el usuario carga desde el botón **⊞** (Cargar librería).
|
|
481
|
+
|
|
482
|
+
**Persistencia automática:** las librerías importadas se guardan en `localStorage` bajo la clave `libraryStorageKey` (por defecto `'venueMapEditor:libraries'`). Al recargar la página se restauran automáticamente **antes** de que el mapa renderice, evitando errores de "tipo de elemento desconocido".
|
|
483
|
+
|
|
484
|
+
**Merge inteligente al importar:** si un grupo con el mismo `id` ya existe, se añaden únicamente los elementos cuyo `id` no esté duplicado. Los elementos existentes nunca se sobreescriben.
|
|
485
|
+
|
|
486
|
+
```tsx
|
|
487
|
+
// Cambiar la clave de almacenamiento (útil con múltiples editores en la misma app)
|
|
488
|
+
<VenueMapEditor libraryStorageKey="mi-proyecto:libs" />
|
|
489
|
+
|
|
490
|
+
// Deshabilitar persistencia
|
|
491
|
+
<VenueMapEditor libraryStorageKey="" />
|
|
492
|
+
```
|
|
440
493
|
|
|
441
494
|
#### Formato del JSON
|
|
442
495
|
|
|
@@ -527,17 +580,19 @@ Ahora puedes definir cualquier figura SVG usando un path:
|
|
|
527
580
|
|
|
528
581
|
#### Propiedades de cada objeto
|
|
529
582
|
|
|
530
|
-
| Campo | Tipo | Descripción |
|
|
531
|
-
|
|
532
|
-
| `id` | `string` | Identificador único del tipo.
|
|
533
|
-
| `label` | `string` | Nombre visible en la paleta. |
|
|
534
|
-
| `shape` | `"rect" \| "circle" \| "arrow" \| "path"` | Forma del objeto
|
|
535
|
-
| `defaultWidth` | `number` | Ancho inicial al colocar el elemento (unidades de canvas ≈
|
|
536
|
-
| `defaultHeight` | `number` | Alto inicial. |
|
|
537
|
-
| `color` | `string` | Color de relleno (cualquier valor CSS: `#hex`, `rgb()`, `hsl()`, etc.). |
|
|
538
|
-
| `strokeColor` | `string` | Color del borde. |
|
|
539
|
-
| `svgPath` | `string` |
|
|
540
|
-
| `
|
|
583
|
+
| Campo | Tipo | Requerido | Descripción |
|
|
584
|
+
|-------|------|-----------|-------------|
|
|
585
|
+
| `id` | `string` | ✓ | Identificador único del tipo. Se usa como key en `onElementTypeClick`. |
|
|
586
|
+
| `label` | `string` | ✓ | Nombre visible en la paleta y en el canvas. |
|
|
587
|
+
| `shape` | `"rect" \| "circle" \| "arrow" \| "path" \| "svg"` | ✓ | Forma del objeto. |
|
|
588
|
+
| `defaultWidth` | `number` | ✓ | Ancho inicial al colocar el elemento (unidades de canvas ≈ px a zoom 1×). |
|
|
589
|
+
| `defaultHeight` | `number` | ✓ | Alto inicial. |
|
|
590
|
+
| `color` | `string` | ✓ | Color de relleno (cualquier valor CSS: `#hex`, `rgb()`, `hsl()`, etc.). |
|
|
591
|
+
| `strokeColor` | `string` | ✓ | Color del borde. |
|
|
592
|
+
| `svgPath` | `string` | solo para `shape:"path"` | Atributo `d` de un `<path>` SVG. Se escala automáticamente al bounding box del elemento. |
|
|
593
|
+
| `svgMarkup` | `string` | solo para `shape:"svg"` | Markup SVG completo `<svg>...</svg>`. Se extrae el contenido interno y se escala al bounding box del elemento. Debe incluir `viewBox`. |
|
|
594
|
+
| `viewBox` | `string` | — | Espacio de coordenadas del `svgPath`. Formato: `"minX minY w h"`. Default: `"0 0 100 100"`. |
|
|
595
|
+
| `fillRule` | `"nonzero" \| "evenodd"` | — | Regla de relleno SVG. Usa `"evenodd"` para crear huecos con sub-paths (engranajes, letras, donuts). Default: `"nonzero"`. |
|
|
541
596
|
|
|
542
597
|
#### Formas disponibles
|
|
543
598
|
|
|
@@ -546,15 +601,94 @@ Ahora puedes definir cualquier figura SVG usando un path:
|
|
|
546
601
|
| `rect` | Rectángulo | Mesas, espacios de parqueo, habitaciones |
|
|
547
602
|
| `circle` | Elipse (círculo si `width === height`) | Mesas redondas, columnas, plantas |
|
|
548
603
|
| `arrow` | Flecha apuntando a la derecha | Entradas, salidas, sentidos de circulación |
|
|
549
|
-
| `path` | Forma SVG personalizada |
|
|
604
|
+
| `path` | Forma SVG personalizada libre | Cualquier figura: estrella, engranaje, piano, logo... |
|
|
605
|
+
| `svg` | SVG completo inline | Cualquier SVG con múltiples elementos, gradientes, etc. |
|
|
606
|
+
|
|
607
|
+
#### Formas personalizadas con `shape: "path"`
|
|
608
|
+
|
|
609
|
+
El campo `svgPath` acepta el atributo `d` de cualquier `<path>` SVG estándar. El sistema escala la figura para que ocupe exactamente el bounding box `width × height` del elemento. Puedes diseñar tus formas con Inkscape, Figma u otro editor vectorial y copiar el `d=` directamente.
|
|
610
|
+
|
|
611
|
+
```json
|
|
612
|
+
{
|
|
613
|
+
"especiales": {
|
|
614
|
+
"name": "Especiales",
|
|
615
|
+
"objects": [
|
|
616
|
+
{
|
|
617
|
+
"id": "STAR",
|
|
618
|
+
"label": "Estrella",
|
|
619
|
+
"shape": "path",
|
|
620
|
+
"viewBox": "0 0 100 100",
|
|
621
|
+
"svgPath": "M50 5 L61 35 L95 35 L68 57 L79 91 L50 70 L21 91 L32 57 L5 35 L39 35 Z",
|
|
622
|
+
"defaultWidth": 60,
|
|
623
|
+
"defaultHeight": 60,
|
|
624
|
+
"color": "#facc15",
|
|
625
|
+
"strokeColor": "#ca8a04"
|
|
626
|
+
},
|
|
627
|
+
{
|
|
628
|
+
"id": "GEAR",
|
|
629
|
+
"label": "Engranaje",
|
|
630
|
+
"shape": "path",
|
|
631
|
+
"viewBox": "0 0 100 100",
|
|
632
|
+
"fillRule": "evenodd",
|
|
633
|
+
"svgPath": "M36.61,17.66 L44.13,5.39 L55.87,5.39 L63.39,17.66 A35,35 0 0,1 77.40,14.30 L85.70,22.60 L82.34,36.61 A35,35 0 0,1 94.61,44.13 L94.61,55.87 L82.34,63.39 A35,35 0 0,1 85.70,77.40 L77.40,85.70 L63.39,82.34 A35,35 0 0,1 55.87,94.61 L44.13,94.61 L36.61,82.34 A35,35 0 0,1 22.60,85.70 L14.30,77.40 L17.66,63.39 A35,35 0 0,1 5.39,55.87 L5.39,44.13 L17.66,36.61 A35,35 0 0,1 14.30,22.60 L22.60,14.30 Z M65,50 A15,15 0 1,0 35,50 A15,15 0 1,0 65,50 Z",
|
|
634
|
+
"defaultWidth": 70,
|
|
635
|
+
"defaultHeight": 70,
|
|
636
|
+
"color": "#94a3b8",
|
|
637
|
+
"strokeColor": "#334155"
|
|
638
|
+
}
|
|
639
|
+
]
|
|
640
|
+
}
|
|
641
|
+
}
|
|
642
|
+
```
|
|
643
|
+
|
|
644
|
+
> **Hitbox de piso:** para formas personalizadas que no llenan su bounding box (estrellas, logos, etc.), la detección de bordes usa un cuadrado de lado `min(width, height)` centrado en el elemento — esto evita que la figura quede demasiado restringida al área del piso.
|
|
645
|
+
|
|
646
|
+
#### Formas personalizadas con `shape: "svg"`
|
|
647
|
+
|
|
648
|
+
El campo `svgMarkup` acepta un **SVG completo** como string. El sistema extrae el `viewBox` del tag `<svg>` y renderiza los elementos internos escalados al bounding box del elemento. Esto permite usar figuras con múltiples paths, círculos, rectángulos, textos, etc.
|
|
649
|
+
|
|
650
|
+
> **Seguridad:** el markup se sanitiza automáticamente eliminando `<script>`, `on*` event handlers, `javascript:` URIs y tags peligrosos.
|
|
651
|
+
|
|
652
|
+
```json
|
|
653
|
+
{
|
|
654
|
+
"iconos": {
|
|
655
|
+
"name": "Iconos SVG",
|
|
656
|
+
"objects": [
|
|
657
|
+
{
|
|
658
|
+
"id": "CAR",
|
|
659
|
+
"label": "Carro",
|
|
660
|
+
"shape": "svg",
|
|
661
|
+
"svgMarkup": "<svg viewBox=\"0 0 100 100\"><rect x=\"10\" y=\"40\" width=\"80\" height=\"35\" rx=\"5\" fill=\"currentColor\"/><rect x=\"5\" y=\"50\" width=\"90\" height=\"20\" rx=\"3\" fill=\"currentColor\"/><circle cx=\"28\" cy=\"75\" r=\"9\" fill=\"currentColor\"/><circle cx=\"72\" cy=\"75\" r=\"9\" fill=\"currentColor\"/><rect x=\"25\" y=\"44\" width=\"20\" height=\"12\" rx=\"2\" fill=\"white\" opacity=\"0.4\"/><rect x=\"55\" y=\"44\" width=\"20\" height=\"12\" rx=\"2\" fill=\"white\" opacity=\"0.4\"/></svg>",
|
|
662
|
+
"defaultWidth": 80,
|
|
663
|
+
"defaultHeight": 80,
|
|
664
|
+
"color": "#3b82f6",
|
|
665
|
+
"strokeColor": "#1e40af"
|
|
666
|
+
},
|
|
667
|
+
{
|
|
668
|
+
"id": "PEOPLE",
|
|
669
|
+
"label": "Persona",
|
|
670
|
+
"shape": "svg",
|
|
671
|
+
"svgMarkup": "<svg viewBox=\"0 0 100 100\"><circle cx=\"50\" cy=\"25\" r=\"15\"/><path d=\"M30 90 L30 50 Q30 40 40 40 L60 40 Q70 40 70 50 L70 90 M20 60 L80 60\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"6\" stroke-linecap=\"round\"/></svg>",
|
|
672
|
+
"defaultWidth": 50,
|
|
673
|
+
"defaultHeight": 50,
|
|
674
|
+
"color": "#f97316",
|
|
675
|
+
"strokeColor": "#c2410c"
|
|
676
|
+
}
|
|
677
|
+
]
|
|
678
|
+
}
|
|
679
|
+
}
|
|
680
|
+
```
|
|
550
681
|
|
|
551
|
-
|
|
682
|
+
**Notas sobre `shape: "svg"`:**
|
|
552
683
|
|
|
553
|
-
|
|
684
|
+
- El string `svgMarkup` **debe** ser un `<svg>` válido con atributo `viewBox`.
|
|
685
|
+
- Los atributos `color` y `strokeColor` del `ElementTypeDef` se aplican como `fill` y `stroke` en el `<g>` contenedor. Usa `currentColor` en tu SVG para heredar el color.
|
|
686
|
+
- El `viewBox` se extrae automáticamente del `<svg>` — no necesitas especificarlo por separado.
|
|
687
|
+
- Funciona con cualquier combinación de elementos SVG internos: `<path>`, `<circle>`, `<rect>`, `<g>`, `<line>`, `<polygon>`, etc.
|
|
554
688
|
|
|
555
689
|
#### Varios grupos en un archivo
|
|
556
690
|
|
|
557
|
-
Un mismo archivo puede tener tantos grupos como necesites. Cada grupo aparece como una
|
|
691
|
+
Un mismo archivo puede tener tantos grupos como necesites. Cada grupo aparece como una **pestaña separada** en la paleta. Se pueden cargar múltiples archivos — los grupos se acumulan. Cada grupo importado muestra un botón **×** en su pestaña para eliminarlo.
|
|
558
692
|
|
|
559
693
|
```json
|
|
560
694
|
{
|
|
@@ -677,9 +811,9 @@ Los elementos y paredes siempre se mantienen dentro del piso al moverlos o coloc
|
|
|
677
811
|
|
|
678
812
|
| Botón | Función |
|
|
679
813
|
|-------|---------|
|
|
680
|
-
| ⬇ Exportar mapa | Descarga el estado actual como `.json` (incluye las librerías embebidas). |
|
|
814
|
+
| ⬇ Exportar mapa | Descarga el estado actual como `.json` (incluye las librerías embebidas para portabilidad). |
|
|
681
815
|
| ⬆ Importar mapa | Carga un `.json` exportado previamente, reemplazando el mapa actual. |
|
|
682
|
-
| ⊞ Cargar librería | Carga un `.json` de elementos
|
|
816
|
+
| ⊞ Cargar librería | Carga un `.json` de elementos. Los grupos se añaden a la paleta como nuevas pestañas. Si el grupo ya existe, sólo se añaden los objetos con `id` nuevo (sin sobrescribir). La librería se persiste automáticamente en `localStorage`. |
|
|
683
817
|
|
|
684
818
|
---
|
|
685
819
|
|