@datosgeo-atdt/geo-ui 0.9.5 → 0.10.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 CHANGED
@@ -5,6 +5,7 @@ Librería de componentes reutilizables en React para aplicaciones geoespaciales.
5
5
  ## Características
6
6
 
7
7
  - **MapaPMTiles**: Componente para renderizar mapas vectoriales usando MapLibre GL y PMTiles
8
+ - **TotalesDisplay**: Componente para mostrar totales y estadísticas con soporte responsive
8
9
  - **Join CSV**: Unión automática de datos tabulares con geometrías vectoriales
9
10
  - **Estilos temáticos**: Soporte para modo booleano y clasificación Jenks
10
11
  - **TypeScript**: Completamente tipado para mejor experiencia de desarrollo
@@ -335,7 +336,276 @@ Puedes usar **fuentes de datos separadas** para entidades y municipios. Cuando e
335
336
  - **Vista de entidades**: Solo muestra el nombre (ej: "Ciudad de México") sin datos adicionales
336
337
  - **Vista de municipios** (después del click): Muestra nombre del municipio + valor del CSV municipal (ej: "Azcapotzalco - Habitantes: 385,000")
337
338
 
338
- ## Props
339
+ ---
340
+
341
+ ## TotalesDisplay
342
+
343
+ Componente para mostrar totales y estadísticas de manera flexible y responsive. Soporta datos hardcodeados, desde CSV o una combinación de ambos.
344
+
345
+ ### Características
346
+
347
+ - **Tres formas de uso**: Datos hardcodeados, CSV por URL o CSV desde archivo local
348
+ - **Responsive**: Se adapta automáticamente a desktop, tablet y mobile
349
+ - **Centrado por defecto**: Posicionamiento centrado configurable
350
+ - **Personalizable**: Formateadores custom para labels y valores
351
+ - **TypeScript**: Completamente tipado
352
+
353
+ ### Uso Básico
354
+
355
+ #### Importar el componente
356
+
357
+ ```tsx
358
+ import { TotalesDisplay } from '@atdt/geo-ui';
359
+ import '@atdt/geo-ui/style.css';
360
+ ```
361
+
362
+ #### Ejemplo 1: Datos hardcodeados (más simple)
363
+
364
+ ```tsx
365
+ import { TotalesDisplay } from '@atdt/geo-ui';
366
+
367
+ function App() {
368
+ return (
369
+ <TotalesDisplay
370
+ items={[
371
+ { label: "Total de caminos abiertos", value: 496 },
372
+ { label: "Total de caminos cerrados", value: 9 },
373
+ ]}
374
+ />
375
+ );
376
+ }
377
+ ```
378
+
379
+ #### Ejemplo 2: Datos desde CSV (URL)
380
+
381
+ ```tsx
382
+ import { TotalesDisplay } from '@atdt/geo-ui';
383
+
384
+ function App() {
385
+ return (
386
+ <TotalesDisplay
387
+ csvUrl="https://example.com/datos.csv"
388
+ joinCsvKey="tipo"
389
+ field="cantidad"
390
+ labelFormatter={(key) => `Total de ${key.toLowerCase()}`}
391
+ valueFormatter={(value) => value.toLocaleString('es-MX')}
392
+ />
393
+ );
394
+ }
395
+ ```
396
+
397
+ #### Ejemplo 3: Datos desde archivo local
398
+
399
+ ```tsx
400
+ import { TotalesDisplay } from '@atdt/geo-ui';
401
+ import { useState } from 'react';
402
+
403
+ function App() {
404
+ const [csvFile, setCsvFile] = useState<File | null>(null);
405
+
406
+ return (
407
+ <div>
408
+ <input
409
+ type="file"
410
+ accept=".csv"
411
+ onChange={(e) => setCsvFile(e.target.files?.[0] || null)}
412
+ />
413
+ {csvFile && (
414
+ <TotalesDisplay
415
+ csvFile={csvFile}
416
+ joinCsvKey="id"
417
+ field="total"
418
+ labelFormatter={(key) => `Categoría ${key}`}
419
+ />
420
+ )}
421
+ </div>
422
+ );
423
+ }
424
+ ```
425
+
426
+ #### Ejemplo 4: Con filtro específico
427
+
428
+ ```tsx
429
+ // Mostrar solo un elemento específico del CSV
430
+ <TotalesDisplay
431
+ csvUrl="https://example.com/datos.csv"
432
+ joinCsvKey="region"
433
+ field="poblacion"
434
+ filterKey="CDMX"
435
+ labelFormatter={(key) => `Población de ${key}`}
436
+ valueFormatter={(value) => `${value.toLocaleString('es-MX')} habitantes`}
437
+ />
438
+ ```
439
+
440
+ #### Ejemplo 5: Sin centrado
441
+
442
+ ```tsx
443
+ <TotalesDisplay
444
+ items={[
445
+ { label: "Cobertura total", value: "98%" },
446
+ { label: "Usuarios activos", value: 15234 },
447
+ ]}
448
+ centered={false}
449
+ />
450
+ ```
451
+
452
+ ### Props de TotalesDisplay
453
+
454
+ #### Props de Datos (una de estas opciones es requerida)
455
+
456
+ | Prop | Tipo | Descripción |
457
+ |------|------|-------------|
458
+ | `items` | `TotalItem[]` | Array de objetos con `label` y `value` para mostrar datos hardcodeados |
459
+ | `csvUrl` | `string` | URL del CSV remoto |
460
+ | `csvFile` | `File` | Archivo File local |
461
+
462
+ #### Props de Configuración CSV
463
+
464
+ | Prop | Tipo | Default | Descripción |
465
+ |------|------|---------|-------------|
466
+ | `joinCsvKey` | `string` | `"key"` | Campo del CSV para usar como identificador |
467
+ | `field` | `string` | `"valor"` | Campo del CSV con los valores a visualizar |
468
+ | `filterKey` | `string` | `undefined` | Si se especifica, solo muestra el elemento con este key |
469
+
470
+ #### Props de Visualización
471
+
472
+ | Prop | Tipo | Default | Descripción |
473
+ |------|------|---------|-------------|
474
+ | `centered` | `boolean` | `true` | Si el contenedor debe estar centrado |
475
+ | `className` | `string` | `""` | Clases CSS adicionales para el contenedor |
476
+ | `labelFormatter` | `(key: string, value: any) => string` | `undefined` | Función para formatear el label |
477
+ | `valueFormatter` | `(value: any) => string \| number` | `undefined` | Función para formatear el valor |
478
+
479
+ #### Tipo TotalItem
480
+
481
+ ```tsx
482
+ interface TotalItem {
483
+ label: string;
484
+ value: number | string;
485
+ }
486
+ ```
487
+
488
+ ### Ejemplos Completos
489
+
490
+ #### Ejemplo con datos dinámicos según estado del mapa
491
+
492
+ ```tsx
493
+ import { TotalesDisplay } from '@atdt/geo-ui';
494
+ import { useState } from 'react';
495
+
496
+ function App() {
497
+ const [selectedRegion, setSelectedRegion] = useState<string | null>(null);
498
+
499
+ const datosGlobales = [
500
+ { label: "Total nacional de escuelas", value: 12450 },
501
+ { label: "Total nacional de estudiantes", value: 850000 },
502
+ ];
503
+
504
+ return (
505
+ <div>
506
+ {!selectedRegion && (
507
+ <TotalesDisplay items={datosGlobales} />
508
+ )}
509
+
510
+ {selectedRegion && (
511
+ <TotalesDisplay
512
+ csvUrl="https://example.com/datos-regionales.csv"
513
+ filterKey={selectedRegion}
514
+ joinCsvKey="region"
515
+ field="total"
516
+ labelFormatter={(key, value) => `${key} - Total`}
517
+ valueFormatter={(value) => value.toLocaleString('es-MX')}
518
+ />
519
+ )}
520
+ </div>
521
+ );
522
+ }
523
+ ```
524
+
525
+ #### Ejemplo integrando con MapaPMTiles
526
+
527
+ ```tsx
528
+ import { MapaPMTiles, TotalesDisplay } from '@atdt/geo-ui';
529
+ import '@atdt/geo-ui/style.css';
530
+
531
+ function App() {
532
+ return (
533
+ <div>
534
+ <TotalesDisplay
535
+ items={[
536
+ { label: "Total de caminos abiertos", value: 496 },
537
+ { label: "Total de caminos cerrados", value: 9 },
538
+ ]}
539
+ />
540
+
541
+ <MapaPMTiles
542
+ csvUrl="https://example.com/datos.csv"
543
+ fitToIds={["09", "15", "14"]}
544
+ mode="jenks"
545
+ enablePopup={true}
546
+ />
547
+ </div>
548
+ );
549
+ }
550
+ ```
551
+
552
+ ### Responsive
553
+
554
+ El componente se adapta automáticamente:
555
+
556
+ - **Desktop**: Layout horizontal con espaciado amplio (13.89vw entre items)
557
+ - **Tablet** (601px - 1024px): Espaciado medio (8vw entre items)
558
+ - **Mobile** (≤600px): Layout vertical, centrado, con espaciado vertical entre items
559
+
560
+ ### Personalización Avanzada
561
+
562
+ ```tsx
563
+ <TotalesDisplay
564
+ csvUrl="https://example.com/estadisticas.csv"
565
+ joinCsvKey="categoria"
566
+ field="valor"
567
+ centered={true}
568
+ className="mis-totales-custom"
569
+ labelFormatter={(key, value) => {
570
+ // Lógica custom para el label
571
+ if (key === 'total') return `🏆 Total General`;
572
+ return `📊 ${key.charAt(0).toUpperCase() + key.slice(1)}`;
573
+ }}
574
+ valueFormatter={(value) => {
575
+ // Formatear números con separadores de miles
576
+ if (typeof value === 'number') {
577
+ return `${value.toLocaleString('es-MX')} unidades`;
578
+ }
579
+ return value;
580
+ }}
581
+ />
582
+ ```
583
+
584
+ ### Estilos CSS Personalizados
585
+
586
+ Puedes sobrescribir los estilos usando tu propio CSS:
587
+
588
+ ```css
589
+ .mis-totales-custom {
590
+ background-color: #f0f0f0;
591
+ padding: 20px;
592
+ border-radius: 8px;
593
+ }
594
+
595
+ .mis-totales-custom .totales-item {
596
+ font-size: 1.2em;
597
+ color: #2c3e50;
598
+ }
599
+
600
+ .mis-totales-custom .totales-value {
601
+ color: #66827f;
602
+ font-weight: 900;
603
+ }
604
+ ```
605
+
606
+ ---
607
+
608
+ ## Props de MapaPMTiles
339
609
 
