carconnect-gatherleads-ui-lib 2.4.1 → 2.4.5
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 +625 -51
- package/dist/carconnect-gatherleads-ui-lib.css +1 -1
- package/dist/carconnect-gatherleads-ui-lib.d.ts +606 -0
- package/dist/carconnect-gatherleads-ui-lib.js +12487 -13351
- package/dist/carconnect-gatherleads-ui-lib.umd.cjs +27 -131
- package/package.json +4 -4
package/README.md
CHANGED
|
@@ -870,66 +870,80 @@ interface GatherTextareaFieldConfig extends BaseFieldConfig {
|
|
|
870
870
|
}
|
|
871
871
|
```
|
|
872
872
|
|
|
873
|
-
|
|
873
|
+
#### **Características Destacadas**
|
|
874
874
|
|
|
875
|
-
|
|
876
|
-
interface GatherDateFieldConfig extends BaseFieldConfig {
|
|
877
|
-
type: 'date' | 'daterange';
|
|
878
|
-
maxDate?: Date;
|
|
879
|
-
minDate?: Date;
|
|
880
|
-
singleDate?: boolean; // Solo para daterange
|
|
881
|
-
displayFormat?: string; // Formato de visualización
|
|
882
|
-
size?: 'sm' | 'md' | 'lg';
|
|
883
|
-
}
|
|
884
|
-
```
|
|
875
|
+
✨ **Nuevas Funcionalidades:**
|
|
885
876
|
|
|
886
|
-
**
|
|
877
|
+
1. **Scroll en selector de años** - El selector ahora tiene altura máxima y scroll automático
|
|
878
|
+
2. **Control de años personalizables** - `pastYearsCount={10}` muestra año actual + últimos 10 años
|
|
879
|
+
3. **Textos personalizables** - Botones Apply/Cancel en cualquier idioma
|
|
880
|
+
4. **Callbacks para eventos** - `onApply`, `onCancel`, `onOpenChange` para lógica personalizada
|
|
881
|
+
5. **Formatos de fecha flexibles** - Personaliza cómo se muestran las fechas
|
|
882
|
+
6. **Auto-cierre inteligente** - Cierre automático tras selección para UX más rápida
|
|
883
|
+
7. **Modo sin botones** - `hideActionButtons={true}` para aplicar cambios inmediatamente
|
|
884
|
+
|
|
885
|
+
#### **Ejemplos de Uso Avanzado**
|
|
887
886
|
|
|
888
887
|
```tsx
|
|
889
|
-
//
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
size: "md"
|
|
899
|
-
}
|
|
888
|
+
// EJEMPLO 1: Date Picker rápido sin confirmación
|
|
889
|
+
<GatherDatePicker
|
|
890
|
+
mode="single"
|
|
891
|
+
selected={date}
|
|
892
|
+
onSelect={setDate}
|
|
893
|
+
autoClose={true}
|
|
894
|
+
hideActionButtons={true}
|
|
895
|
+
placeholder="Selección rápida"
|
|
896
|
+
/>
|
|
900
897
|
|
|
901
|
-
//
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
898
|
+
// EJEMPLO 2: Con validación y callbacks
|
|
899
|
+
<GatherDatePicker
|
|
900
|
+
mode="single"
|
|
901
|
+
selected={date}
|
|
902
|
+
onSelect={setDate}
|
|
903
|
+
applyButtonText="Guardar Fecha"
|
|
904
|
+
cancelButtonText="Descartar"
|
|
905
|
+
onApply={(selectedDate) => {
|
|
906
|
+
// Validar y guardar
|
|
907
|
+
if (selectedDate) {
|
|
908
|
+
saveToAPI(selectedDate);
|
|
909
|
+
toast.success('Fecha guardada');
|
|
910
|
+
}
|
|
911
|
+
}}
|
|
912
|
+
onCancel={() => {
|
|
913
|
+
toast.info('Cambios descartados');
|
|
914
|
+
}}
|
|
915
|
+
/>
|
|
912
916
|
|
|
913
|
-
//
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
917
|
+
// EJEMPLO 3: Rango con formato personalizado y años limitados
|
|
918
|
+
<GatherDatePicker
|
|
919
|
+
mode="range"
|
|
920
|
+
selected={dateRange}
|
|
921
|
+
onSelect={setDateRange}
|
|
922
|
+
rangeFromFormat="dd/MM/yy"
|
|
923
|
+
rangeToFormat="dd/MM/yy"
|
|
924
|
+
pastYearsCount={5} // Solo últimos 5 años
|
|
925
|
+
placeholder="Del - Al"
|
|
926
|
+
/>
|
|
922
927
|
|
|
923
|
-
//
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
}
|
|
928
|
+
// EJEMPLO 4: Input de formulario completo
|
|
929
|
+
<GatherDatePickerInput
|
|
930
|
+
label="Fecha de Registro"
|
|
931
|
+
description="Seleccione la fecha de registro del cliente"
|
|
932
|
+
placeholder="dd/mm/aaaa"
|
|
933
|
+
mode="single"
|
|
934
|
+
selected={date}
|
|
935
|
+
onSelect={setDate}
|
|
936
|
+
dateFormat="dd/MM/yyyy"
|
|
937
|
+
showSelectedDate={true}
|
|
938
|
+
selectedDateLabel="Registrado el:"
|
|
939
|
+
autoClose={true}
|
|
940
|
+
minDate={subDays(new Date(), 365)}
|
|
941
|
+
maxDate={new Date()}
|
|
942
|
+
/>
|
|
931
943
|
```
|
|
932
944
|
|
|
945
|
+
---
|
|
946
|
+
|
|
933
947
|
### 7. **GatherCheckboxGroupFieldConfig** - Grupo de Checkboxes
|
|
934
948
|
|
|
935
949
|
```tsx
|
|
@@ -2614,3 +2628,563 @@ const apiData = await fetch('/api/users').then((r) => r.json());
|
|
|
2614
2628
|
```
|
|
2615
2629
|
|
|
2616
2630
|
Esta guía completa cubre todos los aspectos de GatherTable, desde uso básico hasta implementaciones avanzadas con ordenamiento, filtrado y renderizado personalizado.
|
|
2631
|
+
|
|
2632
|
+
# 🪟 Guía Completa de GatherModal
|
|
2633
|
+
|
|
2634
|
+
## 🎯 Introducción
|
|
2635
|
+
|
|
2636
|
+
`GatherModal` es un componente modal genérico y altamente personalizable que proporciona diálogos flexibles con soporte completo para diferentes posiciones, tamaños, animaciones y accesibilidad avanzada.
|
|
2637
|
+
|
|
2638
|
+
## 🏗️ Características Principales
|
|
2639
|
+
|
|
2640
|
+
- **Posiciones**: center, left, right (ideal para paneles laterales)
|
|
2641
|
+
- **Tamaños**: sm, md, lg, xl, full
|
|
2642
|
+
- **Header personalizable**: Título simple o contenido completamente custom
|
|
2643
|
+
- **Footer flexible**: Botones por defecto o contenido personalizado
|
|
2644
|
+
- **Accesibilidad avanzada**: Focus management, keyboard navigation
|
|
2645
|
+
- **Animaciones**: Transiciones suaves configurables
|
|
2646
|
+
- **Portal rendering**: Se renderiza fuera del árbol DOM
|
|
2647
|
+
|
|
2648
|
+
## 📋 Uso Básico
|
|
2649
|
+
|
|
2650
|
+
### Modal Simple de Confirmación
|
|
2651
|
+
|
|
2652
|
+
```tsx
|
|
2653
|
+
import { GatherModal } from '@/base/modal';
|
|
2654
|
+
import { useState } from 'react';
|
|
2655
|
+
|
|
2656
|
+
const BasicModalExample = () => {
|
|
2657
|
+
const [isOpen, setIsOpen] = useState(false);
|
|
2658
|
+
|
|
2659
|
+
return (
|
|
2660
|
+
<>
|
|
2661
|
+
<button onClick={() => setIsOpen(true)}>Abrir Modal</button>
|
|
2662
|
+
|
|
2663
|
+
<GatherModal
|
|
2664
|
+
open={isOpen}
|
|
2665
|
+
onClose={() => setIsOpen(false)}
|
|
2666
|
+
header={{
|
|
2667
|
+
title: 'Confirmar Acción',
|
|
2668
|
+
subtitle: '¿Estás seguro de continuar?',
|
|
2669
|
+
}}
|
|
2670
|
+
footer={{
|
|
2671
|
+
acceptText: 'Confirmar',
|
|
2672
|
+
cancelText: 'Cancelar',
|
|
2673
|
+
acceptVariant: 'gather-primary',
|
|
2674
|
+
cancelVariant: 'gather-outline',
|
|
2675
|
+
onAccept: () => {
|
|
2676
|
+
console.log('Confirmado');
|
|
2677
|
+
setIsOpen(false);
|
|
2678
|
+
},
|
|
2679
|
+
onCancel: () => setIsOpen(false),
|
|
2680
|
+
}}
|
|
2681
|
+
>
|
|
2682
|
+
<p>Esta acción no se puede deshacer.</p>
|
|
2683
|
+
</GatherModal>
|
|
2684
|
+
</>
|
|
2685
|
+
);
|
|
2686
|
+
};
|
|
2687
|
+
```
|
|
2688
|
+
|
|
2689
|
+
### Modal de Formulario
|
|
2690
|
+
|
|
2691
|
+
```tsx
|
|
2692
|
+
const FormModalExample = () => {
|
|
2693
|
+
const [isOpen, setIsOpen] = useState(false);
|
|
2694
|
+
const [loading, setLoading] = useState(false);
|
|
2695
|
+
|
|
2696
|
+
const handleSubmit = async () => {
|
|
2697
|
+
setLoading(true);
|
|
2698
|
+
try {
|
|
2699
|
+
// Simular API call
|
|
2700
|
+
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
2701
|
+
setIsOpen(false);
|
|
2702
|
+
} finally {
|
|
2703
|
+
setLoading(false);
|
|
2704
|
+
}
|
|
2705
|
+
};
|
|
2706
|
+
|
|
2707
|
+
return (
|
|
2708
|
+
<GatherModal
|
|
2709
|
+
open={isOpen}
|
|
2710
|
+
onClose={() => setIsOpen(false)}
|
|
2711
|
+
size='lg'
|
|
2712
|
+
header={{
|
|
2713
|
+
title: 'Nuevo Usuario',
|
|
2714
|
+
subtitle: 'Complete los datos del usuario',
|
|
2715
|
+
}}
|
|
2716
|
+
footer={{
|
|
2717
|
+
acceptText: 'Guardar',
|
|
2718
|
+
cancelText: 'Cancelar',
|
|
2719
|
+
acceptLoading: loading,
|
|
2720
|
+
acceptDisabled: loading,
|
|
2721
|
+
onAccept: handleSubmit,
|
|
2722
|
+
onCancel: () => setIsOpen(false),
|
|
2723
|
+
}}
|
|
2724
|
+
>
|
|
2725
|
+
<div className='space-y-4'>
|
|
2726
|
+
<div>
|
|
2727
|
+
<label className='mb-1 block text-sm font-medium'>
|
|
2728
|
+
Nombre completo
|
|
2729
|
+
</label>
|
|
2730
|
+
<input
|
|
2731
|
+
type='text'
|
|
2732
|
+
className='w-full rounded border p-2'
|
|
2733
|
+
placeholder='Juan Pérez'
|
|
2734
|
+
/>
|
|
2735
|
+
</div>
|
|
2736
|
+
<div>
|
|
2737
|
+
<label className='mb-1 block text-sm font-medium'>Email</label>
|
|
2738
|
+
<input
|
|
2739
|
+
type='email'
|
|
2740
|
+
className='w-full rounded border p-2'
|
|
2741
|
+
placeholder='juan@ejemplo.com'
|
|
2742
|
+
/>
|
|
2743
|
+
</div>
|
|
2744
|
+
</div>
|
|
2745
|
+
</GatherModal>
|
|
2746
|
+
);
|
|
2747
|
+
};
|
|
2748
|
+
```
|
|
2749
|
+
|
|
2750
|
+
## 🎨 Posiciones y Tamaños
|
|
2751
|
+
|
|
2752
|
+
### Modal Centrado (Por Defecto)
|
|
2753
|
+
|
|
2754
|
+
```tsx
|
|
2755
|
+
<GatherModal
|
|
2756
|
+
open={isOpen}
|
|
2757
|
+
position='center' // Por defecto
|
|
2758
|
+
size='md' // Tamaño mediano
|
|
2759
|
+
onClose={() => setIsOpen(false)}
|
|
2760
|
+
>
|
|
2761
|
+
<p>Modal centrado en la pantalla</p>
|
|
2762
|
+
</GatherModal>
|
|
2763
|
+
```
|
|
2764
|
+
|
|
2765
|
+
### Panel Lateral Derecho
|
|
2766
|
+
|
|
2767
|
+
```tsx
|
|
2768
|
+
<GatherModal
|
|
2769
|
+
open={isOpen}
|
|
2770
|
+
position='right'
|
|
2771
|
+
size='lg'
|
|
2772
|
+
onClose={() => setIsOpen(false)}
|
|
2773
|
+
header={{
|
|
2774
|
+
title: 'Panel de Configuración',
|
|
2775
|
+
}}
|
|
2776
|
+
>
|
|
2777
|
+
<div className='space-y-4'>
|
|
2778
|
+
<h3>Configuraciones</h3>
|
|
2779
|
+
<div>Contenido del panel lateral...</div>
|
|
2780
|
+
</div>
|
|
2781
|
+
</GatherModal>
|
|
2782
|
+
```
|
|
2783
|
+
|
|
2784
|
+
### Modal de Pantalla Completa
|
|
2785
|
+
|
|
2786
|
+
```tsx
|
|
2787
|
+
<GatherModal
|
|
2788
|
+
open={isOpen}
|
|
2789
|
+
size='full'
|
|
2790
|
+
onClose={() => setIsOpen(false)}
|
|
2791
|
+
header={{
|
|
2792
|
+
title: 'Vista Completa',
|
|
2793
|
+
}}
|
|
2794
|
+
scrollBehavior={{
|
|
2795
|
+
bodyScrollable: true,
|
|
2796
|
+
bodyMaxHeight: '80vh',
|
|
2797
|
+
}}
|
|
2798
|
+
>
|
|
2799
|
+
<div>Contenido que ocupa toda la pantalla...</div>
|
|
2800
|
+
</GatherModal>
|
|
2801
|
+
```
|
|
2802
|
+
|
|
2803
|
+
## 🔧 Configuración Avanzada
|
|
2804
|
+
|
|
2805
|
+
### Modal con Contenido Personalizado
|
|
2806
|
+
|
|
2807
|
+
```tsx
|
|
2808
|
+
<GatherModal
|
|
2809
|
+
open={isOpen}
|
|
2810
|
+
onClose={() => setIsOpen(false)}
|
|
2811
|
+
header={{
|
|
2812
|
+
children: (
|
|
2813
|
+
<div className='flex items-center gap-3'>
|
|
2814
|
+
<div className='flex h-8 w-8 items-center justify-center rounded-full bg-blue-500'>
|
|
2815
|
+
<span className='text-sm text-white'>!</span>
|
|
2816
|
+
</div>
|
|
2817
|
+
<div>
|
|
2818
|
+
<h2 className='text-lg font-semibold'>Advertencia</h2>
|
|
2819
|
+
<p className='text-sm text-gray-600'>Acción requerida</p>
|
|
2820
|
+
</div>
|
|
2821
|
+
</div>
|
|
2822
|
+
),
|
|
2823
|
+
showCloseButton: true,
|
|
2824
|
+
}}
|
|
2825
|
+
footer={{
|
|
2826
|
+
children: (
|
|
2827
|
+
<div className='flex w-full justify-between'>
|
|
2828
|
+
<span className='text-sm text-gray-500'>
|
|
2829
|
+
Última actualización: hace 5 min
|
|
2830
|
+
</span>
|
|
2831
|
+
<div className='flex gap-2'>
|
|
2832
|
+
<button className='rounded px-4 py-2 text-gray-600 hover:bg-gray-100'>
|
|
2833
|
+
Ignorar
|
|
2834
|
+
</button>
|
|
2835
|
+
<button className='rounded bg-red-500 px-4 py-2 text-white hover:bg-red-600'>
|
|
2836
|
+
Resolver
|
|
2837
|
+
</button>
|
|
2838
|
+
</div>
|
|
2839
|
+
</div>
|
|
2840
|
+
),
|
|
2841
|
+
}}
|
|
2842
|
+
>
|
|
2843
|
+
<p>Contenido del modal con header y footer personalizados.</p>
|
|
2844
|
+
</GatherModal>
|
|
2845
|
+
```
|
|
2846
|
+
|
|
2847
|
+
### Modal con Scroll Personalizado
|
|
2848
|
+
|
|
2849
|
+
```tsx
|
|
2850
|
+
<GatherModal
|
|
2851
|
+
open={isOpen}
|
|
2852
|
+
onClose={() => setIsOpen(false)}
|
|
2853
|
+
header={{
|
|
2854
|
+
title: 'Contenido Extenso',
|
|
2855
|
+
scrollable: true,
|
|
2856
|
+
maxHeight: '150px',
|
|
2857
|
+
}}
|
|
2858
|
+
scrollBehavior={{
|
|
2859
|
+
bodyScrollable: true,
|
|
2860
|
+
bodyMaxHeight: '400px',
|
|
2861
|
+
modalScrollable: false,
|
|
2862
|
+
}}
|
|
2863
|
+
footer={{
|
|
2864
|
+
scrollable: true,
|
|
2865
|
+
maxHeight: '100px',
|
|
2866
|
+
}}
|
|
2867
|
+
>
|
|
2868
|
+
<div className='space-y-4'>
|
|
2869
|
+
{Array.from({ length: 20 }, (_, i) => (
|
|
2870
|
+
<p key={i}>
|
|
2871
|
+
Párrafo {i + 1}: Lorem ipsum dolor sit amet consectetur adipisicing
|
|
2872
|
+
elit...
|
|
2873
|
+
</p>
|
|
2874
|
+
))}
|
|
2875
|
+
</div>
|
|
2876
|
+
</GatherModal>
|
|
2877
|
+
```
|
|
2878
|
+
|
|
2879
|
+
## ⚙️ Props Principales
|
|
2880
|
+
|
|
2881
|
+
### Props del Modal
|
|
2882
|
+
|
|
2883
|
+
```tsx
|
|
2884
|
+
interface GatherModalProps {
|
|
2885
|
+
// ESTADO
|
|
2886
|
+
open: boolean; // Si el modal está abierto
|
|
2887
|
+
onClose?: () => void; // Función de cierre
|
|
2888
|
+
|
|
2889
|
+
// DISEÑO
|
|
2890
|
+
position?: 'center' | 'left' | 'right'; // Posición
|
|
2891
|
+
size?: 'sm' | 'md' | 'lg' | 'xl' | 'full'; // Tamaño
|
|
2892
|
+
|
|
2893
|
+
// COMPORTAMIENTO
|
|
2894
|
+
closeOnOverlayClick?: boolean; // Cerrar al hacer clic fuera
|
|
2895
|
+
closeOnEscape?: boolean; // Cerrar con tecla Escape
|
|
2896
|
+
preventBodyScroll?: boolean; // Prevenir scroll del body
|
|
2897
|
+
autoFocus?: boolean; // Enfocar automáticamente
|
|
2898
|
+
|
|
2899
|
+
// CONTENIDO
|
|
2900
|
+
header?: GatherModalHeaderProps; // Configuración del header
|
|
2901
|
+
footer?: GatherModalFooterProps; // Configuración del footer
|
|
2902
|
+
children: React.ReactNode; // Contenido principal
|
|
2903
|
+
|
|
2904
|
+
// ACCESIBILIDAD Y ANIMACIÓN
|
|
2905
|
+
animationDuration?: number; // Duración de animaciones (ms)
|
|
2906
|
+
zIndex?: number; // Z-index base
|
|
2907
|
+
scrollBehavior?: ScrollConfig; // Configuración de scroll
|
|
2908
|
+
}
|
|
2909
|
+
```
|
|
2910
|
+
|
|
2911
|
+
### Props del Header
|
|
2912
|
+
|
|
2913
|
+
```tsx
|
|
2914
|
+
interface GatherModalHeaderProps {
|
|
2915
|
+
title?: string; // Título del modal
|
|
2916
|
+
subtitle?: string; // Subtítulo
|
|
2917
|
+
showCloseButton?: boolean; // Mostrar botón X
|
|
2918
|
+
onClose?: () => void; // Función del botón cerrar
|
|
2919
|
+
closeIcon?: React.ReactNode; // Ícono personalizado
|
|
2920
|
+
scrollable?: boolean; // Si el header hace scroll
|
|
2921
|
+
children?: React.ReactNode; // Contenido personalizado
|
|
2922
|
+
}
|
|
2923
|
+
```
|
|
2924
|
+
|
|
2925
|
+
### Props del Footer
|
|
2926
|
+
|
|
2927
|
+
```tsx
|
|
2928
|
+
interface GatherModalFooterProps {
|
|
2929
|
+
// BOTONES POR DEFECTO
|
|
2930
|
+
acceptText?: string; // Texto botón aceptar
|
|
2931
|
+
cancelText?: string; // Texto botón cancelar
|
|
2932
|
+
onAccept?: () => void; // Acción de aceptar
|
|
2933
|
+
onCancel?: () => void; // Acción de cancelar
|
|
2934
|
+
|
|
2935
|
+
// ESTADOS DE LOS BOTONES
|
|
2936
|
+
acceptLoading?: boolean; // Loading en botón aceptar
|
|
2937
|
+
acceptDisabled?: boolean; // Deshabilitar aceptar
|
|
2938
|
+
|
|
2939
|
+
// VARIANTES Y DISEÑO
|
|
2940
|
+
acceptVariant?: ButtonVariant; // Estilo del botón aceptar
|
|
2941
|
+
cancelVariant?: ButtonVariant; // Estilo del botón cancelar
|
|
2942
|
+
buttonsOrder?: 'normal' | 'reverse'; // Orden de botones
|
|
2943
|
+
buttonsAlignment?: 'start' | 'center' | 'end' | 'between';
|
|
2944
|
+
|
|
2945
|
+
// PERSONALIZACIÓN
|
|
2946
|
+
children?: React.ReactNode; // Footer completamente personalizado
|
|
2947
|
+
scrollable?: boolean; // Si el footer hace scroll
|
|
2948
|
+
}
|
|
2949
|
+
```
|
|
2950
|
+
|
|
2951
|
+
## 🎯 Casos de Uso Comunes
|
|
2952
|
+
|
|
2953
|
+
### Modal de Confirmación de Eliminación
|
|
2954
|
+
|
|
2955
|
+
```tsx
|
|
2956
|
+
const DeleteConfirmModal = ({ item, onConfirm, onCancel }) => (
|
|
2957
|
+
<GatherModal
|
|
2958
|
+
open={!!item}
|
|
2959
|
+
onClose={onCancel}
|
|
2960
|
+
size='sm'
|
|
2961
|
+
header={{
|
|
2962
|
+
title: 'Confirmar Eliminación',
|
|
2963
|
+
subtitle: '¿Estás seguro de eliminar este elemento?',
|
|
2964
|
+
}}
|
|
2965
|
+
footer={{
|
|
2966
|
+
acceptText: 'Eliminar',
|
|
2967
|
+
cancelText: 'Cancelar',
|
|
2968
|
+
acceptVariant: 'gather-error',
|
|
2969
|
+
onAccept: () => onConfirm(item),
|
|
2970
|
+
onCancel: onCancel,
|
|
2971
|
+
}}
|
|
2972
|
+
>
|
|
2973
|
+
<p>Esta acción no se puede deshacer.</p>
|
|
2974
|
+
{item && (
|
|
2975
|
+
<div className='mt-3 rounded bg-gray-50 p-3'>
|
|
2976
|
+
<strong>{item.name}</strong>
|
|
2977
|
+
</div>
|
|
2978
|
+
)}
|
|
2979
|
+
</GatherModal>
|
|
2980
|
+
);
|
|
2981
|
+
```
|
|
2982
|
+
|
|
2983
|
+
### Panel de Detalles Lateral
|
|
2984
|
+
|
|
2985
|
+
```tsx
|
|
2986
|
+
const DetailsSidebar = ({ user, isOpen, onClose }) => (
|
|
2987
|
+
<GatherModal
|
|
2988
|
+
open={isOpen}
|
|
2989
|
+
onClose={onClose}
|
|
2990
|
+
position='right'
|
|
2991
|
+
size='lg'
|
|
2992
|
+
header={{
|
|
2993
|
+
title: 'Detalles del Usuario',
|
|
2994
|
+
subtitle: user?.name,
|
|
2995
|
+
}}
|
|
2996
|
+
preventBodyScroll={true}
|
|
2997
|
+
>
|
|
2998
|
+
{user && (
|
|
2999
|
+
<div className='space-y-6'>
|
|
3000
|
+
<div className='text-center'>
|
|
3001
|
+
<img
|
|
3002
|
+
src={user.avatar}
|
|
3003
|
+
alt={user.name}
|
|
3004
|
+
className='mx-auto mb-4 h-20 w-20 rounded-full'
|
|
3005
|
+
/>
|
|
3006
|
+
<h3 className='text-xl font-semibold'>{user.name}</h3>
|
|
3007
|
+
<p className='text-gray-600'>{user.email}</p>
|
|
3008
|
+
</div>
|
|
3009
|
+
|
|
3010
|
+
<div className='space-y-4'>
|
|
3011
|
+
<div>
|
|
3012
|
+
<label className='font-medium'>Departamento</label>
|
|
3013
|
+
<p>{user.department}</p>
|
|
3014
|
+
</div>
|
|
3015
|
+
<div>
|
|
3016
|
+
<label className='font-medium'>Rol</label>
|
|
3017
|
+
<p>{user.role}</p>
|
|
3018
|
+
</div>
|
|
3019
|
+
</div>
|
|
3020
|
+
</div>
|
|
3021
|
+
)}
|
|
3022
|
+
</GatherModal>
|
|
3023
|
+
);
|
|
3024
|
+
```
|
|
3025
|
+
|
|
3026
|
+
Esta guía cubre los aspectos principales de GatherModal, proporcionando ejemplos prácticos para implementaciones comunes desde modales simples hasta paneles laterales complejos.
|
|
3027
|
+
|
|
3028
|
+
# 📅 Guía Completa de GatherDatePicker
|
|
3029
|
+
|
|
3030
|
+
## 🎯 Introducción
|
|
3031
|
+
|
|
3032
|
+
`GatherDatePicker` es un componente de selección de fechas flexible y robusto, basado en `react-day-picker`. Ofrece dos variantes principales: un botón que abre un popover (`GatherDatePicker`) y un input de texto (`GatherDatePickerInput`). Soporta selección de fechas individuales y rangos, zonas horarias, y validaciones.
|
|
3033
|
+
|
|
3034
|
+
## 📦 Uso Básico
|
|
3035
|
+
|
|
3036
|
+
### Selector de Fecha Simple (Botón)
|
|
3037
|
+
|
|
3038
|
+
```tsx
|
|
3039
|
+
import { GatherDatePicker } from '@/base/date-picker/gather-date-picker';
|
|
3040
|
+
|
|
3041
|
+
const SimpleDatePicker = () => {
|
|
3042
|
+
const [date, setDate] = React.useState<Date | undefined>(new Date());
|
|
3043
|
+
|
|
3044
|
+
return (
|
|
3045
|
+
<GatherDatePicker
|
|
3046
|
+
mode='single'
|
|
3047
|
+
selected={date}
|
|
3048
|
+
onSelect={setDate}
|
|
3049
|
+
placeholder='Seleccionar fecha'
|
|
3050
|
+
/>
|
|
3051
|
+
);
|
|
3052
|
+
};
|
|
3053
|
+
```
|
|
3054
|
+
|
|
3055
|
+
### Input de Fecha
|
|
3056
|
+
|
|
3057
|
+
```tsx
|
|
3058
|
+
import { GatherDatePickerInput } from '@/base/date-picker/gather-date-picker-input';
|
|
3059
|
+
|
|
3060
|
+
const InputDatePicker = () => {
|
|
3061
|
+
const [date, setDate] = React.useState<Date | undefined>();
|
|
3062
|
+
|
|
3063
|
+
return (
|
|
3064
|
+
<GatherDatePickerInput
|
|
3065
|
+
mode='single'
|
|
3066
|
+
selected={date}
|
|
3067
|
+
onSelect={setDate}
|
|
3068
|
+
label='Fecha de nacimiento'
|
|
3069
|
+
placeholder='Seleccione su fecha de nacimiento'
|
|
3070
|
+
/>
|
|
3071
|
+
);
|
|
3072
|
+
};
|
|
3073
|
+
```
|
|
3074
|
+
|
|
3075
|
+
## ⚙️ Props Principales
|
|
3076
|
+
|
|
3077
|
+
### GatherDatePickerProps & GatherDatePickerInputProps
|
|
3078
|
+
|
|
3079
|
+
Ambos componentes comparten la mayoría de las props, heredando de una base común:
|
|
3080
|
+
|
|
3081
|
+
```tsx
|
|
3082
|
+
interface GatherDatePickerProps {
|
|
3083
|
+
// CONFIGURACIÓN
|
|
3084
|
+
mode?: 'single' | 'range'; // Modo de selección
|
|
3085
|
+
selected?: Date | DateRange; // Valor seleccionado
|
|
3086
|
+
onSelect?: (date: any) => void; // Callback de selección
|
|
3087
|
+
timeZone?: string; // Zona horaria (default: 'America/Guayaquil')
|
|
3088
|
+
|
|
3089
|
+
// VISUAL
|
|
3090
|
+
placeholder?: string; // Texto placeholder
|
|
3091
|
+
disabled?: boolean; // Estado deshabilitado
|
|
3092
|
+
className?: string; // Clases adicionales
|
|
3093
|
+
|
|
3094
|
+
// RANGO Y RESTRICCIONES
|
|
3095
|
+
minDate?: Date; // Fecha mínima
|
|
3096
|
+
maxDate?: Date; // Fecha máxima
|
|
3097
|
+
disabledDates?: Date[]; // Fechas deshabilitadas específicas
|
|
3098
|
+
pastYearsCount?: number; // Años pasados a mostrar en selector
|
|
3099
|
+
|
|
3100
|
+
// FORMATO
|
|
3101
|
+
dateFormat?: string; // Formato de fecha (ej: 'dd MMM yyyy')
|
|
3102
|
+
rangeFromFormat?: string; // Formato inicio rango
|
|
3103
|
+
rangeToFormat?: string; // Formato fin rango
|
|
3104
|
+
|
|
3105
|
+
// COMPORTAMIENTO
|
|
3106
|
+
autoClose?: boolean; // Cerrar al seleccionar (single mode)
|
|
3107
|
+
|
|
3108
|
+
// VALIDACIÓN (NUEVO)
|
|
3109
|
+
error?: boolean; // Estado de error (borde rojo)
|
|
3110
|
+
helperText?: string; // Texto de ayuda/error
|
|
3111
|
+
required?: boolean; // Marca de campo requerido (*)
|
|
3112
|
+
}
|
|
3113
|
+
```
|
|
3114
|
+
|
|
3115
|
+
### Props Específicas de GatherDatePicker (Botón)
|
|
3116
|
+
|
|
3117
|
+
```tsx
|
|
3118
|
+
interface GatherDatePickerProps {
|
|
3119
|
+
// ... props comunes
|
|
3120
|
+
variant?: 'default' | 'outline' | 'ghost' | 'secondary'; // Variante del botón
|
|
3121
|
+
hideActionButtons?: boolean; // Ocultar botones Aplicar/Cancelar
|
|
3122
|
+
applyButtonText?: string; // Texto botón Aplicar
|
|
3123
|
+
cancelButtonText?: string; // Texto botón Cancelar
|
|
3124
|
+
showPresets?: boolean; // Mostrar panel de presets (ayer, hoy, etc.)
|
|
3125
|
+
presets?: DatePreset[]; // Presets personalizados
|
|
3126
|
+
}
|
|
3127
|
+
```
|
|
3128
|
+
|
|
3129
|
+
### Props Específicas de GatherDatePickerInput (Input)
|
|
3130
|
+
|
|
3131
|
+
```tsx
|
|
3132
|
+
interface GatherDatePickerInputProps {
|
|
3133
|
+
// ... props comunes
|
|
3134
|
+
label?: string; // Etiqueta del input
|
|
3135
|
+
description?: string; // Descripción bajo el label
|
|
3136
|
+
showSelectedDate?: boolean; // Mostrar texto de fecha seleccionada
|
|
3137
|
+
selectedDateLabel?: string; // Prefijo para fecha seleccionada
|
|
3138
|
+
}
|
|
3139
|
+
```
|
|
3140
|
+
|
|
3141
|
+
## 🚨 Manejo de Errores y Validaciones
|
|
3142
|
+
|
|
3143
|
+
Los componentes ahora soportan estados visuales de error y campos requeridos para integrarse mejor en formularios.
|
|
3144
|
+
|
|
3145
|
+
### Input con Error y Mensaje
|
|
3146
|
+
|
|
3147
|
+
```tsx
|
|
3148
|
+
<GatherDatePickerInput
|
|
3149
|
+
mode='single'
|
|
3150
|
+
label='Fecha de Facturación'
|
|
3151
|
+
required={true} // Muestra asterisco rojo *
|
|
3152
|
+
error={true} // Borde rojo en el input
|
|
3153
|
+
helperText='La fecha es obligatoria' // Muestra mensaje en rojo si hay error
|
|
3154
|
+
onSelect={setDate}
|
|
3155
|
+
selected={date}
|
|
3156
|
+
/>
|
|
3157
|
+
```
|
|
3158
|
+
|
|
3159
|
+
### Botón con Estado de Error
|
|
3160
|
+
|
|
3161
|
+
```tsx
|
|
3162
|
+
<GatherDatePicker
|
|
3163
|
+
mode='single'
|
|
3164
|
+
selected={date}
|
|
3165
|
+
onSelect={setDate}
|
|
3166
|
+
error={true} // El botón tendrá un borde rojo
|
|
3167
|
+
className='w-full'
|
|
3168
|
+
/>;
|
|
3169
|
+
{
|
|
3170
|
+
/* Puedes renderizar el texto de error manualmente si usas la versión de botón */
|
|
3171
|
+
}
|
|
3172
|
+
{
|
|
3173
|
+
hasError && (
|
|
3174
|
+
<p className='mt-1 text-xs text-red-500'>Seleccione una fecha válida</p>
|
|
3175
|
+
);
|
|
3176
|
+
}
|
|
3177
|
+
```
|
|
3178
|
+
|
|
3179
|
+
## 🌍 Zonas Horarias y Formatos
|
|
3180
|
+
|
|
3181
|
+
El componente maneja zonas horarias utilizando `date-fns-tz`. Por defecto usa `America/Guayaquil`.
|
|
3182
|
+
|
|
3183
|
+
```tsx
|
|
3184
|
+
<GatherDatePicker
|
|
3185
|
+
timeZone='Europe/Madrid' // Configurar zona horaria
|
|
3186
|
+
dateFormat='dd/MM/yyyy' // Configurar formato visual
|
|
3187
|
+
selected={date}
|
|
3188
|
+
onSelect={setDate}
|
|
3189
|
+
/>
|
|
3190
|
+
```
|