danielgm-ui 0.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.
Files changed (109) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +372 -0
  3. package/TABLER-LICENSE.txt +21 -0
  4. package/dist/accordion.d.ts +49 -0
  5. package/dist/accordion.js +14 -0
  6. package/dist/aspect-ratio.d.ts +8 -0
  7. package/dist/aspect-ratio.js +8 -0
  8. package/dist/avatar.d.ts +27 -0
  9. package/dist/avatar.js +15 -0
  10. package/dist/breadcrumbs.d.ts +20 -0
  11. package/dist/breadcrumbs.js +5 -0
  12. package/dist/button.d.ts +44 -0
  13. package/dist/button.js +25 -0
  14. package/dist/carousel.d.ts +19 -0
  15. package/dist/carousel.js +133 -0
  16. package/dist/choice-group.d.ts +36 -0
  17. package/dist/choice-group.js +27 -0
  18. package/dist/combobox.d.ts +37 -0
  19. package/dist/combobox.js +57 -0
  20. package/dist/command-palette.d.ts +28 -0
  21. package/dist/command-palette.js +90 -0
  22. package/dist/container.d.ts +21 -0
  23. package/dist/container.js +10 -0
  24. package/dist/context-menu.d.ts +21 -0
  25. package/dist/context-menu.js +50 -0
  26. package/dist/custom-select.d.ts +45 -0
  27. package/dist/custom-select.js +26 -0
  28. package/dist/data-table.d.ts +53 -0
  29. package/dist/data-table.js +104 -0
  30. package/dist/date-picker.d.ts +43 -0
  31. package/dist/date-picker.js +142 -0
  32. package/dist/display.d.ts +63 -0
  33. package/dist/display.js +43 -0
  34. package/dist/drawer.d.ts +12 -0
  35. package/dist/drawer.js +29 -0
  36. package/dist/dropdown-menu.d.ts +47 -0
  37. package/dist/dropdown-menu.js +24 -0
  38. package/dist/empty-state.d.ts +13 -0
  39. package/dist/empty-state.js +8 -0
  40. package/dist/fields.d.ts +62 -0
  41. package/dist/fields.js +70 -0
  42. package/dist/file-upload.d.ts +33 -0
  43. package/dist/file-upload.js +111 -0
  44. package/dist/index.d.ts +48 -0
  45. package/dist/index.js +48 -0
  46. package/dist/internal/field-open.d.ts +2 -0
  47. package/dist/internal/field-open.js +15 -0
  48. package/dist/internal/focus.d.ts +4 -0
  49. package/dist/internal/focus.js +27 -0
  50. package/dist/internal/icons.d.ts +29 -0
  51. package/dist/internal/icons.js +32 -0
  52. package/dist/internal/navigation-origin.d.ts +2 -0
  53. package/dist/internal/navigation-origin.js +23 -0
  54. package/dist/internal/overlay-layer.d.ts +12 -0
  55. package/dist/internal/overlay-layer.js +14 -0
  56. package/dist/internal/pagination.d.ts +6 -0
  57. package/dist/internal/pagination.js +36 -0
  58. package/dist/internal/scroll-hints.d.ts +8 -0
  59. package/dist/internal/scroll-hints.js +108 -0
  60. package/dist/internal/state.d.ts +2 -0
  61. package/dist/internal/state.js +13 -0
  62. package/dist/internal/trigger-label.d.ts +5 -0
  63. package/dist/internal/trigger-label.js +26 -0
  64. package/dist/loader.d.ts +27 -0
  65. package/dist/loader.js +13 -0
  66. package/dist/modal.d.ts +23 -0
  67. package/dist/modal.js +30 -0
  68. package/dist/multi-select.d.ts +23 -0
  69. package/dist/multi-select.js +63 -0
  70. package/dist/navigation-menu.d.ts +26 -0
  71. package/dist/navigation-menu.js +27 -0
  72. package/dist/number-input.d.ts +14 -0
  73. package/dist/number-input.js +37 -0
  74. package/dist/pagination.d.ts +24 -0
  75. package/dist/pagination.js +60 -0
  76. package/dist/popover.d.ts +30 -0
  77. package/dist/popover.js +27 -0
  78. package/dist/progress.d.ts +18 -0
  79. package/dist/progress.js +9 -0
  80. package/dist/radio-group.d.ts +46 -0
  81. package/dist/radio-group.js +16 -0
  82. package/dist/resizable-panels.d.ts +22 -0
  83. package/dist/resizable-panels.js +70 -0
  84. package/dist/skeleton.d.ts +17 -0
  85. package/dist/skeleton.js +9 -0
  86. package/dist/slider.d.ts +41 -0
  87. package/dist/slider.js +29 -0
  88. package/dist/stepper.d.ts +25 -0
  89. package/dist/stepper.js +25 -0
  90. package/dist/styles.css +3369 -0
  91. package/dist/tabs.d.ts +32 -0
  92. package/dist/tabs.js +8 -0
  93. package/dist/toast.d.ts +54 -0
  94. package/dist/toast.js +107 -0
  95. package/dist/toggle-group.d.ts +32 -0
  96. package/dist/toggle-group.js +16 -0
  97. package/dist/toggles.d.ts +33 -0
  98. package/dist/toggles.js +29 -0
  99. package/dist/tooltip.d.ts +26 -0
  100. package/dist/tooltip.js +16 -0
  101. package/dist/transition.d.ts +27 -0
  102. package/dist/transition.js +148 -0
  103. package/dist/tree-view.d.ts +22 -0
  104. package/dist/tree-view.js +128 -0
  105. package/dist/typography.d.ts +23 -0
  106. package/dist/typography.js +20 -0
  107. package/dist/utils.d.ts +13 -0
  108. package/dist/utils.js +17 -0
  109. package/package.json +73 -0