340
610
  ### Props Obligatorias
341
611
 
@@ -454,62 +724,6 @@ Para consumir PMTiles y CSV de dominios externos, asegúrate de que:
454
724
  - **PapaParse**: Parsing de CSV
455
725
  - **Vite**: Build tool
456
726
 
457
- ## Build y Desarrollo
458
-
459
- ### Desarrollo local
460
-
461
- ```bash
462
- npm install
463
- npm run dev
464
- ```
465
-
466
- ### Build de producción
467
-
468
- ```bash
469
- npm run build
470
- ```
471
-
472
- Esto genera:
473
-
474
- - `dist/geo-ui.js` (ES module)
475
- - `dist/geo-ui.umd.cjs` (UMD)
476
- - `dist/index.d.ts` (TypeScript definitions)
477
- - `dist/style.css` (estilos de MapLibre GL)
478
-
479
- ### Publicar a npm
480
-
481
- ```bash
482
- # Asegúrate de estar autenticado en npm
483
- npm login
484
-
485
- # Incrementa versión (patch, minor, major)
486
- npm version patch
487
-
488
- # Publica (ejecutará automáticamente build por prepublishOnly)
489
- npm publish --access public
490
- ```
491
-
492
- **Nota**: Para paquetes con scope `@atdt/`, necesitas usar `--access public` la primera vez.
493
-
494
- ## Estructura del Proyecto
495
-
496
- ```
497
- geo-ui/
498
- ├── src/
499
- │ ├── components/
500
- │ │ └── MapaPMTiles.tsx # Componente principal
501
- │ ├── utils/
502
- │ │ ├── csv.ts # Utilidades para parsear CSV
503
- │ │ ├── jenks.ts # Algoritmo Jenks Natural Breaks
504
- │ │ └── pmtilesFeatureSearch.ts # Búsqueda de features y bbox
505
- │ └── index.ts # Entry point
506
- ├── dist/ # Build output (generado)
507
- ├── package.json
508
- ├── tsconfig.json
509
- ├── vite.config.ts
510
- └── README.md
511
- ```
512
-
513
727
  ## Licencia
