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.
- package/LICENSE +21 -0
- package/README.md +372 -0
- package/TABLER-LICENSE.txt +21 -0
- package/dist/accordion.d.ts +49 -0
- package/dist/accordion.js +14 -0
- package/dist/aspect-ratio.d.ts +8 -0
- package/dist/aspect-ratio.js +8 -0
- package/dist/avatar.d.ts +27 -0
- package/dist/avatar.js +15 -0
- package/dist/breadcrumbs.d.ts +20 -0
- package/dist/breadcrumbs.js +5 -0
- package/dist/button.d.ts +44 -0
- package/dist/button.js +25 -0
- package/dist/carousel.d.ts +19 -0
- package/dist/carousel.js +133 -0
- package/dist/choice-group.d.ts +36 -0
- package/dist/choice-group.js +27 -0
- package/dist/combobox.d.ts +37 -0
- package/dist/combobox.js +57 -0
- package/dist/command-palette.d.ts +28 -0
- package/dist/command-palette.js +90 -0
- package/dist/container.d.ts +21 -0
- package/dist/container.js +10 -0
- package/dist/context-menu.d.ts +21 -0
- package/dist/context-menu.js +50 -0
- package/dist/custom-select.d.ts +45 -0
- package/dist/custom-select.js +26 -0
- package/dist/data-table.d.ts +53 -0
- package/dist/data-table.js +104 -0
- package/dist/date-picker.d.ts +43 -0
- package/dist/date-picker.js +142 -0
- package/dist/display.d.ts +63 -0
- package/dist/display.js +43 -0
- package/dist/drawer.d.ts +12 -0
- package/dist/drawer.js +29 -0
- package/dist/dropdown-menu.d.ts +47 -0
- package/dist/dropdown-menu.js +24 -0
- package/dist/empty-state.d.ts +13 -0
- package/dist/empty-state.js +8 -0
- package/dist/fields.d.ts +62 -0
- package/dist/fields.js +70 -0
- package/dist/file-upload.d.ts +33 -0
- package/dist/file-upload.js +111 -0
- package/dist/index.d.ts +48 -0
- package/dist/index.js +48 -0
- package/dist/internal/field-open.d.ts +2 -0
- package/dist/internal/field-open.js +15 -0
- package/dist/internal/focus.d.ts +4 -0
- package/dist/internal/focus.js +27 -0
- package/dist/internal/icons.d.ts +29 -0
- package/dist/internal/icons.js +32 -0
- package/dist/internal/navigation-origin.d.ts +2 -0
- package/dist/internal/navigation-origin.js +23 -0
- package/dist/internal/overlay-layer.d.ts +12 -0
- package/dist/internal/overlay-layer.js +14 -0
- package/dist/internal/pagination.d.ts +6 -0
- package/dist/internal/pagination.js +36 -0
- package/dist/internal/scroll-hints.d.ts +8 -0
- package/dist/internal/scroll-hints.js +108 -0
- package/dist/internal/state.d.ts +2 -0
- package/dist/internal/state.js +13 -0
- package/dist/internal/trigger-label.d.ts +5 -0
- package/dist/internal/trigger-label.js +26 -0
- package/dist/loader.d.ts +27 -0
- package/dist/loader.js +13 -0
- package/dist/modal.d.ts +23 -0
- package/dist/modal.js +30 -0
- package/dist/multi-select.d.ts +23 -0
- package/dist/multi-select.js +63 -0
- package/dist/navigation-menu.d.ts +26 -0
- package/dist/navigation-menu.js +27 -0
- package/dist/number-input.d.ts +14 -0
- package/dist/number-input.js +37 -0
- package/dist/pagination.d.ts +24 -0
- package/dist/pagination.js +60 -0
- package/dist/popover.d.ts +30 -0
- package/dist/popover.js +27 -0
- package/dist/progress.d.ts +18 -0
- package/dist/progress.js +9 -0
- package/dist/radio-group.d.ts +46 -0
- package/dist/radio-group.js +16 -0
- package/dist/resizable-panels.d.ts +22 -0
- package/dist/resizable-panels.js +70 -0
- package/dist/skeleton.d.ts +17 -0
- package/dist/skeleton.js +9 -0
- package/dist/slider.d.ts +41 -0
- package/dist/slider.js +29 -0
- package/dist/stepper.d.ts +25 -0
- package/dist/stepper.js +25 -0
- package/dist/styles.css +3369 -0
- package/dist/tabs.d.ts +32 -0
- package/dist/tabs.js +8 -0
- package/dist/toast.d.ts +54 -0
- package/dist/toast.js +107 -0
- package/dist/toggle-group.d.ts +32 -0
- package/dist/toggle-group.js +16 -0
- package/dist/toggles.d.ts +33 -0
- package/dist/toggles.js +29 -0
- package/dist/tooltip.d.ts +26 -0
- package/dist/tooltip.js +16 -0
- package/dist/transition.d.ts +27 -0
- package/dist/transition.js +148 -0
- package/dist/tree-view.d.ts +22 -0
- package/dist/tree-view.js +128 -0
- package/dist/typography.d.ts +23 -0
- package/dist/typography.js +20 -0
- package/dist/utils.d.ts +13 -0
- package/dist/utils.js +17 -0
- package/package.json +73 -0
package/dist/progress.js
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { cx } from './utils.js';
|
|
3
|
+
export function Progress({ label, value, max = 100, showValue = false, valueText, size = 'md', className, }) {
|
|
4
|
+
const limit = Number.isFinite(max) && max > 0 ? max : 100;
|
|
5
|
+
const current = value === undefined || !Number.isFinite(value)
|
|
6
|
+
? undefined
|
|
7
|
+
: Math.max(0, Math.min(limit, value));
|
|
8
|
+
return (_jsxs("div", { className: cx('dgm-progress', `dgm-progress--${size}`, className), children: [_jsxs("div", { className: "dgm-progress-label", children: [_jsx("span", { children: label }), showValue && current !== undefined && (_jsxs("span", { "aria-hidden": "true", children: [Math.round((current / limit) * 100), "%"] }))] }), _jsx("progress", { "aria-label": label, "aria-valuetext": valueText, max: limit, value: current })] }));
|
|
9
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/** Selección única visible, con navegación por flechas y participación en formularios. */
|
|
2
|
+
import { type ReactNode } from 'react';
|
|
3
|
+
export interface RadioOption {
|
|
4
|
+
/** Valor único y no vacío que se envía al formulario. */
|
|
5
|
+
value: string;
|
|
6
|
+
/** Etiqueta visible: admite texto e iconos decorativos. */
|
|
7
|
+
label: ReactNode;
|
|
8
|
+
/** Ayuda de esta opción, vinculada al radio mediante ARIA. */
|
|
9
|
+
description?: ReactNode;
|
|
10
|
+
/** Desactiva solo esta alternativa. */
|
|
11
|
+
disabled?: boolean;
|
|
12
|
+
}
|
|
13
|
+
export interface RadioGroupProps {
|
|
14
|
+
/** Nombre visible y accesible del conjunto. */
|
|
15
|
+
label: ReactNode;
|
|
16
|
+
/** Alternativas en orden de lectura, con valores estables. */
|
|
17
|
+
options: readonly RadioOption[];
|
|
18
|
+
/** Valor controlado; no combinar con defaultValue. */
|
|
19
|
+
value?: string;
|
|
20
|
+
/** No selecciona ninguna opción por defecto. */
|
|
21
|
+
defaultValue?: string;
|
|
22
|
+
/** Notifica el valor seleccionado con teclado o puntero. */
|
|
23
|
+
onValueChange?: (value: string) => void;
|
|
24
|
+
/** Ayuda común del grupo. */
|
|
25
|
+
description?: ReactNode;
|
|
26
|
+
/** Error visible; activa aria-invalid, sin validar la lógica de negocio. */
|
|
27
|
+
error?: ReactNode;
|
|
28
|
+
/** Nombre de los radios de formulario que crea Radix. */
|
|
29
|
+
name?: string;
|
|
30
|
+
/** Exige una opción seleccionada en el formulario. @defaultValue false */
|
|
31
|
+
required?: boolean;
|
|
32
|
+
/** Impide interactuar con todo el grupo. @defaultValue false */
|
|
33
|
+
disabled?: boolean;
|
|
34
|
+
/** Distribución y dirección de navegación. @defaultValue 'vertical' */
|
|
35
|
+
orientation?: 'horizontal' | 'vertical';
|
|
36
|
+
/** Las flechas vuelven al principio al llegar al final. @defaultValue true */
|
|
37
|
+
loop?: boolean;
|
|
38
|
+
/** Identificador del grupo. Se genera con useId si se omite. */
|
|
39
|
+
id?: string;
|
|
40
|
+
/** Clase del contenedor con role radiogroup. */
|
|
41
|
+
className?: string;
|
|
42
|
+
/** Clase del conjunto formado por etiqueta, radios y mensajes. */
|
|
43
|
+
wrapperClassName?: string;
|
|
44
|
+
}
|
|
45
|
+
/** El ref apunta al grupo. Cada etiqueta amplía el área que permite elegir la opción. */
|
|
46
|
+
export declare const RadioGroup: import("react").ForwardRefExoticComponent<RadioGroupProps & import("react").RefAttributes<HTMLDivElement>>;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
/** Selección única visible, con navegación por flechas y participación en formularios. */
|
|
4
|
+
import { forwardRef, useId } from 'react';
|
|
5
|
+
import * as Primitive from '@radix-ui/react-radio-group';
|
|
6
|
+
import { cx, hasContent } from './utils.js';
|
|
7
|
+
/** El ref apunta al grupo. Cada etiqueta amplía el área que permite elegir la opción. */
|
|
8
|
+
export const RadioGroup = forwardRef(function RadioGroup({ label, options, value, defaultValue, onValueChange, description, error, name, required, disabled, orientation = 'vertical', loop = true, id, className, wrapperClassName, }, ref) {
|
|
9
|
+
const generatedId = useId();
|
|
10
|
+
const controlId = id ?? `dgm-radio-${generatedId}`;
|
|
11
|
+
const describedBy = cx(hasContent(description) && `${controlId}-description`, hasContent(error) && `${controlId}-error`) || undefined;
|
|
12
|
+
return (_jsxs("div", { className: cx('dgm-field', wrapperClassName), children: [_jsxs("span", { className: "dgm-label", id: `${controlId}-label`, children: [label, required && _jsx("span", { "aria-hidden": "true", children: " *" })] }), _jsx(Primitive.Root, { ref: ref, id: controlId, value: value, defaultValue: defaultValue, onValueChange: onValueChange, name: name, required: required, disabled: disabled, orientation: orientation, loop: loop, "aria-labelledby": `${controlId}-label`, "aria-describedby": describedBy, "aria-invalid": hasContent(error) || undefined, className: cx('dgm-radio-group', className), children: options.map((option, index) => {
|
|
13
|
+
const optionId = `${controlId}-option-${index}`;
|
|
14
|
+
return (_jsxs("label", { htmlFor: optionId, className: "dgm-radio-option", "data-disabled": disabled || option.disabled || undefined, children: [_jsx(Primitive.Item, { id: optionId, value: option.value, disabled: option.disabled, "aria-labelledby": `${optionId}-label`, "aria-describedby": hasContent(option.description) ? `${optionId}-description` : undefined, className: "dgm-radio-control", children: _jsx("span", { className: "dgm-radio-dot", "aria-hidden": "true" }) }), _jsxs("span", { className: "dgm-radio-copy", children: [_jsx("span", { id: `${optionId}-label`, children: option.label }), hasContent(option.description) && (_jsx("span", { className: "dgm-description", id: `${optionId}-description`, children: option.description }))] })] }, option.value));
|
|
15
|
+
}) }), hasContent(description) && (_jsx("span", { className: "dgm-description", id: `${controlId}-description`, children: description })), hasContent(error) && (_jsx("span", { className: "dgm-error", id: `${controlId}-error`, children: error }))] }));
|
|
16
|
+
});
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/** Dos paneles ajustables mediante Pointer Events o teclado, sin listeners globales ni tamaños en píxeles persistidos. */
|
|
2
|
+
import { type CSSProperties, type ReactElement, type ReactNode } from 'react';
|
|
3
|
+
export interface ResizablePanelsProps {
|
|
4
|
+
first: ReactNode;
|
|
5
|
+
second: ReactNode;
|
|
6
|
+
firstLabel: string;
|
|
7
|
+
secondLabel: string;
|
|
8
|
+
label: string;
|
|
9
|
+
/** Tamaño del primer panel como porcentaje del espacio útil, sin contar el separador. */
|
|
10
|
+
size?: number;
|
|
11
|
+
defaultSize?: number;
|
|
12
|
+
onSizeChange?: (size: number) => void;
|
|
13
|
+
minSize?: number;
|
|
14
|
+
maxSize?: number;
|
|
15
|
+
orientation?: 'horizontal' | 'vertical';
|
|
16
|
+
dir?: 'ltr' | 'rtl';
|
|
17
|
+
disabled?: boolean;
|
|
18
|
+
className?: string;
|
|
19
|
+
style?: CSSProperties;
|
|
20
|
+
}
|
|
21
|
+
/** Flechas mueven 1%, Shift+flecha 10%, Home/End alcanzan límites y Enter vuelve al tamaño inicial. */
|
|
22
|
+
export declare function ResizablePanels({ first, second, firstLabel, secondLabel, label, size, defaultSize, onSizeChange, minSize, maxSize, orientation, dir, disabled, className, style, }: ResizablePanelsProps): ReactElement;
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
/** Dos paneles ajustables mediante Pointer Events o teclado, sin listeners globales ni tamaños en píxeles persistidos. */
|
|
4
|
+
import { useId, useRef } from 'react';
|
|
5
|
+
import { useControllable } from './internal/state.js';
|
|
6
|
+
import { cx } from './utils.js';
|
|
7
|
+
/** Flechas mueven 1%, Shift+flecha 10%, Home/End alcanzan límites y Enter vuelve al tamaño inicial. */
|
|
8
|
+
export function ResizablePanels({ first, second, firstLabel, secondLabel, label, size, defaultSize = 50, onSizeChange, minSize = 15, maxSize = 85, orientation = 'horizontal', dir = 'ltr', disabled, className, style, }) {
|
|
9
|
+
if (![minSize, maxSize, defaultSize, size ?? 50].every(Number.isFinite) ||
|
|
10
|
+
minSize < 0 ||
|
|
11
|
+
maxSize > 100 ||
|
|
12
|
+
minSize >= maxSize)
|
|
13
|
+
throw new RangeError('ResizablePanels requires 0 <= minSize < maxSize <= 100.');
|
|
14
|
+
const [current, change] = useControllable(size, defaultSize, onSizeChange);
|
|
15
|
+
const root = useRef(null);
|
|
16
|
+
const dragging = useRef(false);
|
|
17
|
+
const id = useId();
|
|
18
|
+
const vertical = orientation === 'vertical';
|
|
19
|
+
const clamp = (value) => Math.min(maxSize, Math.max(minSize, value));
|
|
20
|
+
const actual = clamp(current);
|
|
21
|
+
const tracks = `minmax(0, ${actual}fr) 1rem minmax(0, ${100 - actual}fr)`;
|
|
22
|
+
return (_jsxs("div", { ref: root, dir: dir, className: cx('dgm-resizable', className), "data-orientation": orientation, style: { ...style, [vertical ? 'gridTemplateRows' : 'gridTemplateColumns']: tracks }, children: [_jsx("section", { id: `${id}-first`, "aria-label": firstLabel, className: "dgm-resizable-panel", children: first }), _jsx("div", { role: "separator", tabIndex: disabled ? -1 : 0, "aria-label": label, "aria-controls": `${id}-first`, "aria-orientation": vertical ? 'horizontal' : 'vertical', "aria-valuemin": minSize, "aria-valuemax": maxSize, "aria-valuenow": Math.round(actual), "aria-disabled": disabled || undefined, className: "dgm-resize-handle", onKeyDown: (event) => {
|
|
23
|
+
if (disabled)
|
|
24
|
+
return;
|
|
25
|
+
const forward = vertical ? 'ArrowDown' : dir === 'rtl' ? 'ArrowLeft' : 'ArrowRight';
|
|
26
|
+
const backward = vertical ? 'ArrowUp' : dir === 'rtl' ? 'ArrowRight' : 'ArrowLeft';
|
|
27
|
+
let next;
|
|
28
|
+
if (event.key === forward)
|
|
29
|
+
next = actual + (event.shiftKey ? 10 : 1);
|
|
30
|
+
else if (event.key === backward)
|
|
31
|
+
next = actual - (event.shiftKey ? 10 : 1);
|
|
32
|
+
else if (event.key === 'Home')
|
|
33
|
+
next = minSize;
|
|
34
|
+
else if (event.key === 'End')
|
|
35
|
+
next = maxSize;
|
|
36
|
+
else if (event.key === 'Enter')
|
|
37
|
+
next = defaultSize;
|
|
38
|
+
else
|
|
39
|
+
return;
|
|
40
|
+
event.preventDefault();
|
|
41
|
+
change(clamp(next));
|
|
42
|
+
}, onPointerDown: (event) => {
|
|
43
|
+
if (!disabled && event.button === 0) {
|
|
44
|
+
dragging.current = true;
|
|
45
|
+
event.currentTarget.setPointerCapture(event.pointerId);
|
|
46
|
+
event.currentTarget.focus({ preventScroll: true });
|
|
47
|
+
}
|
|
48
|
+
}, onPointerMove: (event) => {
|
|
49
|
+
if (!dragging.current || disabled || !root.current)
|
|
50
|
+
return;
|
|
51
|
+
const bounds = root.current.getBoundingClientRect();
|
|
52
|
+
const handle = event.currentTarget.getBoundingClientRect();
|
|
53
|
+
const available = vertical ? bounds.height - handle.height : bounds.width - handle.width;
|
|
54
|
+
if (available <= 0)
|
|
55
|
+
return;
|
|
56
|
+
const position = vertical
|
|
57
|
+
? event.clientY - bounds.top - handle.height / 2
|
|
58
|
+
: (dir === 'rtl' ? bounds.right - event.clientX : event.clientX - bounds.left) -
|
|
59
|
+
handle.width / 2;
|
|
60
|
+
change(clamp((position / available) * 100));
|
|
61
|
+
}, onPointerUp: (event) => {
|
|
62
|
+
dragging.current = false;
|
|
63
|
+
if (event.currentTarget.hasPointerCapture(event.pointerId))
|
|
64
|
+
event.currentTarget.releasePointerCapture(event.pointerId);
|
|
65
|
+
}, onPointerCancel: () => {
|
|
66
|
+
dragging.current = false;
|
|
67
|
+
}, onLostPointerCapture: () => {
|
|
68
|
+
dragging.current = false;
|
|
69
|
+
}, children: _jsx("span", { "aria-hidden": "true" }) }), _jsx("section", { "aria-label": secondLabel, className: "dgm-resizable-panel", children: second })] }));
|
|
70
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/** Marcador visual estático compatible con Server Components. No necesita hidratación. */
|
|
2
|
+
import type { CSSProperties, HTMLAttributes, ReactElement } from 'react';
|
|
3
|
+
export interface SkeletonProps extends Omit<HTMLAttributes<HTMLSpanElement>, 'children'> {
|
|
4
|
+
/** Forma del marcador. @defaultValue 'text' */
|
|
5
|
+
variant?: 'text' | 'rectangular' | 'circular';
|
|
6
|
+
/** Número en píxeles o unidad CSS. @defaultValue '100%' */
|
|
7
|
+
width?: CSSProperties['width'];
|
|
8
|
+
/** Altura CSS; text usa 1em y las otras formas 3rem por defecto. */
|
|
9
|
+
height?: CSSProperties['height'];
|
|
10
|
+
/** Pulso suave o marcador inmóvil. @defaultValue true */
|
|
11
|
+
animate?: boolean;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Siempre decorativo: el consumidor anuncia la carga una vez en su región con aria-busy.
|
|
15
|
+
* No repite «cargando» por cada línea. style puede afinar las dimensiones y la forma.
|
|
16
|
+
*/
|
|
17
|
+
export declare function Skeleton({ variant, width, height, animate, className, style, ...props }: SkeletonProps): ReactElement;
|
package/dist/skeleton.js
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { cx } from './utils.js';
|
|
3
|
+
/**
|
|
4
|
+
* Siempre decorativo: el consumidor anuncia la carga una vez en su región con aria-busy.
|
|
5
|
+
* No repite «cargando» por cada línea. style puede afinar las dimensiones y la forma.
|
|
6
|
+
*/
|
|
7
|
+
export function Skeleton({ variant = 'text', width = '100%', height, animate = true, className, style, ...props }) {
|
|
8
|
+
return (_jsx("span", { ...props, "aria-hidden": "true", className: cx('dgm-skeleton', `dgm-skeleton--${variant}`, animate && 'dgm-skeleton--animated'), style: { width, height: height ?? (variant === 'text' ? '1em' : '3rem'), ...style } }));
|
|
9
|
+
}
|
package/dist/slider.d.ts
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/** Radix mantiene teclado, gestos táctiles y valores de formulario; la librería aporta etiquetas y estilo. */
|
|
2
|
+
import { type ReactElement, type ReactNode } from 'react';
|
|
3
|
+
/** Opciones comunes de los selectores numéricos. min < max y step debe ser positivo. */
|
|
4
|
+
export interface SliderBaseProps {
|
|
5
|
+
label: ReactNode;
|
|
6
|
+
description?: ReactNode;
|
|
7
|
+
error?: ReactNode;
|
|
8
|
+
min?: number;
|
|
9
|
+
max?: number;
|
|
10
|
+
step?: number;
|
|
11
|
+
disabled?: boolean;
|
|
12
|
+
name?: string;
|
|
13
|
+
/** Valor visible y aria-valuetext; útil para porcentajes, unidades o precios. */
|
|
14
|
+
formatValue?: (value: number) => string;
|
|
15
|
+
/** Marcas informativas, sin interacción propia. Deben estar entre min y max. */
|
|
16
|
+
marks?: readonly {
|
|
17
|
+
value: number;
|
|
18
|
+
label?: ReactNode;
|
|
19
|
+
}[];
|
|
20
|
+
className?: string;
|
|
21
|
+
id?: string;
|
|
22
|
+
}
|
|
23
|
+
export interface SliderProps extends SliderBaseProps {
|
|
24
|
+
value?: number;
|
|
25
|
+
defaultValue?: number;
|
|
26
|
+
onValueChange?: (value: number) => void;
|
|
27
|
+
/** Se ejecuta al terminar el arrastre o un cambio de teclado. */
|
|
28
|
+
onValueCommit?: (value: number) => void;
|
|
29
|
+
}
|
|
30
|
+
export interface RangeSliderProps extends SliderBaseProps {
|
|
31
|
+
value?: [number, number];
|
|
32
|
+
defaultValue?: [number, number];
|
|
33
|
+
onValueChange?: (value: [number, number]) => void;
|
|
34
|
+
onValueCommit?: (value: [number, number]) => void;
|
|
35
|
+
/** Nombres de los extremos para lectores de pantalla. */
|
|
36
|
+
thumbLabels?: [string, string];
|
|
37
|
+
}
|
|
38
|
+
/** Un valor: flechas cambian por step y Home/End recorren los límites. Controla value para reset de formulario. */
|
|
39
|
+
export declare function Slider({ value, defaultValue, onValueChange, onValueCommit, ...props }: SliderProps): ReactElement;
|
|
40
|
+
/** Dos extremos ordenados. FormData.getAll(name) recoge ambos valores; pueden coincidir. */
|
|
41
|
+
export declare function RangeSlider({ value, defaultValue, onValueChange, onValueCommit, thumbLabels, ...props }: RangeSliderProps): ReactElement;
|
package/dist/slider.js
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
/** Radix mantiene teclado, gestos táctiles y valores de formulario; la librería aporta etiquetas y estilo. */
|
|
4
|
+
import { useId } from 'react';
|
|
5
|
+
import * as Primitive from '@radix-ui/react-slider';
|
|
6
|
+
import { useControllable } from './internal/state.js';
|
|
7
|
+
import { cx, hasContent } from './utils.js';
|
|
8
|
+
/** Infraestructura interna; cada API pública mantiene un número fijo de controles. */
|
|
9
|
+
function SliderControl({ label, description, error, min = 0, max = 100, step = 1, disabled, name, formatValue = String, marks = [], className, id, values, change, commit, thumbLabels, }) {
|
|
10
|
+
const generated = useId();
|
|
11
|
+
const controlId = id ?? `dgm-slider-${generated}`;
|
|
12
|
+
if (![min, max, step].every(Number.isFinite) || min >= max || step <= 0) {
|
|
13
|
+
throw new RangeError('Slider requires finite min < max and step > 0.');
|
|
14
|
+
}
|
|
15
|
+
const describedBy = cx(hasContent(description) && `${controlId}-description`, hasContent(error) && `${controlId}-error`) || undefined;
|
|
16
|
+
return (_jsxs("div", { className: cx('dgm-field', 'dgm-slider-field', className), id: controlId, children: [_jsxs("div", { className: "dgm-slider-heading", children: [_jsx("span", { className: "dgm-label", id: `${controlId}-label`, children: label }), _jsx("span", { className: "dgm-description", "aria-hidden": "true", children: values.map(formatValue).join(' – ') })] }), _jsxs(Primitive.Root, { className: "dgm-slider", value: values, onValueChange: change, onValueCommit: commit, min: min, max: max, step: step, disabled: disabled, name: name, children: [_jsx(Primitive.Track, { className: "dgm-slider-track", children: _jsx(Primitive.Range, { className: "dgm-slider-range" }) }), values.map((value, index) => (_jsx(Primitive.Thumb, { name: name, className: "dgm-slider-thumb", "aria-labelledby": `${controlId}-label${thumbLabels ? ` ${controlId}-thumb-${index}` : ''}`, "aria-describedby": describedBy, "aria-invalid": hasContent(error) || undefined, "aria-valuetext": formatValue(value) }, index)))] }), thumbLabels?.map((text, index) => (_jsx("span", { id: `${controlId}-thumb-${index}`, className: "dgm-visually-hidden", children: text }, index))), marks.length > 0 && (_jsx("div", { className: "dgm-slider-marks", "aria-hidden": "true", children: marks
|
|
17
|
+
.filter((mark) => mark.value >= min && mark.value <= max)
|
|
18
|
+
.map((mark) => (_jsx("span", { style: { left: `${(100 * (mark.value - min)) / (max - min)}%` }, children: mark.label ?? formatValue(mark.value) }, mark.value))) })), hasContent(description) && (_jsx("span", { id: `${controlId}-description`, className: "dgm-description", children: description })), hasContent(error) && (_jsx("span", { id: `${controlId}-error`, className: "dgm-error", children: error }))] }));
|
|
19
|
+
}
|
|
20
|
+
/** Un valor: flechas cambian por step y Home/End recorren los límites. Controla value para reset de formulario. */
|
|
21
|
+
export function Slider({ value, defaultValue, onValueChange, onValueCommit, ...props }) {
|
|
22
|
+
const [current, change] = useControllable(value, defaultValue ?? props.min ?? 0, onValueChange);
|
|
23
|
+
return (_jsx(SliderControl, { ...props, values: [current], change: (next) => change(next[0]), commit: (next) => onValueCommit?.(next[0]) }));
|
|
24
|
+
}
|
|
25
|
+
/** Dos extremos ordenados. FormData.getAll(name) recoge ambos valores; pueden coincidir. */
|
|
26
|
+
export function RangeSlider({ value, defaultValue, onValueChange, onValueCommit, thumbLabels = ['Minimum', 'Maximum'], ...props }) {
|
|
27
|
+
const [current, change] = useControllable(value, defaultValue ?? [props.min ?? 0, props.max ?? 100], onValueChange);
|
|
28
|
+
return (_jsx(SliderControl, { ...props, values: current, thumbLabels: thumbLabels, change: (next) => change([next[0], next[1]]), commit: (next) => onValueCommit?.([next[0], next[1]]) }));
|
|
29
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { ReactElement, ReactNode } from 'react';
|
|
2
|
+
export interface StepItem {
|
|
3
|
+
id: string;
|
|
4
|
+
title: ReactNode;
|
|
5
|
+
description?: ReactNode;
|
|
6
|
+
disabled?: boolean;
|
|
7
|
+
}
|
|
8
|
+
export interface StepperProps {
|
|
9
|
+
steps: readonly StepItem[];
|
|
10
|
+
/** ID del paso actual. */
|
|
11
|
+
value: string;
|
|
12
|
+
/** Si se omite, presenta el progreso sin botones de navegación. */
|
|
13
|
+
onValueChange?: (id: string) => void;
|
|
14
|
+
/** Impide saltar a pasos futuros desde el índice. @defaultValue true */
|
|
15
|
+
linear?: boolean;
|
|
16
|
+
/** @defaultValue 'horizontal' */
|
|
17
|
+
orientation?: 'horizontal' | 'vertical';
|
|
18
|
+
/** Nombre accesible de la navegación. */
|
|
19
|
+
label: string;
|
|
20
|
+
/** Si es true, todos los pasos aparecen completados. */
|
|
21
|
+
completed?: boolean;
|
|
22
|
+
locale?: 'es' | 'en';
|
|
23
|
+
className?: string;
|
|
24
|
+
}
|
|
25
|
+
export declare function Stepper({ steps, value, onValueChange, linear, orientation, label, completed, locale, className, }: StepperProps): ReactElement;
|
package/dist/stepper.js
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
3
|
+
/** Índice de un proceso controlado. La validación y el contenido de los pasos pertenecen al consumidor. */
|
|
4
|
+
import { CheckIcon } from './internal/icons.js';
|
|
5
|
+
import { cx } from './utils.js';
|
|
6
|
+
export function Stepper({ steps, value, onValueChange, linear = true, orientation = 'horizontal', label, completed = false, locale = 'en', className, }) {
|
|
7
|
+
const active = steps.findIndex((step) => step.id === value);
|
|
8
|
+
const es = locale === 'es';
|
|
9
|
+
return (_jsx("nav", { "aria-label": label, className: cx('dgm-stepper', className), "data-orientation": orientation, children: _jsx("ol", { children: steps.map((step, index) => {
|
|
10
|
+
const done = completed || index < active;
|
|
11
|
+
const current = !completed && step.id === value;
|
|
12
|
+
const content = (_jsxs(_Fragment, { children: [_jsx("span", { className: "dgm-step-number", "aria-hidden": "true", children: done ? _jsx(CheckIcon, {}) : index + 1 }), _jsxs("span", { className: "dgm-step-copy", children: [_jsx("span", { children: step.title }), step.description && _jsx("span", { className: "dgm-description", children: step.description }), _jsx("span", { className: "dgm-visually-hidden", children: done
|
|
13
|
+
? es
|
|
14
|
+
? 'Completado'
|
|
15
|
+
: 'Completed'
|
|
16
|
+
: current
|
|
17
|
+
? es
|
|
18
|
+
? 'Actual'
|
|
19
|
+
: 'Current'
|
|
20
|
+
: es
|
|
21
|
+
? 'Pendiente'
|
|
22
|
+
: 'Pending' })] })] }));
|
|
23
|
+
return (_jsx("li", { "data-complete": done || undefined, "data-current": current || undefined, children: onValueChange ? (_jsx("button", { type: "button", className: "dgm-step", disabled: step.disabled || (linear && !completed && index > active), "aria-current": current ? 'step' : undefined, onClick: () => onValueChange(step.id), children: content })) : (_jsx("div", { className: "dgm-step", "aria-current": current ? 'step' : undefined, children: content })) }, step.id));
|
|
24
|
+
}) }) }));
|
|
25
|
+
}
|