danielgm-ui 0.4.1 → 0.4.3

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.
@@ -8,6 +8,8 @@ export interface ContextMenuProps {
8
8
  children: ReactElement;
9
9
  /** Acciones y separadores con IDs únicos. Los atajos son leyendas, no se registran. */
10
10
  items: readonly ContextMenuItem[];
11
+ /** Apariencia de las opciones; no modifica el panel. @defaultValue 'default' */
12
+ itemVariant?: 'default' | 'ghost';
11
13
  /** Encabezado visible y nombre accesible del menú. */
12
14
  label: string;
13
15
  /** Desactiva únicamente el menú personalizado y conserva el menú nativo. @defaultValue false */
@@ -18,4 +20,4 @@ export interface ContextMenuProps {
18
20
  className?: string;
19
21
  }
20
22
  /** Clic derecho, pulsación prolongada o Shift+F10. El clic izquierdo conserva la acción del hijo. */
21
- export declare function ContextMenu({ children, items, label, disabled, onOpenChange, className, }: ContextMenuProps): ReactElement;
23
+ export declare function ContextMenu({ children, items, itemVariant, label, disabled, onOpenChange, className, }: ContextMenuProps): ReactElement;
@@ -3,11 +3,12 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  /** Menú contextual sobre una zona concreta. Radix gestiona el anclaje al puntero y la navegación. */
4
4
  import { useId, useRef, useState } from 'react';
5
5
  import * as Primitive from '@radix-ui/react-context-menu';
6
+ import { menuItemFocusProps } from './internal/menu-item-focus.js';
6
7
  import { restoreFocus } from './internal/focus.js';
7
8
  import { OverlayLayerProvider, useOverlayLayer } from './internal/overlay-layer.js';
8
9
  import { cx } from './utils.js';
9
10
  /** Clic derecho, pulsación prolongada o Shift+F10. El clic izquierdo conserva la acción del hijo. */
10
- export function ContextMenu({ children, items, label, disabled = false, onOpenChange, className, }) {
11
+ export function ContextMenu({ children, items, itemVariant = 'default', label, disabled = false, onOpenChange, className, }) {
11
12
  const { depth, style: layerStyle } = useOverlayLayer();
12
13
  const trigger = useRef(null);
13
14
  const outside = useRef(false);
@@ -34,7 +35,7 @@ export function ContextMenu({ children, items, label, disabled = false, onOpenCh
34
35
  clientX: rect.left + 8,
35
36
  clientY: rect.bottom,
36
37
  }));
37
- }, children: children }), _jsx(Primitive.Portal, { children: _jsxs(Primitive.Content, { style: layerStyle, id: menuId, "aria-label": label, collisionPadding: 12, className: cx('dgm-dropdown', 'dgm-context-menu', className), onContextMenu: (event) => {
38
+ }, children: children }), _jsx(Primitive.Portal, { children: _jsxs(Primitive.Content, { ...menuItemFocusProps, style: layerStyle, id: menuId, "aria-label": label, collisionPadding: 12, className: cx('dgm-dropdown', 'dgm-context-menu', className), onContextMenu: (event) => {
38
39
  // El panel ya es el menú contextual: evita superponer el del navegador
39
40
  // o propagar el gesto a otra zona contextual a través del portal.
40
41
  event.preventDefault();
@@ -46,5 +47,5 @@ export function ContextMenu({ children, items, label, disabled = false, onOpenCh
46
47
  if (!outside.current && trigger.current?.isConnected)
47
48
  restoreFocus(trigger.current);
48
49
  outside.current = false;
49
- }, children: [_jsx(Primitive.Label, { className: "dgm-dropdown-label", children: label }), items.map((item) => item.type === 'separator' ? (_jsx(Primitive.Separator, { className: "dgm-dropdown-separator" }, item.id)) : (_jsxs(Primitive.Item, { textValue: item.label, disabled: item.disabled, onSelect: item.onSelect, className: cx('dgm-dropdown-item', item.danger && 'dgm-dropdown-item--danger'), children: [item.icon && (_jsx("span", { className: "dgm-option-icon", "aria-hidden": "true", children: item.icon })), _jsx("span", { className: "dgm-dropdown-text", children: item.label }), item.shortcut && (_jsx("span", { className: "dgm-dropdown-shortcut", "aria-hidden": "true", children: item.shortcut }))] }, item.id)))] }) })] }) }));
50
+ }, children: [_jsx(Primitive.Label, { className: "dgm-dropdown-label", children: label }), items.map((item) => item.type === 'separator' ? (_jsx(Primitive.Separator, { className: "dgm-dropdown-separator" }, item.id)) : (_jsxs(Primitive.Item, { textValue: item.label, disabled: item.disabled, onSelect: item.onSelect, className: cx('dgm-dropdown-item', (item.variant ?? itemVariant) === 'ghost' && 'dgm-dropdown-item--ghost', item.danger && 'dgm-dropdown-item--danger'), children: [item.icon && (_jsx("span", { className: "dgm-option-icon", "aria-hidden": "true", children: item.icon })), _jsx("span", { className: "dgm-dropdown-text", children: item.label }), item.shortcut && (_jsx("span", { className: "dgm-dropdown-shortcut", "aria-hidden": "true", children: item.shortcut }))] }, item.id)))] }) })] }) }));
50
51
  }