@@ -0,0 +1,62 @@
1
+ /**
2
+ * Campos de texto y selección con un contrato común de etiquetas, ayuda y errores.
3
+ * Este módulo agrupa la infraestructura compartida y sus tres controles nativos.
4
+ * Ninguno almacena el valor: el navegador o el componente consumidor lo controla.
5
+ */
6
+ import { type InputHTMLAttributes, type TextareaHTMLAttributes, type SelectHTMLAttributes, type ReactNode } from 'react';
7
+ /** Opciones de presentación compartidas por Input, Textarea y Select. */
8
+ interface FieldProps {
9
+ /** Etiqueta visible. Debe aportar un nombre comprensible, aunque se admita ReactNode. */
10
+ label: ReactNode;
11
+ /** Ayuda opcional que se vincula al control mediante aria-describedby. */
12
+ description?: ReactNode;
13
+ /** Contenido de error. Marca el campo como inválido; no ejecuta validación por sí solo. */
14
+ error?: ReactNode;
15
+ /** Clase del contenedor; className se aplica al input, textarea o select. */
16
+ wrapperClassName?: string;
17
+ }
18
+ /** Props nativas de input. El ancho se define con CSS; la prop HTML size se excluye. */
19
+ export interface InputProps extends Omit<InputHTMLAttributes<HTMLInputElement>, 'size'>, FieldProps {
20
+ /** Visibilidad controlada de type="password"; no altera el valor del campo. */
21
+ visible?: boolean;
22
+ /** Visibilidad inicial no controlada. @defaultValue false */
23
+ defaultVisible?: boolean;
24
+ /** Solicita el cambio de visibilidad al pulsar el ojo. */
25
+ onVisibleChange?: (visible: boolean) => void;
26
+ /** Idioma del botón de contraseña. @defaultValue 'en' */
27
+ locale?: 'es' | 'en';
28
+ }
29
+ /** Props nativas de textarea; rows utiliza 4 como altura inicial por defecto. */
30
+ export interface TextareaProps extends TextareaHTMLAttributes<HTMLTextAreaElement>, FieldProps {
31
+ }
32
+ /**
33
+ * Props nativas de select, incluidas multiple, value, defaultValue y sus opciones hijas.
34
+ * @deprecated Usa CustomSelectProps para nuevas selecciones únicas. Se conserva por compatibilidad.
35
+ */
36
+ export interface SelectProps extends SelectHTMLAttributes<HTMLSelectElement>, FieldProps {
37
+ }
38
+ /**
39
+ * Entrada nativa con etiqueta asociada y ref al HTMLInputElement.
40
+ * Admite value/onChange (controlado) o defaultValue (no controlado).
41
+ * Un error propio fuerza aria-invalid; sin error se respeta el valor del consumidor.
42
+ *
43
+ * @example
44
+ * <Input label="Email" type="email" name="email" autoComplete="email" />
45
+ */
46
+ export declare const Input: import("react").ForwardRefExoticComponent<InputProps & import("react").RefAttributes<HTMLInputElement>>;
47
+ /**
48
+ * Entrada de varias líneas con ref al HTMLTextAreaElement y altura inicial de 4 filas.
49
+ * Conserva maxLength, required, name y los eventos nativos. El CSS permite redimensionar
50
+ * verticalmente sin desbordar el contenedor en pantallas pequeñas.
51
+ */
52
+ export declare const Textarea: import("react").ForwardRefExoticComponent<TextareaProps & import("react").RefAttributes<HTMLTextAreaElement>>;
53
+ /**
54
+ * Selector nativo con ref al HTMLSelectElement.
55
+ * children debe contener option u optgroup; se conserva el selector del sistema en
56
+ * móvil y su comportamiento de teclado. No se reemplaza por un menú personalizado.
57
+ * @deprecated Usa CustomSelect en código nuevo. Migra children a options y onChange a
58
+ * onValueChange. CustomSelect no admite multiple, optgroup ni ref de HTMLSelectElement;
59
+ * esos casos pueden seguir usando Select mientras no exista una alternativa equivalente.
60
+ */
61
+ export declare const Select: import("react").ForwardRefExoticComponent<SelectProps & import("react").RefAttributes<HTMLSelectElement>>;
62
+ export {};
package/dist/fields.js ADDED
@@ -0,0 +1,70 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ /**
4
+ * Campos de texto y selección con un contrato común de etiquetas, ayuda y errores.
5
+ * Este módulo agrupa la infraestructura compartida y sus tres controles nativos.
6
+ * Ninguno almacena el valor: el navegador o el componente consumidor lo controla.
7
+ */
8
+ import { forwardRef, useId, } from 'react';
9
+ import { cx, hasContent } from './utils.js';
10
+ import { useControllable } from './internal/state.js';
11
+ import { EyeIcon, EyeOffIcon } from './internal/icons.js';
12
+ /**
13
+ * Construye el ID del control y los enlaces con su ayuda y error.
14
+ * Conserva las descripciones que ya haya definido el consumidor y añade las propias.
15
+ * useId se llama siempre, incluso si llega un id, para mantener el orden de hooks.
16
+ * React puede reproducir estos IDs al hidratar si el árbol de servidor y cliente coincide.
17
+ */
18
+ function useField(id, description, error, describedBy) {
19
+ const generatedId = useId();
20
+ const fieldId = id ?? `dgm-${generatedId}`;
21
+ return {
22
+ id: fieldId,
23
+ 'aria-describedby': cx(describedBy, hasContent(description) && `${fieldId}-description`, hasContent(error) && `${fieldId}-error`) || undefined,
24
+ };
25
+ }
26
+ /**
27
+ * Estructura visual interna. El control se recibe en children para reutilizar la
28
+ * asociación de etiquetas sin sustituir la semántica de cada elemento nativo.
29
+ * El asterisco es decorativo: required se aplica también al control correspondiente.
30
+ */
31
+ function Field({ id, label, description, error, wrapperClassName, required, children, }) {
32
+ return (_jsxs("div", { className: cx('dgm-field', wrapperClassName), children: [_jsxs("label", { className: "dgm-label", htmlFor: id, children: [label, required && _jsx("span", { "aria-hidden": "true", children: " *" })] }), children, hasContent(description) && (_jsx("span", { className: "dgm-description", id: `${id}-description`, children: description })), hasContent(error) && (_jsx("span", { className: "dgm-error", id: `${id}-error`, children: error }))] }));
33
+ }
34
+ /**
35
+ * Entrada nativa con etiqueta asociada y ref al HTMLInputElement.
36
+ * Admite value/onChange (controlado) o defaultValue (no controlado).
37
+ * Un error propio fuerza aria-invalid; sin error se respeta el valor del consumidor.
38
+ *
39
+ * @example
40
+ * <Input label="Email" type="email" name="email" autoComplete="email" />
41
+ */
42
+ export const Input = forwardRef(function Input({ label, description, error, wrapperClassName, className, id, required, type = 'text', disabled, visible, defaultVisible = false, onVisibleChange, locale = 'en', 'aria-describedby': describedBy, 'aria-invalid': invalid, ...props }, ref) {
43
+ const field = useField(id, description, error, describedBy);
44
+ const [shown, changeVisibility] = useControllable(visible, defaultVisible, onVisibleChange);
45
+ const password = type === 'password';
46
+ // El mismo nodo input conserva valor, ref y validación al cambiar su presentación.
47
+ const control = (_jsx("input", { ...props, ...field, ref: ref, required: required, disabled: disabled, type: password && shown ? 'text' : type, "aria-invalid": hasContent(error) ? true : invalid, className: cx('dgm-input', password && 'dgm-input--password', className) }));
48
+ return (_jsx(Field, { label, description, error, wrapperClassName, required, id: field.id, children: _jsxs("div", { className: "dgm-input-control", children: [control, password && (_jsx("button", { type: "button", className: "dgm-password-toggle", disabled: disabled, "aria-label": locale === 'es' ? 'Mostrar contraseña' : 'Show password', "aria-pressed": shown, "aria-controls": field.id, onClick: () => changeVisibility(!shown), children: shown ? _jsx(EyeOffIcon, {}) : _jsx(EyeIcon, {}) }))] }) }));
49
+ });
50
+ /**
51
+ * Entrada de varias líneas con ref al HTMLTextAreaElement y altura inicial de 4 filas.
52
+ * Conserva maxLength, required, name y los eventos nativos. El CSS permite redimensionar
53
+ * verticalmente sin desbordar el contenedor en pantallas pequeñas.
54
+ */
55
+ export const Textarea = forwardRef(function Textarea({ label, description, error, wrapperClassName, className, id, required, rows = 4, 'aria-describedby': describedBy, 'aria-invalid': invalid, ...props }, ref) {
56
+ const field = useField(id, description, error, describedBy);
57
+ return (_jsx(Field, { label, description, error, wrapperClassName, required, id: field.id, children: _jsx("textarea", { ...props, ...field, ref: ref, required: required, rows: rows, "aria-invalid": hasContent(error) ? true : invalid, className: cx('dgm-input', 'dgm-textarea', className) }) }));
58
+ });
59
+ /**
60
+ * Selector nativo con ref al HTMLSelectElement.
61
+ * children debe contener option u optgroup; se conserva el selector del sistema en
62
+ * móvil y su comportamiento de teclado. No se reemplaza por un menú personalizado.
63
+ * @deprecated Usa CustomSelect en código nuevo. Migra children a options y onChange a
64
+ * onValueChange. CustomSelect no admite multiple, optgroup ni ref de HTMLSelectElement;
65
+ * esos casos pueden seguir usando Select mientras no exista una alternativa equivalente.
66
+ */
67
+ export const Select = forwardRef(function Select({ label, description, error, wrapperClassName, className, id, required, children, multiple, size, 'aria-describedby': describedBy, 'aria-invalid': invalid, ...props }, ref) {
68
+ const field = useField(id, description, error, describedBy);
69
+ return (_jsx(Field, { label, description, error, wrapperClassName, required, id: field.id, children: _jsx("div", { className: cx('dgm-select-control', !multiple && (size === undefined || size <= 1) && 'dgm-select-control--dropdown'), children: _jsx("select", { ...props, ...field, ref: ref, multiple: multiple, size: size, required: required, "aria-invalid": hasContent(error) ? true : invalid, className: cx('dgm-input', 'dgm-select', className), children: children }) }) }));
70
+ });
@@ -0,0 +1,33 @@
1
+ /** Selección local de archivos. No sube datos: entrega File[] para que el consumidor decida el transporte. */
2
+ import { type ReactElement, type ReactNode } from 'react';
3
+ export interface FileRejection {
4
+ file: File;
5
+ reason: 'type' | 'size' | 'count' | 'duplicate';
6
+ }
7
+ export interface FileUploadProps {
8
+ label: ReactNode;
9
+ /** Archivos controlados; no se pueden precargar en el input nativo por razones del navegador. */
10
+ value?: readonly File[];
11
+ onValueChange?: (files: File[]) => void;
12
+ /** Acepta tipos MIME, comodines image/* o extensiones, separados por comas. */
13
+ accept?: string;
14
+ /** @defaultValue false */
15
+ multiple?: boolean;
16
+ /** Límite total al acumular selecciones. @defaultValue 5 */
17
+ maxFiles?: number;
18
+ /** Bytes máximos por archivo. @defaultValue 10485760 */
19
+ maxSize?: number;
20
+ /** Notifica los archivos rechazados junto a su causa. */
21
+ onReject?: (rejections: FileRejection[]) => void;
22
+ description?: ReactNode;
23
+ error?: ReactNode;
24
+ disabled?: boolean;
25
+ /** Exige al menos un archivo antes del envío del formulario. */
26
+ required?: boolean;
27
+ /** Miniaturas locales de imágenes. Las URLs se revocan al retirar o desmontar. @defaultValue true */
28
+ preview?: boolean;
29
+ locale?: 'es' | 'en';
30
+ className?: string;
31
+ }
32
+ /** El mismo filtrado se aplica al diálogo nativo y al arrastre; nunca se envían archivos en esta capa. */
33
+ export declare function FileUpload({ label, value, onValueChange, accept, multiple, maxFiles, maxSize, onReject, description, error, disabled, required, preview, locale, className, }: FileUploadProps): ReactElement;
@@ -0,0 +1,111 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ /** Selección local de archivos. No sube datos: entrega File[] para que el consumidor decida el transporte. */
4
+ import { useEffect, useId, useRef, useState } from 'react';
5
+ import { Button } from './button.js';
6
+ import { useControllable } from './internal/state.js';
7
+ import { CloseIcon } from './internal/icons.js';
8
+ import { cx, hasContent } from './utils.js';
9
+ /** accept usa la sintaxis del input nativo y se comprueba también al arrastrar. */
10
+ function matchesType(file, accept) {
11
+ if (!accept)
12
+ return true;
13
+ return accept.split(',').some((raw) => {
14
+ const type = raw.trim().toLowerCase();
15
+ return type.startsWith('.')
16
+ ? file.name.toLowerCase().endsWith(type)
17
+ : type.endsWith('/*')
18
+ ? file.type.toLowerCase().startsWith(type.slice(0, -1))
19
+ : file.type.toLowerCase() === type;
20
+ });
21
+ }
22
+ /** Evita acumular una misma selección sin leer el contenido completo del archivo. */
23
+ function sameFile(a, b) {
24
+ return (a.name === b.name && a.size === b.size && a.lastModified === b.lastModified && a.type === b.type);
25
+ }
26
+ /** Una URL temporal por miniatura; el cleanup evita conservar archivos retirados en memoria. */
27
+ function Preview({ file }) {
28
+ const [url, setUrl] = useState();
29
+ useEffect(() => {
30
+ if (!file.type.startsWith('image/') || !URL.createObjectURL)
31
+ return;
32
+ const next = URL.createObjectURL(file);
33
+ setUrl(next);
34
+ return () => URL.revokeObjectURL(next);
35
+ }, [file]);
36
+ return url ? _jsx("img", { className: "dgm-file-preview", src: url, alt: "" }) : null;
37
+ }
38
+ /** El mismo filtrado se aplica al diálogo nativo y al arrastre; nunca se envían archivos en esta capa. */
39
+ export function FileUpload({ label, value, onValueChange, accept, multiple = false, maxFiles = 5, maxSize = 10485760, onReject, description, error, disabled, required, preview = true, locale = 'en', className, }) {
40
+ const [files, change] = useControllable(value, [], onValueChange ? (next) => onValueChange([...next]) : undefined);
41
+ const [rejections, setRejections] = useState([]);
42
+ const [dragging, setDragging] = useState(false);
43
+ const [missing, setMissing] = useState(false);
44
+ const input = useRef(null);
45
+ const button = useRef(null);
46
+ const id = useId();
47
+ const es = locale === 'es';
48
+ const maxCount = multiple
49
+ ? Number.isFinite(maxFiles)
50
+ ? Math.max(1, Math.floor(maxFiles))
51
+ : 5
52
+ : 1;
53
+ const sizeLimit = Number.isFinite(maxSize) && maxSize >= 0 ? maxSize : 10485760;
54
+ const reasons = {
55
+ type: es ? 'Tipo no admitido' : 'Unsupported type',
56
+ size: es ? 'Archivo demasiado grande' : 'File too large',
57
+ count: es ? 'Límite de archivos alcanzado' : 'File limit reached',
58
+ duplicate: es ? 'Archivo ya añadido' : 'File already added',
59
+ };
60
+ const message = error || (missing ? (es ? 'Añade al menos un archivo.' : 'Add at least one file.') : undefined);
61
+ function add(incoming) {
62
+ if (disabled)
63
+ return;
64
+ const accepted = multiple ? [...files] : [];
65
+ const rejected = [];
66
+ for (const file of incoming) {
67
+ const reason = !matchesType(file, accept)
68
+ ? 'type'
69
+ : file.size > sizeLimit
70
+ ? 'size'
71
+ : accepted.some((existing) => sameFile(existing, file))
72
+ ? 'duplicate'
73
+ : accepted.length >= maxCount
74
+ ? 'count'
75
+ : undefined;
76
+ if (reason)
77
+ rejected.push({ file, reason });
78
+ else
79
+ accepted.push(file);
80
+ }
81
+ if (accepted.length) {
82
+ change(accepted);
83
+ setMissing(false);
84
+ }
85
+ setRejections(rejected);
86
+ if (rejected.length)
87
+ onReject?.(rejected);
88
+ }
89
+ return (_jsxs("div", { className: cx('dgm-file-upload', className), children: [_jsxs("span", { className: "dgm-label", id: `${id}-label`, children: [label, required && _jsx("span", { "aria-hidden": "true", children: " *" })] }), _jsx("input", { ref: input, type: "file", className: "dgm-visually-hidden", tabIndex: -1, "aria-hidden": "true", accept: accept, multiple: multiple, disabled: disabled, onChange: (event) => {
90
+ add(Array.from(event.target.files ?? []));
91
+ event.target.value = '';
92
+ } }), _jsx("input", { className: "dgm-form-value", tabIndex: -1, "aria-hidden": "true", required: required, disabled: disabled, value: files.length ? 'selected' : '', onChange: () => { }, onInvalid: (event) => {
93
+ event.preventDefault();
94
+ setMissing(true);
95
+ button.current?.focus({ preventScroll: true });
96
+ } }), _jsxs("div", { className: "dgm-file-dropzone", "data-dragging": dragging || undefined, "data-disabled": disabled || undefined, onDragOver: (event) => {
97
+ event.preventDefault();
98
+ if (!disabled)
99
+ setDragging(true);
100
+ }, onDragLeave: (event) => {
101
+ if (!event.currentTarget.contains(event.relatedTarget))
102
+ setDragging(false);
103
+ }, onDrop: (event) => {
104
+ event.preventDefault();
105
+ setDragging(false);
106
+ add(Array.from(event.dataTransfer.files));
107
+ }, children: [_jsx("p", { children: es ? 'Arrastra tus archivos aquí' : 'Drop your files here' }), _jsx(Button, { ref: button, variant: "outline", disabled: disabled, "aria-labelledby": `${id}-choose ${id}-label`, "aria-describedby": cx(hasContent(description) && `${id}-description`, hasContent(message) && `${id}-error`) || undefined, "aria-invalid": hasContent(message) || undefined, onClick: () => input.current?.click(), children: _jsx("span", { id: `${id}-choose`, children: es ? 'Seleccionar archivos' : 'Choose files' }) })] }), hasContent(description) && (_jsx("p", { className: "dgm-description", id: `${id}-description`, children: description })), hasContent(message) && (_jsx("p", { className: "dgm-error", id: `${id}-error`, children: message })), _jsx("ul", { className: "dgm-file-list", "aria-labelledby": `${id}-label`, children: files.map((file, index) => (_jsxs("li", { children: [preview && _jsx(Preview, { file: file }), _jsxs("span", { className: "dgm-file-name", children: [file.name, _jsxs("small", { children: [Math.ceil(file.size / 1024), " KB"] })] }), _jsx(Button, { iconOnly: true, variant: "ghost", disabled: disabled, "aria-label": `${es ? 'Quitar' : 'Remove'} ${file.name}`, onClick: () => {
108
+ change(files.filter((_, position) => position !== index));
109
+ button.current?.focus({ preventScroll: true });
110
+ }, children: _jsx(CloseIcon, {}) })] }, `${file.name}-${file.size}-${file.lastModified}`))) }), _jsxs("div", { role: "status", className: "dgm-description", children: [files.length, " ", es ? 'archivos seleccionados' : 'files selected', rejections.map((item, index) => (_jsxs("p", { children: [item.file.name, ": ", reasons[item.reason]] }, index)))] })] }));
111
+ }
@@ -0,0 +1,48 @@
1
+ /**
2
+ * API pública de danielgm-ui: componentes y tipos disponibles desde el paquete npm.
3
+ * Mantener este barrel sin `use client` permite usar las piezas estáticas en servidor.
4
+ * Los controles interactivos declaran su frontera en sus propios módulos.
5
+ * Las extensiones .js son intencionadas: TypeScript emitirá módulos ESM para NodeNext.
6
+ * El CSS se importa por separado desde `danielgm-ui/styles.css`.
7
+ */
8
+ export { Button, type ButtonProps } from './button.js';
9
+ export { Typography, type TypographyProps, type TypographyVariant, type TypographyElement, type TypographyTone, } from './typography.js';
10
+ export { Slider, RangeSlider, type SliderProps, type RangeSliderProps, type SliderBaseProps, } from './slider.js';
11
+ export { MultiSelect, type MultiSelectProps } from './multi-select.js';
12
+ export { NumberInput, type NumberInputProps } from './number-input.js';
13
+ export { ToggleGroup, type ToggleGroupProps, type ToggleOption } from './toggle-group.js';
14
+ export { EmptyState, type EmptyStateProps } from './empty-state.js';
15
+ export { NavigationMenu, type NavigationMenuProps, type NavigationMenuItem, type NavigationLink, } from './navigation-menu.js';
16
+ export { AspectRatio, type AspectRatioProps } from './aspect-ratio.js';
17
+ export { ResizablePanels, type ResizablePanelsProps } from './resizable-panels.js';
18
+ export { TreeView, type TreeViewProps, type TreeNode } from './tree-view.js';
19
+ export { Carousel, type CarouselProps, type CarouselItem } from './carousel.js';
20
+ export { Container, type ContainerProps, type ContainerElement, type ContainerSize, } from './container.js';
21
+ export { Popover, type PopoverProps } from './popover.js';
22
+ export { Drawer, Sheet, type DrawerProps, type SheetProps } from './drawer.js';
23
+ export { Combobox, Autocomplete, type ComboboxProps, type ComboboxOption, type AutocompleteProps, } from './combobox.js';
24
+ export { Pagination, type PaginationProps } from './pagination.js';
25
+ export { Breadcrumbs, type BreadcrumbsProps, type BreadcrumbItem } from './breadcrumbs.js';
26
+ export { Avatar, type AvatarProps, type AvatarStatus } from './avatar.js';
27
+ export { Progress, type ProgressProps } from './progress.js';
28
+ export { CheckboxGroup, SwitchGroup, type CheckboxGroupProps, type SwitchGroupProps, type ChoiceGroupOption, } from './choice-group.js';
29
+ export { DatePicker, DateRangePicker, type DatePickerProps, type DateRangePickerProps, type DateRangeValue, type DatePickerBaseProps, } from './date-picker.js';
30
+ export { DataTable, type DataTableProps, type DataColumn, type DataTableSort, } from './data-table.js';
31
+ export { FileUpload, type FileUploadProps, type FileRejection } from './file-upload.js';
32
+ export { CommandPalette, type CommandPaletteProps, type CommandAction } from './command-palette.js';
33
+ export { Stepper, type StepperProps, type StepItem } from './stepper.js';
34
+ export { DropdownMenu, type DropdownMenuProps, type DropdownMenuItem } from './dropdown-menu.js';
35
+ export { ContextMenu, type ContextMenuProps, type ContextMenuItem } from './context-menu.js';
36
+ export { RadioGroup, type RadioGroupProps, type RadioOption } from './radio-group.js';
37
+ export { Skeleton, type SkeletonProps } from './skeleton.js';
38
+ export { Loader, type LoaderProps } from './loader.js';
39
+ export { ToastProvider, useToast, type ToastProviderProps, type ToastOptions, type ToastContextValue, } from './toast.js';
40
+ export { CustomSelect, type CustomSelectProps, type SelectOption } from './custom-select.js';
41
+ export { Modal, type ModalProps } from './modal.js';
42
+ export { Tooltip, type TooltipProps } from './tooltip.js';
43
+ export { Transition, type TransitionProps } from './transition.js';
44
+ export { Tabs, type TabsProps, type TabItem } from './tabs.js';
45
+ export { Accordion, type AccordionProps, type AccordionItem, type AccordionSingleProps, type AccordionMultipleProps, } from './accordion.js';
46
+ export { Input, Textarea, Select, type InputProps, type TextareaProps, type SelectProps, } from './fields.js';
47
+ export { Checkbox, Switch, type CheckboxProps, type SwitchProps } from './toggles.js';
48
+ export { Badge, Card, CardHeader, CardContent, CardFooter, Alert, Separator, type BadgeProps, type CardProps, type AlertProps, type SeparatorProps, type Tone, } from './display.js';
package/dist/index.js ADDED
@@ -0,0 +1,48 @@
1
+ /**
2
+ * API pública de danielgm-ui: componentes y tipos disponibles desde el paquete npm.
3
+ * Mantener este barrel sin `use client` permite usar las piezas estáticas en servidor.
4
+ * Los controles interactivos declaran su frontera en sus propios módulos.
5
+ * Las extensiones .js son intencionadas: TypeScript emitirá módulos ESM para NodeNext.
6
+ * El CSS se importa por separado desde `danielgm-ui/styles.css`.
7
+ */
8
+ export { Button } from './button.js';
9
+ export { Typography, } from './typography.js';
10
+ export { Slider, RangeSlider, } from './slider.js';
11
+ export { MultiSelect } from './multi-select.js';
12
+ export { NumberInput } from './number-input.js';
13
+ export { ToggleGroup } from './toggle-group.js';
14
+ export { EmptyState } from './empty-state.js';
15
+ export { NavigationMenu, } from './navigation-menu.js';
16
+ export { AspectRatio } from './aspect-ratio.js';
17
+ export { ResizablePanels } from './resizable-panels.js';
18
+ export { TreeView } from './tree-view.js';
19
+ export { Carousel } from './carousel.js';
20
+ export { Container, } from './container.js';
21
+ export { Popover } from './popover.js';
22
+ export { Drawer, Sheet } from './drawer.js';
23
+ export { Combobox, Autocomplete, } from './combobox.js';
24
+ export { Pagination } from './pagination.js';
25
+ export { Breadcrumbs } from './breadcrumbs.js';
26
+ export { Avatar } from './avatar.js';
27
+ export { Progress } from './progress.js';
28
+ export { CheckboxGroup, SwitchGroup, } from './choice-group.js';
29
+ export { DatePicker, DateRangePicker, } from './date-picker.js';
30
+ export { DataTable, } from './data-table.js';
31
+ export { FileUpload } from './file-upload.js';
32
+ export { CommandPalette } from './command-palette.js';
33
+ export { Stepper } from './stepper.js';
34
+ export { DropdownMenu } from './dropdown-menu.js';
35
+ export { ContextMenu } from './context-menu.js';
36
+ export { RadioGroup } from './radio-group.js';
37
+ export { Skeleton } from './skeleton.js';
38
+ export { Loader } from './loader.js';
39
+ export { ToastProvider, useToast, } from './toast.js';
40
+ export { CustomSelect } from './custom-select.js';
41
+ export { Modal } from './modal.js';
42
+ export { Tooltip } from './tooltip.js';
43
+ export { Transition } from './transition.js';
44
+ export { Tabs } from './tabs.js';
45
+ export { Accordion, } from './accordion.js';
46
+ export { Input, Textarea, Select, } from './fields.js';
47
+ export { Checkbox, Switch } from './toggles.js';
48
+ export { Badge, Card, CardHeader, CardContent, CardFooter, Alert, Separator, } from './display.js';
@@ -0,0 +1,2 @@
1
+ /** Cierra un campo al desactivarlo y evita que reaparezca al volver a habilitarlo. */
2
+ export declare function useFieldOpen(disabled?: boolean): readonly [boolean, (next: boolean) => void];
@@ -0,0 +1,15 @@
1
+ 'use client';
2
+ import { useEffect, useState } from 'react';
3
+ /** Cierra un campo al desactivarlo y evita que reaparezca al volver a habilitarlo. */
4
+ export function useFieldOpen(disabled = false) {
5
+ const [open, setOpen] = useState(false);
6
+ useEffect(() => {
7
+ if (disabled)
8
+ setOpen(false);
9
+ }, [disabled]);
10
+ function changeOpen(next) {
11
+ setOpen(!disabled && next);
12
+ }
13
+ // El panel deja de ser interactivo en el mismo render que recibe disabled.
14
+ return [!disabled && open, changeOpen];
15
+ }
@@ -0,0 +1,4 @@
1
+ /** Permite a Tooltip ignorar únicamente el foco restaurado sobre su propio disparador. */
2
+ export declare function isRestoringFocus(element: HTMLElement): boolean;
3
+ /** Devuelve el foco sin desplazar la página ni reabrir ayudas contextuales. */
4
+ export declare function restoreFocus(element: HTMLElement | null | undefined): void;
@@ -0,0 +1,27 @@
1
+ 'use client';
2
+ /**
3
+ * Distingue el retorno del foco al cerrar una capa de una nueva interacción.
4
+ * La marca solo existe durante focus(), que despacha el evento de forma síncrona.
5
+ * No requiere timers, listeners globales ni estado en el componente consumidor.
6
+ */
7
+ const restoringFocus = new WeakSet();
8
+ /** Permite a Tooltip ignorar únicamente el foco restaurado sobre su propio disparador. */
9
+ export function isRestoringFocus(element) {
10
+ return restoringFocus.has(element);
11
+ }
12
+ /** Devuelve el foco sin desplazar la página ni reabrir ayudas contextuales. */
13
+ export function restoreFocus(element) {
14
+ if (!element?.isConnected)
15
+ return;
16
+ const alreadyRestoring = restoringFocus.has(element);
17
+ restoringFocus.add(element);
18
+ try {
19
+ element.focus({ preventScroll: true });
20
+ }
21
+ finally {
22
+ // También se limpia si el destino no admite foco o focus() produce una excepción.
23
+ // Una llamada anidada sobre el mismo elemento conserva la marca de la exterior.
24
+ if (!alreadyRestoring)
25
+ restoringFocus.delete(element);
26
+ }
27
+ }
@@ -0,0 +1,29 @@
1
+ /** Iconos decorativos de Tabler. El nombre accesible pertenece al control exterior.
2
+ * Los imports explícitos permiten descartar los iconos no utilizados al empaquetar.
3
+ */
4
+ import type { ReactElement } from 'react';
5
+ import { type IconProps } from '@tabler/icons-react';
6
+ export declare const CloseIcon: (props: IconProps) => ReactElement;
7
+ export declare const ChevronIcon: (props: IconProps) => ReactElement;
8
+ export declare const ChevronUpIcon: (props: IconProps) => ReactElement;
9
+ export declare const ChevronLeftIcon: (props: IconProps) => ReactElement;
10
+ export declare const ChevronRightIcon: (props: IconProps) => ReactElement;
11
+ export declare const CheckIcon: (props: IconProps) => ReactElement;
12
+ export declare const CalendarIcon: (props: IconProps) => ReactElement;
13
+ export declare const PlusIcon: (props: IconProps) => ReactElement;
14
+ export declare const MinusIcon: (props: IconProps) => ReactElement;
15
+ export declare const ExpandIcon: (props: IconProps) => ReactElement;
16
+ export declare const CollapseIcon: (props: IconProps) => ReactElement;
17
+ export declare const FolderIcon: (props: IconProps) => ReactElement;
18
+ export declare const FolderOpenIcon: (props: IconProps) => ReactElement;
19
+ export declare const FileIcon: (props: IconProps) => ReactElement;
20
+ export declare const SearchIcon: (props: IconProps) => ReactElement;
21
+ export declare const EyeIcon: (props: IconProps) => ReactElement;
22
+ export declare const EyeOffIcon: (props: IconProps) => ReactElement;
23
+ export declare const SuccessIcon: (props: IconProps) => ReactElement;
24
+ export declare const InfoIcon: (props: IconProps) => ReactElement;
25
+ export declare const WarningIcon: (props: IconProps) => ReactElement;
26
+ export declare const DangerIcon: (props: IconProps) => ReactElement;
27
+ export declare const ArrowUpIcon: (props: IconProps) => ReactElement;
28
+ export declare const ArrowDownIcon: (props: IconProps) => ReactElement;
29
+ export declare const SortIcon: (props: IconProps) => ReactElement;
@@ -0,0 +1,32 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { IconX, IconChevronDown, IconChevronUp, IconChevronLeft, IconChevronRight, IconCheck, IconCalendar, IconPlus, IconMinus, IconSquarePlus, IconSquareMinus, IconFolder, IconFolderOpen, IconFile, IconSearch, IconEye, IconEyeOff, IconCircleCheck, IconInfoCircle, IconAlertTriangle, IconAlertCircle, IconArrowUp, IconArrowDown, IconArrowsSort, } from '@tabler/icons-react';
3
+ /** Tamaño común y color heredado; no introduce foco ni nombres accesibles duplicados. */
4
+ function decorative(Icon) {
5
+ return function DecorativeIcon(props) {
6
+ return _jsx(Icon, { size: 18, stroke: 2, "aria-hidden": "true", focusable: "false", ...props });
7
+ };
8
+ }
9
+ export const CloseIcon = decorative(IconX);
10
+ export const ChevronIcon = decorative(IconChevronDown);
11
+ export const ChevronUpIcon = decorative(IconChevronUp);
12
+ export const ChevronLeftIcon = decorative(IconChevronLeft);
13
+ export const ChevronRightIcon = decorative(IconChevronRight);
14
+ export const CheckIcon = decorative(IconCheck);
15
+ export const CalendarIcon = decorative(IconCalendar);
16
+ export const PlusIcon = decorative(IconPlus);
17
+ export const MinusIcon = decorative(IconMinus);
18
+ export const ExpandIcon = decorative(IconSquarePlus);
19
+ export const CollapseIcon = decorative(IconSquareMinus);
20
+ export const FolderIcon = decorative(IconFolder);
21
+ export const FolderOpenIcon = decorative(IconFolderOpen);
22
+ export const FileIcon = decorative(IconFile);
23
+ export const SearchIcon = decorative(IconSearch);
24
+ export const EyeIcon = decorative(IconEye);
25
+ export const EyeOffIcon = decorative(IconEyeOff);
26
+ export const SuccessIcon = decorative(IconCircleCheck);
27
+ export const InfoIcon = decorative(IconInfoCircle);
28
+ export const WarningIcon = decorative(IconAlertTriangle);
29
+ export const DangerIcon = decorative(IconAlertCircle);
30
+ export const ArrowUpIcon = decorative(IconArrowUp);
31
+ export const ArrowDownIcon = decorative(IconArrowDown);
32
+ export const SortIcon = decorative(IconArrowsSort);
@@ -0,0 +1,2 @@
1
+ /** NavigationMenu no usa Popper: situamos el origen en el borde más próximo a su disparador. */
2
+ export declare function observeNavigationOrigin(panel: HTMLElement, trigger: HTMLElement): () => void;
@@ -0,0 +1,23 @@
1
+ /** NavigationMenu no usa Popper: situamos el origen en el borde más próximo a su disparador. */
2
+ export function observeNavigationOrigin(panel, trigger) {
3
+ const container = panel.offsetParent instanceof HTMLElement
4
+ ? panel.offsetParent
5
+ : panel.closest('.dgm-navigation');
6
+ if (!container)
7
+ return () => { };
8
+ const update = () => {
9
+ const anchor = trigger.getBoundingClientRect();
10
+ const parent = container.getBoundingClientRect();
11
+ // offsetLeft no incluye el scale de la animación en curso; medir el panel con
12
+ // getBoundingClientRect desplazaría el propio origen a medida que crece.
13
+ const left = parent.left + container.clientLeft - container.scrollLeft + panel.offsetLeft;
14
+ const x = Math.min(panel.offsetWidth, Math.max(0, anchor.left + anchor.width / 2 - left));
15
+ panel.style.setProperty('--dgm-navigation-origin-x', `${x}px`);
16
+ };
17
+ const resize = new ResizeObserver(update);
18
+ resize.observe(container);
19
+ resize.observe(trigger);
20
+ resize.observe(panel);
21
+ update();
22
+ return () => resize.disconnect();
23
+ }
@@ -0,0 +1,12 @@
1
+ /** La profundidad React atraviesa los portales sin cambiar su destino ni su geometría. */
2
+ import { type CSSProperties, type ReactNode } from 'react';
3
+ /** Cada panel pertenece a la siguiente capa del panel que contiene su disparador. */
4
+ export declare function useOverlayLayer(): {
5
+ depth: number;
6
+ style: CSSProperties;
7
+ };
8
+ /** Sin contenedor DOM: los portales siguen escapando del overflow y transform del padre. */
9
+ export declare function OverlayLayerProvider({ depth, children }: {
10
+ depth: number;
11
+ children: ReactNode;
12
+ }): import("react").JSX.Element;
@@ -0,0 +1,14 @@
1
+ 'use client';
2
+ import { jsx as _jsx } from "react/jsx-runtime";
3
+ /** La profundidad React atraviesa los portales sin cambiar su destino ni su geometría. */
4
+ import { createContext, useContext } from 'react';
5
+ const OverlayDepth = createContext(-1);
6
+ /** Cada panel pertenece a la siguiente capa del panel que contiene su disparador. */
7
+ export function useOverlayLayer() {
8
+ const depth = useContext(OverlayDepth) + 1;
9
+ return { depth, style: { '--dgm-layer-depth': depth } };
10
+ }
11
+ /** Sin contenedor DOM: los portales siguen escapando del overflow y transform del padre. */
12
+ export function OverlayLayerProvider({ depth, children }) {
13
+ return _jsx(OverlayDepth.Provider, { value: depth, children: children });
14
+ }
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Construye una ventana para un presupuesto de celdas (números y elipsis).
3
+ * Conserva la página actual y, cuando caben, los extremos. Cerca del principio
4
+ * o del final desplaza la ventana para aprovechar también el espacio sobrante.
5
+ */
6
+ export declare function fitPaginationPages(current: number, count: number, slots: number): number[];
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Construye una ventana para un presupuesto de celdas (números y elipsis).
3
+ * Conserva la página actual y, cuando caben, los extremos. Cerca del principio
4
+ * o del final desplaza la ventana para aprovechar también el espacio sobrante.
5
+ */
6
+ export function fitPaginationPages(current, count, slots) {
7
+ const budget = Math.max(1, Math.floor(slots));
8
+ if (count === 0)
9
+ return [];
10
+ if (count <= budget)
11
+ return Array.from({ length: count }, (_, index) => index + 1);
12
+ for (let length = budget; length >= 1; length--) {
13
+ const start = Math.max(1, Math.min(count - length + 1, current - Math.floor((length - 1) / 2)));
14
+ const pages = new Set(Array.from({ length }, (_, index) => start + index));
15
+ // En móviles muy estrechos priorizamos los vecinos y la página actual.
16
+ if (budget >= 5) {
17
+ pages.add(1);
18
+ pages.add(count);
19
+ }
20
+ const ordered = [...pages].sort((left, right) => left - right);
21
+ const expanded = [];
22
+ let gaps = 0;
23
+ ordered.forEach((page, index) => {
24
+ const distance = index > 0 ? page - ordered[index - 1] : 0;
25
+ // Un único número omitido ocupa lo mismo que una elipsis y aporta más información.
26
+ if (distance === 2)
27
+ expanded.push(page - 1);
28
+ else if (distance > 2)
29
+ gaps++;
30
+ expanded.push(page);
31
+ });
32
+ if (expanded.length + gaps <= budget)
33
+ return expanded;
34
+ }
35
+ return [current];
36
+ }
@@ -0,0 +1,8 @@
1
+ import { type ReactNode } from 'react';
2
+ /** Decoración interna: el primer hijo conserva el scroll, su semántica y su foco.
3
+ * Observamos tamaño y contenido porque buscar, traducir o cargar opciones puede
4
+ * cambiar el desbordamiento sin que el usuario desplace la lista. */
5
+ export declare function ScrollHints({ children, className }: {
6
+ children: ReactNode;
7
+ className?: string;
8
+ }): import("react").JSX.Element;