514
728
 
515
729
  ISC
@@ -0,0 +1,19 @@
1
+ import { default as React } from 'react';
2
+ export interface TotalItem {
3
+ label: string;
4
+ value: number | string;
5
+ }
6
+ export interface TotalesDisplayProps {
7
+ items?: TotalItem[];
8
+ csvUrl?: string;
9
+ csvFile?: File;
10
+ joinCsvKey?: string;
11
+ field?: string;
12
+ filterKey?: string;
13
+ centered?: boolean;
14
+ className?: string;
15
+ labelFormatter?: (key: string, value: any) => string;
16
+ valueFormatter?: (value: any) => string | number;
17
+ }
18
+ export declare const TotalesDisplay: React.FC<TotalesDisplayProps>;
19
+ //# sourceMappingURL=TotalesDisplay.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TotalesDisplay.d.ts","sourceRoot":"","sources":["../../src/components/TotalesDisplay.tsx"],"names":[],"mappings":"AAAA,OAAO,KAA8B,MAAM,OAAO,CAAC;AAEnD,OAAO,sBAAsB,CAAC;AAE9B,MAAM,WAAW,SAAS;IACxB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,GAAG,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,mBAAmB;IAElC,KAAK,CAAC,EAAE,SAAS,EAAE,CAAC;IAGpB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,IAAI,CAAC;IACf,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;IAGf,SAAS,CAAC,EAAE,MAAM,CAAC;IAGnB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IAGnB,cAAc,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,KAAK,MAAM,CAAC;IACrD,cAAc,CAAC,EAAE,CAAC,KAAK,EAAE,GAAG,KAAK,MAAM,GAAG,MAAM,CAAC;CAClD;AAED,eAAO,MAAM,cAAc,EAAE,KAAK,CAAC,EAAE,CAAC,mBAAmB,CAmGxD,CAAC"}