@@ -40,6 +40,8 @@ export interface DataTableProps<T extends object> {
40
40
  searchPlaceholder?: string;
41
41
  /** Registros por página. @defaultValue 10 */
42
42
  pageSize?: number;
43
+ /** Oculta solo los controles con cero o una página de resultados filtrados. @defaultValue false */
44
+ hidePaginationOnSinglePage?: boolean;
43
45
  /** Página controlada, comenzando en 1. */
44
46
  page?: number;
45
47
  onPageChange?: (page: number) => void;
@@ -56,4 +58,4 @@ export interface DataTableProps<T extends object> {
56
58
  className?: string;
57
59
  }
58
60
  /** Paginación y selección conservan IDs reales. El filtrado devuelve a la primera página. */
59
- export declare function DataTable<T extends object>({ data, columns, getRowId, caption, sorting, sortable, manualSorting, defaultSorting, onSortingChange, searchable, searchPlaceholder, pageSize, page, onPageChange, selectable, selection, defaultSelection, onSelectionChange, loading, emptyMessage, locale, className, }: DataTableProps<T>): ReactElement;
61
+ export declare function DataTable<T extends object>({ data, columns, getRowId, caption, sorting, sortable, manualSorting, defaultSorting, onSortingChange, searchable, searchPlaceholder, pageSize, hidePaginationOnSinglePage, page, onPageChange, selectable, selection, defaultSelection, onSelectionChange, loading, emptyMessage, locale, className, }: DataTableProps<T>): ReactElement;
@@ -20,7 +20,7 @@ const features = tableFeatures({
20
20
  filterFns: { includesString: filterFn_includesString },
21
21
  });
22
22
  /** Paginación y selección conservan IDs reales. El filtrado devuelve a la primera página. */
23
- export function DataTable({ data, columns, getRowId, caption, sorting, sortable = true, manualSorting = false, defaultSorting = [], onSortingChange, searchable = true, searchPlaceholder, pageSize = 10, page, onPageChange, selectable = false, selection, defaultSelection = [], onSelectionChange, loading = false, emptyMessage, locale = 'en', className, }) {
23
+ export function DataTable({ data, columns, getRowId, caption, sorting, sortable = true, manualSorting = false, defaultSorting = [], onSortingChange, searchable = true, searchPlaceholder, pageSize = 10, hidePaginationOnSinglePage = false, page, onPageChange, selectable = false, selection, defaultSelection = [], onSelectionChange, loading = false, emptyMessage, locale = 'en', className, }) {
24
24
  const [sort, changeSort] = useControllable(sorting, defaultSorting, onSortingChange);
25
25
  const [currentPage, changePage] = useControllable(page, 1, onPageChange);
26
26
  const [selected, changeSelection] = useControllable(selection, defaultSelection, onSelectionChange ? (next) => onSelectionChange([...next]) : undefined);
@@ -117,5 +117,5 @@ export function DataTable({ data, columns, getRowId, caption, sorting, sortable
117
117
  ? es
118
118
  ? 'Cargando…'
119
119
  : 'Loading…'
120
- : `${filtered.length} ${es ? 'resultados' : 'results'}${selectable ? ` · ${selectedCount} ${es ? 'seleccionados' : 'selected'}` : ''}` }), _jsx(Pagination, { page: current, pageCount: pageCount, onPageChange: changePage, disabled: loading, locale: locale })] })] }));
120
+ : `${filtered.length} ${es ? 'resultados' : 'results'}${selectable ? ` · ${selectedCount} ${es ? 'seleccionados' : 'selected'}` : ''}` }), (!hidePaginationOnSinglePage || pageCount > 1) && (_jsx(Pagination, { page: current, pageCount: pageCount, onPageChange: changePage, disabled: loading, locale: locale }))] })] }));
121
121
  }
@@ -14,6 +14,8 @@ export type DropdownMenuItem = {
14
14
  /** Leyenda visual: no registra por sí misma un atajo de teclado. */
15
15
  shortcut?: string;
16
16
  disabled?: boolean;
17
+ /** Apariencia de esta opción; hereda itemVariant del menú. */
18
+ variant?: 'default' | 'ghost';
17
19
  /** Resalta acciones destructivas sin ejecutarlas automáticamente. */
18
20
  danger?: boolean;
19
21
  /** Se ejecuta con puntero o teclado. preventDefault mantiene abierto el menú. */
@@ -24,6 +26,8 @@ export interface DropdownMenuProps {
24
26
  children: ReactElement;
25
27
  /** Acciones y separadores en orden de lectura. IDs únicos. */
26
28
  items: readonly DropdownMenuItem[];
29
+ /** Apariencia de las opciones; no modifica el panel. @defaultValue 'default' */
30
+ itemVariant?: 'default' | 'ghost';
27
31
  /** Encabezado opcional del menú. */
28
32
  label?: string;
29
33
  /** Apertura controlada, junto con onOpenChange. */
@@ -44,4 +48,4 @@ export interface DropdownMenuProps {
44
48
  className?: string;
45
49
  }
46
50
  /** El menú no bloquea el scroll de la página y devuelve el foco sin desplazarla. */
47
- export declare function DropdownMenu({ children, items, label, open, defaultOpen, onOpenChange, side, align, sideOffset, disabled, className, }: DropdownMenuProps): ReactElement;
51
+ export declare function DropdownMenu({ children, items, itemVariant, label, open, defaultOpen, onOpenChange, side, align, sideOffset, disabled, className, }: DropdownMenuProps): ReactElement;
@@ -3,15 +3,16 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  /** Menú de acciones: Radix gestiona teclado, búsqueda por letras, portal y foco. */
4
4
  import { useRef } from 'react';
5
5
  import * as Primitive from '@radix-ui/react-dropdown-menu';
6
+ import { menuItemFocusProps } from './internal/menu-item-focus.js';
6
7
  import { restoreFocus } from './internal/focus.js';
7
8
  import { OverlayLayerProvider, useOverlayLayer } from './internal/overlay-layer.js';
8
9
  import { cx } from './utils.js';
9
10
  /** El menú no bloquea el scroll de la página y devuelve el foco sin desplazarla. */
10
- export function DropdownMenu({ children, items, label, open, defaultOpen, onOpenChange, side = 'bottom', align = 'start', sideOffset = 6, disabled, className, }) {
11
+ export function DropdownMenu({ children, items, itemVariant = 'default', label, open, defaultOpen, onOpenChange, side = 'bottom', align = 'start', sideOffset = 6, disabled, className, }) {
11
12
  const { depth, style: layerStyle } = useOverlayLayer();
12
13
  const trigger = useRef(null);
13
14
  const interactedOutside = useRef(false);
14
- return (_jsx(OverlayLayerProvider, { depth: depth, children: _jsxs(Primitive.Root, { open: open, defaultOpen: defaultOpen, onOpenChange: onOpenChange, modal: false, children: [_jsx(Primitive.Trigger, { asChild: true, ref: trigger, disabled: disabled, children: children }), _jsx(Primitive.Portal, { children: _jsxs(Primitive.Content, { style: layerStyle, side: side, align: align, sideOffset: sideOffset, collisionPadding: 12, className: cx('dgm-dropdown', className), onInteractOutside: () => {
15
+ return (_jsx(OverlayLayerProvider, { depth: depth, children: _jsxs(Primitive.Root, { open: open, defaultOpen: defaultOpen, onOpenChange: onOpenChange, modal: false, children: [_jsx(Primitive.Trigger, { asChild: true, ref: trigger, disabled: disabled, children: children }), _jsx(Primitive.Portal, { children: _jsxs(Primitive.Content, { ...menuItemFocusProps, style: layerStyle, side: side, align: align, sideOffset: sideOffset, collisionPadding: 12, className: cx('dgm-dropdown', className), onInteractOutside: () => {
15
16
  interactedOutside.current = true;
16
17
  }, onCloseAutoFocus: (event) => {
17
18
  // Un clic fuera conserva su destino; Escape o una acción vuelven al botón.
@@ -20,5 +21,5 @@ export function DropdownMenu({ children, items, label, open, defaultOpen, onOpen
20
21
  restoreFocus(trigger.current);
21
22
  }
22
23
  interactedOutside.current = false;
23
- }, children: [label && _jsx(Primitive.Label, { className: "dgm-dropdown-label", children: label }), items.map((item) => item.type === 'separator' ? (_jsx(Primitive.Separator, { className: "dgm-dropdown-separator" }, item.id)) : (_jsxs(Primitive.Item, { textValue: item.label, disabled: item.disabled, onSelect: item.onSelect, className: cx('dgm-dropdown-item', item.danger && 'dgm-dropdown-item--danger'), children: [item.icon && (_jsx("span", { className: "dgm-option-icon", "aria-hidden": "true", children: item.icon })), _jsx("span", { className: "dgm-dropdown-text", children: item.label }), item.shortcut && (_jsx("span", { className: "dgm-dropdown-shortcut", "aria-hidden": "true", children: item.shortcut }))] }, item.id)))] }) })] }) }));
24
+ }, children: [label && _jsx(Primitive.Label, { className: "dgm-dropdown-label", children: label }), items.map((item) => item.type === 'separator' ? (_jsx(Primitive.Separator, { className: "dgm-dropdown-separator" }, item.id)) : (_jsxs(Primitive.Item, { textValue: item.label, disabled: item.disabled, onSelect: item.onSelect, className: cx('dgm-dropdown-item', (item.variant ?? itemVariant) === 'ghost' && 'dgm-dropdown-item--ghost', item.danger && 'dgm-dropdown-item--danger'), children: [item.icon && (_jsx("span", { className: "dgm-option-icon", "aria-hidden": "true", children: item.icon })), _jsx("span", { className: "dgm-dropdown-text", children: item.label }), item.shortcut && (_jsx("span", { className: "dgm-dropdown-shortcut", "aria-hidden": "true", children: item.shortcut }))] }, item.id)))] }) })] }) }));
24
25
  }
@@ -0,0 +1,5 @@
1
+ export declare const menuItemFocusProps: {
2
+ onPointerMoveCapture(event: import("react").PointerEvent<HTMLDivElement>): void;
3
+ onPointerDownCapture(event: import("react").PointerEvent<HTMLDivElement>): void;
4
+ onKeyDownCapture(event: import("react").KeyboardEvent<HTMLDivElement>): void;
5
+ };
@@ -0,0 +1,11 @@
1
+ export const menuItemFocusProps = {
2
+ onPointerMoveCapture(event) {
3
+ event.currentTarget.dataset.inputMode = 'pointer';
4
+ },
5
+ onPointerDownCapture(event) {
6
+ event.currentTarget.dataset.inputMode = 'pointer';
7
+ },
8
+ onKeyDownCapture(event) {
9
+ event.currentTarget.dataset.inputMode = 'keyboard';
10
+ },
11
+ };
@@ -17,10 +17,12 @@ export interface NavigationMenuProps {
17
17
  items: readonly NavigationMenuItem[];
18
18
  /** href activo exacto; el consumidor conoce la ruta actual. */
19
19
  currentHref?: string;
20
+ /** Apariencia de enlaces y disparadores. @defaultValue 'default' */
21
+ variant?: 'default' | 'ghost';
20
22
  /** Adaptador para routers: devuelve un único enlace que acepte props y ref. */
21
23
  renderLink?: (item: NavigationLink) => ReactElement;
22
24
  delayDuration?: number;
23
25
  className?: string;
24
26
  }
25
27
  /** En móvil los elementos se distribuyen en filas y los paneles permanecen dentro del ancho disponible. */
26
- export declare function NavigationMenu({ label, items, currentHref, renderLink, delayDuration, className, }: NavigationMenuProps): ReactElement;
28
+ export declare function NavigationMenu({ label, items, currentHref, variant, renderLink, delayDuration, className, }: NavigationMenuProps): ReactElement;
@@ -18,10 +18,10 @@ function NavigationPanel({ label, children }) {
18
18
  return (_jsxs(_Fragment, { children: [_jsxs(Primitive.Trigger, { ref: trigger, className: "dgm-navigation-trigger", children: [label, _jsx(ChevronIcon, {})] }), _jsx(Primitive.Content, { ref: contentRef, className: "dgm-navigation-content", children: children })] }));
19
19
  }
20
20
  /** En móvil los elementos se distribuyen en filas y los paneles permanecen dentro del ancho disponible. */
21
- export function NavigationMenu({ label, items, currentHref, renderLink, delayDuration = 150, className, }) {
21
+ export function NavigationMenu({ label, items, currentHref, variant = 'default', renderLink, delayDuration = 150, className, }) {
22
22
  function link(item) {
23
23
  const active = currentHref === item.href;
24
24
  return (_jsx(Primitive.Link, { asChild: true, active: active, "aria-current": active ? 'page' : undefined, className: "dgm-navigation-link", children: renderLink ? (renderLink(item)) : (_jsxs("a", { href: item.href, children: [item.icon && _jsx("span", { "aria-hidden": "true", children: item.icon }), _jsxs("span", { children: [item.label, item.description && _jsx("small", { children: item.description })] })] })) }, item.id));
25
25
  }
26
- return (_jsx(Primitive.Root, { "aria-label": label, delayDuration: delayDuration, className: cx('dgm-navigation', className), children: _jsx(Primitive.List, { className: "dgm-navigation-list", children: items.map((item) => (_jsx(Primitive.Item, { className: "dgm-navigation-item", children: 'items' in item ? (_jsx(NavigationPanel, { label: item.label, children: _jsx("ul", { children: item.items.map((child) => (_jsx("li", { children: link(child) }, child.id))) }) })) : (link(item)) }, item.id))) }) }));
26
+ return (_jsx(Primitive.Root, { "aria-label": label, delayDuration: delayDuration, className: cx('dgm-navigation', `dgm-navigation--${variant}`, className), children: _jsx(Primitive.List, { className: "dgm-navigation-list", children: items.map((item) => (_jsx(Primitive.Item, { className: "dgm-navigation-item", children: 'items' in item ? (_jsx(NavigationPanel, { label: item.label, children: _jsx("ul", { children: item.items.map((child) => (_jsx("li", { children: link(child) }, child.id))) }) })) : (link(item)) }, item.id))) }) }));
27
27
  }
package/dist/slider.d.ts CHANGED
@@ -10,6 +10,8 @@ export interface SliderBaseProps {
10
10
  step?: number;
11
11
  disabled?: boolean;
12
12
  name?: string;
13
+ /** Visibilidad visual de los extremos en Slider y RangeSlider; conserva foco y accesibilidad. @defaultValue 'always' */
14
+ thumbVisibility?: 'always' | 'interaction';
13
15
  /** Valor visible y aria-valuetext; útil para porcentajes, unidades o precios. */
14
16
  formatValue?: (value: number) => string;
15
17
  /** Marcas informativas, sin interacción propia. Deben estar entre min y max. */
package/dist/slider.js CHANGED
@@ -1,19 +1,27 @@
1
1
  'use client';
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  /** Radix mantiene teclado, gestos táctiles y valores de formulario; la librería aporta etiquetas y estilo. */
4
- import { useId } from 'react';
4
+ import { useId, useState, useEffect } from 'react';
5
5
  import * as Primitive from '@radix-ui/react-slider';
6
6
  import { useControllable } from './internal/state.js';
7
7
  import { cx, hasContent } from './utils.js';
8
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, }) {
9
+ function SliderControl({ label, description, error, min = 0, max = 100, step = 1, disabled, name, thumbVisibility = 'always', formatValue = String, marks = [], className, id, values, change, commit, thumbLabels, }) {
10
10
  const generated = useId();
11
+ const [activePointer, setActivePointer] = useState(null);
12
+ useEffect(() => {
13
+ if (disabled)
14
+ setActivePointer(null);
15
+ }, [disabled]);
11
16
  const controlId = id ?? `dgm-slider-${generated}`;
12
17
  if (![min, max, step].every(Number.isFinite) || min >= max || step <= 0) {
13
18
  throw new RangeError('Slider requires finite min < max and step > 0.');
14
19
  }
15
20
  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
21
+ 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", "data-thumb-visibility": thumbVisibility, "data-dragging": !disabled && activePointer !== null ? '' : undefined, onPointerDownCapture: (event) => {
22
+ if (!disabled && event.button === 0)
23
+ setActivePointer(event.pointerId);
24
+ }, onPointerUpCapture: () => setActivePointer(null), onPointerCancelCapture: () => setActivePointer(null), onLostPointerCapture: () => setActivePointer(null), 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
25
  .filter((mark) => mark.value >= min && mark.value <= max)
18
26
  .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
27
  }
package/dist/styles.css CHANGED
@@ -1609,7 +1609,10 @@ button.dgm-step:hover:not(:disabled),
1609
1609
  cursor: pointer;
1610
1610
  transition: var(--dgm-hover-transition);
1611
1611
  }
1612
- .dgm-dropdown-item[data-highlighted]:not([data-disabled]) {
1612
+ /* Ghost distingue el foco de teclado del foco que Radix mueve con el puntero. */
1613
+ .dgm-dropdown-item:not(.dgm-dropdown-item--ghost)[data-highlighted]:not([data-disabled]),
1614
+ .dgm-dropdown:not([data-input-mode='pointer'])
1615
+ .dgm-dropdown-item--ghost:focus-visible:not([data-disabled]) {
1613
1616
  outline: 2px solid var(--dgm-primary);
1614
1617
  outline-offset: -2px;
1615
1618
  }
@@ -2775,7 +2778,7 @@ button.dgm-step:hover:not(:disabled),
2775
2778
  }
2776
2779
  .dgm-table-sort-icon {
2777
2780
  position: absolute;
2778
- inset-inline-end: 0;
2781
+ inset-inline-end: 0.5625rem;
2779
2782
  top: 50%;
2780
2783
  width: 0.875rem;
2781
2784
  height: 0.875rem;
@@ -2793,22 +2796,13 @@ button.dgm-step:hover:not(:disabled),
2793
2796
  opacity: 1;
2794
2797
  transform: translateY(-50%) scale(1);
2795
2798
  }
2796
- .dgm-data-table th[data-align='center'] .dgm-table-sort[data-sorted] .dgm-table-sort-label,
2797
- .dgm-data-table th[data-align='end'] .dgm-table-sort[data-sorted] .dgm-table-sort-label {
2799
+ .dgm-data-table th .dgm-table-sort[data-sorted] .dgm-table-sort-label {
2798
2800
  transform: translateX(-0.5625rem);
2799
2801
  }
2800
- .dgm-data-table th[data-align='center'] .dgm-table-sort-icon,
2801
- .dgm-data-table th[data-align='end'] .dgm-table-sort-icon {
2802
- inset-inline-end: 0.5625rem;
2803
- }
2804
2802
  .dgm-data-table th[data-align='start'] .dgm-table-sort {
2805
2803
  margin-inline-start: calc(-1 * var(--dgm-table-sort-space));
2806
2804
  }
2807
- [dir='rtl']
2808
- .dgm-data-table
2809
- th:not([data-align='start'])
2810
- .dgm-table-sort[data-sorted]
2811
- .dgm-table-sort-label {
2805
+ [dir='rtl'] .dgm-data-table th .dgm-table-sort[data-sorted] .dgm-table-sort-label {
2812
2806
  transform: translateX(0.5625rem);
2813
2807
  }
2814
2808
  @media (prefers-reduced-motion: reduce) {
@@ -3139,6 +3133,22 @@ button.dgm-step:disabled {
3139
3133
  box-shadow: 0 2px 5px #0002;
3140
3134
  cursor: grab;
3141
3135
  }
3136
+ /* Opacidad conserva la geometría, el área táctil y los controles accesibles. */
3137
+ .dgm-slider[data-thumb-visibility='interaction'] .dgm-slider-thumb {
3138
+ opacity: 0;
3139
+ transition: opacity 0.15s ease;
3140
+ }
3141
+ .dgm-slider[data-thumb-visibility='interaction']:not([data-disabled]):focus-within
3142
+ .dgm-slider-thumb,
3143
+ .dgm-slider[data-thumb-visibility='interaction']:not([data-disabled])[data-dragging]
3144
+ .dgm-slider-thumb {
3145
+ opacity: 1;
3146
+ }
3147
+ @media (hover: hover) {
3148
+ .dgm-slider[data-thumb-visibility='interaction']:not([data-disabled]):hover .dgm-slider-thumb {
3149
+ opacity: 1;
3150
+ }
3151
+ }
3142
3152
  .dgm-slider-thumb::after {
3143
3153
  content: '';
3144
3154
  position: absolute;
@@ -3331,6 +3341,19 @@ button.dgm-step:disabled {
3331
3341
  .dgm-navigation-trigger[data-state='open'] {
3332
3342
  background: var(--dgm-subtle);
3333
3343
  }
3344
+ /* Conserva la geometría y el foco compartidos, también dentro de los submenús. */
3345
+ .dgm-navigation--ghost .dgm-navigation-trigger,
3346
+ .dgm-navigation--ghost .dgm-navigation-link {
3347
+ background: transparent;
3348
+ border-color: transparent;
3349
+ }
3350
+ .dgm-navigation--ghost .dgm-navigation-link[data-active],
3351
+ .dgm-navigation--ghost .dgm-navigation-trigger[data-state='open'],
3352
+ .dgm-navigation--ghost .dgm-navigation-trigger:hover:not(:disabled),
3353
+ .dgm-navigation--ghost .dgm-navigation-link:hover:not([aria-disabled='true']) {
3354
+ background: var(--dgm-hover);
3355
+ color: var(--dgm-on-hover);
3356
+ }
3334
3357
  .dgm-navigation-content {
3335
3358
  /* Misma separación inicial que Popover y los selectores que lo componen. */
3336
3359
  --dgm-navigation-gap: 8px;
@@ -3584,7 +3607,8 @@ button.dgm-step:disabled {
3584
3607
  .dgm-navigation-content {
3585
3608
  animation: none;
3586
3609
  }
3587
- .dgm-toggle-item {
3610
+ .dgm-toggle-item,
3611
+ .dgm-slider[data-thumb-visibility='interaction'] .dgm-slider-thumb {
3588
3612
  transition: none;
3589
3613
  }
3590
3614
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "danielgm-ui",
3
- "version": "0.4.1",
3
+ "version": "0.4.3",
4
4
  "description": "Accessible React components for Next.js and the web.",
5
5
  "repository": {
6
6
  "type": "git",