@beweco/aurora-ui 1.0.0-beta.133 → 1.0.0-beta.134

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/dist/index.cjs.js CHANGED
@@ -861,7 +861,7 @@ const ACTIVE_LINK_CLASS = "rounded-[12px] bg-primary text-primary-foreground hov
861
861
  * Traducciones por defecto en español. Todas son `aria-label`: este componente no tiene más
862
862
  * texto visible que los números de página.
863
863
  */
864
- const DEFAULT_TRANSLATIONS$f = {
864
+ const DEFAULT_TRANSLATIONS$g = {
865
865
  previousPageAriaLabel: "Página anterior",
866
866
  nextPageAriaLabel: "Página siguiente",
867
867
  jumpBackwardAriaLabel: "Retroceder {count} páginas",
@@ -976,7 +976,7 @@ function getPaginationRange(total, current) {
976
976
  const Pagination = ({ total, page, initialPage = 1, onChange, size: _size = "sm", className, isDisabled, translations = {}, }) => {
977
977
  const [internalPage, setInternalPage] = $8WNJc$react.useState(initialPage);
978
978
  const currentPage = page ?? internalPage;
979
- const t = { ...DEFAULT_TRANSLATIONS$f, ...translations };
979
+ const t = { ...DEFAULT_TRANSLATIONS$g, ...translations };
980
980
  const goTo = (next) => {
981
981
  const clamped = Math.min(Math.max(next, 1), total);
982
982
  if (page === undefined) {
@@ -2114,7 +2114,7 @@ function withCollectionItemIds(children) {
2114
2114
  }
2115
2115
 
2116
2116
  /** Traducciones por defecto en español (mismo literal que la ✕ de la fachada `Input`). */
2117
- const DEFAULT_TRANSLATIONS$e = {
2117
+ const DEFAULT_TRANSLATIONS$f = {
2118
2118
  clearButtonAriaLabel: "Limpiar",
2119
2119
  };
2120
2120
  /**
@@ -2421,7 +2421,7 @@ isClearable = true, isReadOnly, inputValue, selectedKey, onInputChange, onSelect
2421
2421
  const generatedId = $8WNJc$react.useId();
2422
2422
  const autoId = id || generatedId;
2423
2423
  const resolvedIcon = icon === undefined ? DEFAULT_ICON : icon;
2424
- const t = { ...DEFAULT_TRANSLATIONS$e, ...translations };
2424
+ const t = { ...DEFAULT_TRANSLATIONS$f, ...translations };
2425
2425
  /** El `<input>` real, para devolverle el foco tras limpiar (ver `handleClear`). */
2426
2426
  const inputRef = $8WNJc$react.useRef(null);
2427
2427
  // `data-visible` de v2, traducido a montar/no montar: `!!state.selectedItem ||
@@ -4973,7 +4973,7 @@ function computeSlices(data, palette = DEFAULT_DONUT_COLORS) {
4973
4973
  });
4974
4974
  }
4975
4975
 
4976
- const DEFAULT_TRANSLATIONS$d = {
4976
+ const DEFAULT_TRANSLATIONS$e = {
4977
4977
  emptyState: "Sin datos para mostrar",
4978
4978
  totalLabel: "Total",
4979
4979
  };
@@ -4983,7 +4983,7 @@ const DEFAULT_TRANSLATIONS$d = {
4983
4983
  * para que sean estables independientemente del tamaño medido del contenedor.
4984
4984
  */
4985
4985
  const DonutChart = ({ data, height = 220, innerRadiusRatio = 0.65, showLegend = true, showTotal = true, colors, formatValue, animated = true, translations = {}, className, }) => {
4986
- const t = { ...DEFAULT_TRANSLATIONS$d, ...translations };
4986
+ const t = { ...DEFAULT_TRANSLATIONS$e, ...translations };
4987
4987
  if (isDonutEmpty(data)) {
4988
4988
  return (jsxRuntime.jsx("div", { className: className, children: jsxRuntime.jsx(P, { children: t.emptyState }) }));
4989
4989
  }
@@ -5172,11 +5172,11 @@ const NotificationButton = ({ notificationCount = 0, onPress, color = "secondary
5172
5172
  };
5173
5173
  NotificationButton.displayName = "NotificationButton";
5174
5174
 
5175
- const DEFAULT_TRANSLATIONS$c = {
5175
+ const DEFAULT_TRANSLATIONS$d = {
5176
5176
  logout: "Cerrar sesión",
5177
5177
  };
5178
5178
  const HeaderComponent = ({ notificationCount, options, onMenuClick, onLogout, translations = {}, breadcrumbs, extraContent, showMenuButton = true, onNotificationClick, onOptionSelect, onBreadcrumbClick, notificationButtonColor, }) => {
5179
- const t = { ...DEFAULT_TRANSLATIONS$c, ...translations };
5179
+ const t = { ...DEFAULT_TRANSLATIONS$d, ...translations };
5180
5180
  return (jsxRuntime.jsx("header", { className: "header__container", children: jsxRuntime.jsxs("div", { className: "flex items-center justify-between w-full", children: [jsxRuntime.jsxs("div", { className: "flex items-center gap-4", children: [showMenuButton && (jsxRuntime.jsx(Button, { className: "sm:hidden", isIconOnly: true,
5181
5181
  // Fachada propia por defecto pinta color="primary" (D-G4-1); el crudo
5182
5182
  // de v2 pintaba "default" (gris) — se fija explícito.
@@ -5432,7 +5432,7 @@ KanbanColumn.displayName = "KanbanColumn";
5432
5432
  * Traducciones por defecto en español.
5433
5433
  * Se mezclan con las traducciones proporcionadas por el usuario.
5434
5434
  */
5435
- const DEFAULT_TRANSLATIONS$b = {
5435
+ const DEFAULT_TRANSLATIONS$c = {
5436
5436
  dropHere: "Soltar aquí",
5437
5437
  emptyMessage: "No hay elementos",
5438
5438
  itemsLabel: "elementos",
@@ -5466,7 +5466,7 @@ const columnGapClasses = {
5466
5466
  */
5467
5467
  const KanbanComponent = ({ columns, renderItem, onCardClick, onItemMove, isDraggable, cardClassName, isCardClickable, className, columnWidth = "280px", columnGap = "md", horizontalScroll = true, columnMaxHeight, renderColumnHeader, renderEmptyState, translations = {}, onLoadMore, showItemCount = false, }) => {
5468
5468
  // Mezclar traducciones del usuario con las por defecto
5469
- const t = { ...DEFAULT_TRANSLATIONS$b, ...translations };
5469
+ const t = { ...DEFAULT_TRANSLATIONS$c, ...translations };
5470
5470
  // Determinar si drag está habilitado
5471
5471
  const dragEnabled = isDraggable ?? !!onItemMove;
5472
5472
  // Estado del drag actual
@@ -5951,7 +5951,7 @@ const MenuNavList = $8WNJc$react.forwardRef(({ items, isCollapsed, defaultSelect
5951
5951
  });
5952
5952
  MenuNavList.displayName = "MenuNavList";
5953
5953
 
5954
- const DEFAULT_TRANSLATIONS$a = {
5954
+ const DEFAULT_TRANSLATIONS$b = {
5955
5955
  menuLabel: "Menú",
5956
5956
  closeSidebarAriaLabel: "Cerrar menú lateral",
5957
5957
  mobileNavAriaLabel: "Navegación móvil",
@@ -5969,7 +5969,7 @@ const DEFAULT_TRANSLATIONS$a = {
5969
5969
  const MenuComponent = $8WNJc$react.memo(function Menu({ commerceInfo, userInfo, helpButton, isOpenSidebar, onOpenSidebarChange, menuItems, mobileBottomBarGroupKey, showNativeMenu = true, onUserInfoClick, translations = {}, }) {
5970
5970
  const [isCollapsed, setIsCollapsed] = $8WNJc$react.useState(false);
5971
5971
  const [logoError, setLogoError] = $8WNJc$react.useState(false);
5972
- const t = { ...DEFAULT_TRANSLATIONS$a, ...translations };
5972
+ const t = { ...DEFAULT_TRANSLATIONS$b, ...translations };
5973
5973
  const selectedKey = $8WNJc$react.useMemo(() => {
5974
5974
  if (menuItems.selectedPath) {
5975
5975
  return (getSelectedKeyFromPath(menuItems.selectedPath, menuItems.items) ??
@@ -6249,7 +6249,7 @@ const MenuClawNavItem = ({ item, selectedKey, isCollapsed, onSelect, depth = 0,
6249
6249
  };
6250
6250
  MenuClawNavItem.displayName = "MenuClawNavItem";
6251
6251
 
6252
- const DEFAULT_TRANSLATIONS$9 = {
6252
+ const DEFAULT_TRANSLATIONS$a = {
6253
6253
  menuLabel: "Menú",
6254
6254
  closeSidebarAriaLabel: "Cerrar menú lateral",
6255
6255
  mobileNavAriaLabel: "Navegación móvil",
@@ -6287,7 +6287,7 @@ const MenuClaw = $8WNJc$react.memo(function MenuClaw({ userInfo, helpButton, men
6287
6287
  const isControlled = isCollapsedProp !== undefined;
6288
6288
  const [internalCollapsed, setInternalCollapsed] = $8WNJc$react.useState(defaultCollapsed ?? false);
6289
6289
  const collapsed = isCollapsedProp ?? internalCollapsed;
6290
- const t = { ...DEFAULT_TRANSLATIONS$9, ...translations };
6290
+ const t = { ...DEFAULT_TRANSLATIONS$a, ...translations };
6291
6291
  const selectedKey = $8WNJc$react.useMemo(() => {
6292
6292
  if (menuItems.selectedPath) {
6293
6293
  return (getSelectedKeyFromPath(menuItems.selectedPath, menuItems.items) ??
@@ -7137,7 +7137,7 @@ function computeBars(data, palette = DEFAULT_RANKED_BAR_COLORS) {
7137
7137
  });
7138
7138
  }
7139
7139
 
7140
- const DEFAULT_TRANSLATIONS$8 = {
7140
+ const DEFAULT_TRANSLATIONS$9 = {
7141
7141
  emptyState: "Sin datos para mostrar",
7142
7142
  };
7143
7143
  /**
@@ -7147,7 +7147,7 @@ const DEFAULT_TRANSLATIONS$8 = {
7147
7147
  * etapa del funnel, etc.).
7148
7148
  */
7149
7149
  const RankedBarList = ({ data, colors, formatValue, showValue = true, barHeight = 8, translations = {}, className, }) => {
7150
- const t = { ...DEFAULT_TRANSLATIONS$8, ...translations };
7150
+ const t = { ...DEFAULT_TRANSLATIONS$9, ...translations };
7151
7151
  if (isRankedBarListEmpty(data)) {
7152
7152
  return (jsxRuntime.jsx("div", { className: className, children: jsxRuntime.jsx(P, { children: t.emptyState }) }));
7153
7153
  }
@@ -8638,6 +8638,80 @@ const SIZE_TEXT_CLASS = {
8638
8638
  // `min-w-0` es obligatorio: un ítem flex tiene `min-width: auto`, o sea no baja de su `min-content`,
8639
8639
  // y sin eso el texto desborda el trigger en vez de truncar con elipsis.
8640
8640
  const VALUE_ROOM_CLASS = "me-6 min-w-0 truncate";
8641
+ /*
8642
+ * EL ESTADO VACÍO DEL DESPLEGABLE (TECH-2459 · F5 · defecto reportado por el usuario y medido por
8643
+ * la sesión de backoffice: «se pulsa el campo y no pasa nada»).
8644
+ *
8645
+ * EL SÍNTOMA. Un `Select` cuya colección llega VACÍA no se abre: ni popover, ni mensaje, ni señal
8646
+ * de estar vivo, y sin error en consola. La sesión de backoffice lo midió en su app con el mismo
8647
+ * código de clic para todos los campos: tres con 5/3/6 opciones pasaban `aria-expanded` de `false`
8648
+ * a `true`, y el de 0 opciones se quedaba en `false`. Aquí está fijado por control negativo: sin
8649
+ * el `allowsEmptyCollection` de abajo, tres de los cuatro casos de `Select.empty-collection.test.tsx`
8650
+ * fallan y el CONTROL con opciones sigue pasando.
8651
+ *
8652
+ * LA CAUSA, y por qué NO es un olvido de HeroUI. La guarda es de react-aria y es explícita
8653
+ * (`react-stately@3.49.0`, `dist/private/select/useSelectState.mjs:108-111` y su gemela en
8654
+ * `toggle`):
8655
+ *
8656
+ * // Don't open if the collection is empty.
8657
+ * if (listState.collection.size !== 0 || props.allowsEmptyCollection) { … }
8658
+ *
8659
+ * O sea que react-aria trae la puerta de salida hecha —`allowsEmptyCollection`— y nadie la pasa:
8660
+ * cero apariciones en `@heroui/react/dist` (grep sobre el paquete instalado). No está en el código
8661
+ * de HeroUI, pero **sí en su API**: `SelectRootProps` extiende las props de
8662
+ * `react-aria-components/Select`, que son `Omit<AriaSelectProps, "children"|"label"|…>` sin omitir
8663
+ * ésta, y `AriaSelectProps` extiende el `SelectProps` de react-stately donde la prop se declara
8664
+ * (`react-aria/dist/types/src/select/useSelect.d.ts:6` →
8665
+ * `react-stately/dist/types/src/select/useSelectState.d.ts`). Y el `SelectRoot` de v3 hace
8666
+ * `<Select data-slot="select" {...props}>` (`@heroui/react/dist/components/select/select.js:37`),
8667
+ * que acaba en `useSelectState({...props})` (`react-aria-components/dist/private/Select.mjs:92`).
8668
+ * Basta pasarla.
8669
+ *
8670
+ * ⚠️ QUÉ HACÍA v2, VERIFICADO EN EL PAQUETE Y NO DEDUCIDO. v2 se abría, y era **a propósito**: su
8671
+ * `@heroui/select@2.4.22` no usa `react-stately` (no figura entre sus dependencias) sino el fork
8672
+ * propio `@heroui/use-aria-multiselect@2.4.15`, cuyo `useMultiSelectState` invierte la guarda
8673
+ * (`dist/use-multiselect-state.js:102`, guardas en `:112` y `:117`):
8674
+ *
8675
+ * const shouldHideContent = listState.collection.size === 0 && props.hideEmptyContent;
8676
+ * open(...) { if (shouldHideContent) return; … }
8677
+ * toggle(...) { if (shouldHideContent) return; … }
8678
+ *
8679
+ * es decir: abre SIEMPRE salvo que se lo prohíban, y la prohibición es opt-in con default
8680
+ * `hideEmptyContent = false` (`@heroui/select@2.4.22`, `dist/use-select.js:83`). Que exista esa
8681
+ * prop es la prueba de que abrir con la lista vacía era el comportamiento buscado, no un accidente.
8682
+ * Ninguna de las apps la pasa (0 usos de `hideEmptyContent`, medido 17-sep-2026 en los 5 repos
8683
+ * consumidores), así que aquí se reproduce el default de v2 sin exponer la palanca.
8684
+ *
8685
+ * EL TEXTO. Lo que v2 pintaba en ese hueco venía del listbox, no del select: `emptyContent` de
8686
+ * `@heroui/listbox@2.3.21` con `@default "No items."` (`dist/use-listbox.js:51` y el `@default` en
8687
+ * `dist/use-listbox.d.ts:64`) — un literal **inglés hardcodeado en HeroUI**, que es un género
8688
+ * distinto del `selectPlaceholder` que se neutralizó más abajo: ese sí era una cadena de
8689
+ * react-aria que el locale del navegador traducía. Aquí no hay nada de react-aria que traducir (su
8690
+ * bundle de cadenas trae cuatro claves y ninguna es un estado vacío; el `ListBox` de v3 solo pinta
8691
+ * algo si le pasas `renderEmptyState`, `react-aria-components/dist/private/ListBox.mjs:222-228`),
8692
+ * así que el texto lo aporta la fachada y va por el patrón i18n del repo (`translations` +
8693
+ * `DEFAULT_TRANSLATIONS` en español), no hardcodeado y no en inglés.
8694
+ */
8695
+ const DEFAULT_TRANSLATIONS$8 = {
8696
+ emptyContent: "No hay opciones",
8697
+ };
8698
+ // Medidas del estado vacío. Se toman las del ITEM de v3 (`@heroui/styles`,
8699
+ // `dist/components/list-box-item.css:6`: `min-h-9 … px-2 py-1.5`) y no las del slot `emptyContent`
8700
+ // de v2, y es una decisión medida: el literal de v2 era
8701
+ // `["h-10","px-2","py-1.5","w-full","h-full","text-foreground-400","text-start"]`
8702
+ // (`@heroui/theme@2.4.15`, `dist/components/listbox.js:89-97`), donde el `h-10` **nunca se
8703
+ // aplicaba** — el `tv` de v2 lleva `twMerge: true`, así que `h-full`, declarado después, lo
8704
+ // borraba. Reproducir `h-10` sería copiar una intención que v2 no cumplía; alinear con el item de
8705
+ // v3 deja el hueco vacío del mismo alto que tendría una opción, que es lo que se ve bien (§9.1).
8706
+ // Del literal de v2 se conservan lo que sí pintaba: padding, ancho completo, alineación al inicio
8707
+ // y el gris `text-foreground-400` (existe en el tema y se invierte en oscuro —
8708
+ // `src/styles/theme-packs.generated.css:21` claro / `:121` oscuro—, así que no se apaga).
8709
+ //
8710
+ // El `px-2.5` (10px) y no el `px-2` (8px) del item base: dentro del desplegable del Select,
8711
+ // `select.css:205-207` pisa el padding de los items con `px-2.5`, así que 10px es la sangría real
8712
+ // de una opción ahí (medido en el navegador: item `6px 28px 6px 10px`). Con `px-2` el texto del
8713
+ // hueco vacío arrancaba 2px a la izquierda del de las opciones.
8714
+ const EMPTY_CONTENT_CLASS = "flex min-h-9 w-full items-center px-2.5 py-1.5 text-start text-foreground-400";
8641
8715
  const RADIUS_CLASS$4 = {
8642
8716
  none: "rounded-none",
8643
8717
  sm: "rounded-[8px]",
@@ -8900,10 +8974,11 @@ placeholder = "", selectionMode = "single", selectedKeys, defaultSelectedKeys, o
8900
8974
  // `undefined` y el campo dejaba de llenar su contenedor: en una fila flex se encogía al ancho
8901
8975
  // de su contenido (medido por S2: 208px dentro de una fila de 592, 336px de hueco). En una
8902
8976
  // columna no se notaba porque el `stretch` del flex lo tapaba — pero afecta a cualquier app.
8903
- fullWidth = true, isOpen, defaultOpen, onOpenChange, className, classNames, "aria-label": ariaLabel, "aria-labelledby": ariaLabelledBy, "aria-describedby": ariaDescribedBy, "aria-details": ariaDetails, }) => {
8977
+ fullWidth = true, isOpen, defaultOpen, onOpenChange, className, classNames, translations = {}, "aria-label": ariaLabel, "aria-labelledby": ariaLabelledBy, "aria-describedby": ariaDescribedBy, "aria-details": ariaDetails, }) => {
8904
8978
  const generatedId = $8WNJc$react.useId();
8905
8979
  const selectId = id || generatedId;
8906
8980
  const requiredField = isRequired;
8981
+ const t = { ...DEFAULT_TRANSLATIONS$8, ...translations };
8907
8982
  // Mismo criterio que el `group-data-[has-helper=true]` de v2: cuenta como "con ayuda" el campo
8908
8983
  // que efectivamente PINTA una descripción o un mensaje de error, no el que solo recibe la prop.
8909
8984
  const hasHelper = Boolean((description && !isInvalid) || (isInvalid && errorMessage));
@@ -8920,10 +8995,30 @@ fullWidth = true, isOpen, defaultOpen, onOpenChange, className, classNames, "ari
8920
8995
  "w-full", classNames?.trigger);
8921
8996
  const inner = (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [label && (jsxRuntime.jsx(react.Label, { className: react.cn(LABEL_CLASS$1, hasHelper
8922
8997
  ? LABEL_OFFSET_HELPER_CLASS[size]
8923
- : LABEL_OFFSET_CLASS[size], isInvalid ? INVALID_LABEL_CLASS$1 : "", classNames?.label), children: label })), jsxRuntime.jsxs(react.Select.Trigger, { className: triggerClassName, children: [jsxRuntime.jsx(react.Select.Value, { className: react.cn(SIZE_TEXT_CLASS[size], VALUE_ROOM_CLASS, isInvalid ? INVALID_TEXT_CLASS$1 : "") }), jsxRuntime.jsx(react.Select.Indicator, { className: "end-3" })] }), description && !isInvalid && (jsxRuntime.jsx(react.Description, { className: `${HELPER_CLASS$1} text-left`, children: description })), jsxRuntime.jsx(react.Select.Popover, { className: classNames?.popoverContent, children: jsxRuntime.jsx(react.ListBox, { children: withItemIndicator(withCollectionItemIds(children)) }) }), isInvalid && errorMessage && (jsxRuntime.jsx(react.FieldError, { className: `${HELPER_CLASS$1} text-left`, children: errorMessage }))] }));
8998
+ : LABEL_OFFSET_CLASS[size], isInvalid ? INVALID_LABEL_CLASS$1 : "", classNames?.label), children: label })), jsxRuntime.jsxs(react.Select.Trigger, { className: triggerClassName, children: [jsxRuntime.jsx(react.Select.Value, { className: react.cn(SIZE_TEXT_CLASS[size], VALUE_ROOM_CLASS, isInvalid ? INVALID_TEXT_CLASS$1 : "") }), jsxRuntime.jsx(react.Select.Indicator, { className: "end-3" })] }), description && !isInvalid && (jsxRuntime.jsx(react.Description, { className: `${HELPER_CLASS$1} text-left`, children: description })), jsxRuntime.jsx(react.Select.Popover, { className: classNames?.popoverContent, children: jsxRuntime.jsx(react.ListBox
8999
+ // El hueco que v2 llenaba con su `emptyContent` (ver el bloque de
9000
+ // `DEFAULT_TRANSLATIONS`). `renderEmptyState` es la vía de v3: el `ListBox` de
9001
+ // react-aria solo pinta algo con la colección vacía si se le pasa, y envuelve
9002
+ // el resultado en un `<div role="option" style="display:contents">`
9003
+ // (`react-aria-components/dist/private/ListBox.mjs:222-228`), o sea que la caja
9004
+ // que se ve es la nuestra.
9005
+ , {
9006
+ // El hueco que v2 llenaba con su `emptyContent` (ver el bloque de
9007
+ // `DEFAULT_TRANSLATIONS`). `renderEmptyState` es la vía de v3: el `ListBox` de
9008
+ // react-aria solo pinta algo con la colección vacía si se le pasa, y envuelve
9009
+ // el resultado en un `<div role="option" style="display:contents">`
9010
+ // (`react-aria-components/dist/private/ListBox.mjs:222-228`), o sea que la caja
9011
+ // que se ve es la nuestra.
9012
+ renderEmptyState: () => (jsxRuntime.jsx("div", { className: EMPTY_CONTENT_CLASS, children: t.emptyContent })), children: withItemIndicator(withCollectionItemIds(children)) }) }), isInvalid && errorMessage && (jsxRuntime.jsx(react.FieldError, { className: `${HELPER_CLASS$1} text-left`, children: errorMessage }))] }));
8924
9013
  const commonProps = {
8925
9014
  id: selectId,
8926
9015
  name,
9016
+ // La puerta de salida de la guarda de react-aria, sin la cual un campo con la lista vacía
9017
+ // no se abre (ver el bloque de `DEFAULT_TRANSLATIONS` para la cita y para qué hacía v2).
9018
+ // Incondicional a propósito: es el default de v2 (`hideEmptyContent = false`), y las apps
9019
+ // construyen la mayoría de sus colecciones desde datos, así que "vacío" es un estado
9020
+ // normal, no un error.
9021
+ allowsEmptyCollection: true,
8927
9022
  // Etiquetado accesible. Va en la RAÍZ y no en el disparador a propósito: el nombre del
8928
9023
  // disparador lo compone react-aria a partir de las props de la raíz (`useSelect` →
8929
9024
  // `useLabel`), y puesto en `Select.Trigger` se quedaría como un `aria-label` suelto que
package/dist/index.esm.js CHANGED
@@ -861,7 +861,7 @@ const ACTIVE_LINK_CLASS = "rounded-[12px] bg-primary text-primary-foreground hov
861
861
  * Traducciones por defecto en español. Todas son `aria-label`: este componente no tiene más
862
862
  * texto visible que los números de página.
863
863
  */
864
- const DEFAULT_TRANSLATIONS$f = {
864
+ const DEFAULT_TRANSLATIONS$g = {
865
865
  previousPageAriaLabel: "Página anterior",
866
866
  nextPageAriaLabel: "Página siguiente",
867
867
  jumpBackwardAriaLabel: "Retroceder {count} páginas",
@@ -976,7 +976,7 @@ function getPaginationRange(total, current) {
976
976
  const Pagination = ({ total, page, initialPage = 1, onChange, size: _size = "sm", className, isDisabled, translations = {}, }) => {
977
977
  const [internalPage, setInternalPage] = useState(initialPage);
978
978
  const currentPage = page ?? internalPage;
979
- const t = { ...DEFAULT_TRANSLATIONS$f, ...translations };
979
+ const t = { ...DEFAULT_TRANSLATIONS$g, ...translations };
980
980
  const goTo = (next) => {
981
981
  const clamped = Math.min(Math.max(next, 1), total);
982
982
  if (page === undefined) {
@@ -2114,7 +2114,7 @@ function withCollectionItemIds(children) {
2114
2114
  }
2115
2115
 
2116
2116
  /** Traducciones por defecto en español (mismo literal que la ✕ de la fachada `Input`). */
2117
- const DEFAULT_TRANSLATIONS$e = {
2117
+ const DEFAULT_TRANSLATIONS$f = {
2118
2118
  clearButtonAriaLabel: "Limpiar",
2119
2119
  };
2120
2120
  /**
@@ -2421,7 +2421,7 @@ isClearable = true, isReadOnly, inputValue, selectedKey, onInputChange, onSelect
2421
2421
  const generatedId = useId();
2422
2422
  const autoId = id || generatedId;
2423
2423
  const resolvedIcon = icon === undefined ? DEFAULT_ICON : icon;
2424
- const t = { ...DEFAULT_TRANSLATIONS$e, ...translations };
2424
+ const t = { ...DEFAULT_TRANSLATIONS$f, ...translations };
2425
2425
  /** El `<input>` real, para devolverle el foco tras limpiar (ver `handleClear`). */
2426
2426
  const inputRef = useRef(null);
2427
2427
  // `data-visible` de v2, traducido a montar/no montar: `!!state.selectedItem ||
@@ -4973,7 +4973,7 @@ function computeSlices(data, palette = DEFAULT_DONUT_COLORS) {
4973
4973
  });
4974
4974
  }
4975
4975
 
4976
- const DEFAULT_TRANSLATIONS$d = {
4976
+ const DEFAULT_TRANSLATIONS$e = {
4977
4977
  emptyState: "Sin datos para mostrar",
4978
4978
  totalLabel: "Total",
4979
4979
  };
@@ -4983,7 +4983,7 @@ const DEFAULT_TRANSLATIONS$d = {
4983
4983
  * para que sean estables independientemente del tamaño medido del contenedor.
4984
4984
  */
4985
4985
  const DonutChart = ({ data, height = 220, innerRadiusRatio = 0.65, showLegend = true, showTotal = true, colors, formatValue, animated = true, translations = {}, className, }) => {
4986
- const t = { ...DEFAULT_TRANSLATIONS$d, ...translations };
4986
+ const t = { ...DEFAULT_TRANSLATIONS$e, ...translations };
4987
4987
  if (isDonutEmpty(data)) {
4988
4988
  return (jsx("div", { className: className, children: jsx(P, { children: t.emptyState }) }));
4989
4989
  }
@@ -5172,11 +5172,11 @@ const NotificationButton = ({ notificationCount = 0, onPress, color = "secondary
5172
5172
  };
5173
5173
  NotificationButton.displayName = "NotificationButton";
5174
5174
 
5175
- const DEFAULT_TRANSLATIONS$c = {
5175
+ const DEFAULT_TRANSLATIONS$d = {
5176
5176
  logout: "Cerrar sesión",
5177
5177
  };
5178
5178
  const HeaderComponent = ({ notificationCount, options, onMenuClick, onLogout, translations = {}, breadcrumbs, extraContent, showMenuButton = true, onNotificationClick, onOptionSelect, onBreadcrumbClick, notificationButtonColor, }) => {
5179
- const t = { ...DEFAULT_TRANSLATIONS$c, ...translations };
5179
+ const t = { ...DEFAULT_TRANSLATIONS$d, ...translations };
5180
5180
  return (jsx("header", { className: "header__container", children: jsxs("div", { className: "flex items-center justify-between w-full", children: [jsxs("div", { className: "flex items-center gap-4", children: [showMenuButton && (jsx(Button, { className: "sm:hidden", isIconOnly: true,
5181
5181
  // Fachada propia por defecto pinta color="primary" (D-G4-1); el crudo
5182
5182
  // de v2 pintaba "default" (gris) — se fija explícito.
@@ -5432,7 +5432,7 @@ KanbanColumn.displayName = "KanbanColumn";
5432
5432
  * Traducciones por defecto en español.
5433
5433
  * Se mezclan con las traducciones proporcionadas por el usuario.
5434
5434
  */
5435
- const DEFAULT_TRANSLATIONS$b = {
5435
+ const DEFAULT_TRANSLATIONS$c = {
5436
5436
  dropHere: "Soltar aquí",
5437
5437
  emptyMessage: "No hay elementos",
5438
5438
  itemsLabel: "elementos",
@@ -5466,7 +5466,7 @@ const columnGapClasses = {
5466
5466
  */
5467
5467
  const KanbanComponent = ({ columns, renderItem, onCardClick, onItemMove, isDraggable, cardClassName, isCardClickable, className, columnWidth = "280px", columnGap = "md", horizontalScroll = true, columnMaxHeight, renderColumnHeader, renderEmptyState, translations = {}, onLoadMore, showItemCount = false, }) => {
5468
5468
  // Mezclar traducciones del usuario con las por defecto
5469
- const t = { ...DEFAULT_TRANSLATIONS$b, ...translations };
5469
+ const t = { ...DEFAULT_TRANSLATIONS$c, ...translations };
5470
5470
  // Determinar si drag está habilitado
5471
5471
  const dragEnabled = isDraggable ?? !!onItemMove;
5472
5472
  // Estado del drag actual
@@ -5951,7 +5951,7 @@ const MenuNavList = $8WNJc$react.forwardRef(({ items, isCollapsed, defaultSelect
5951
5951
  });
5952
5952
  MenuNavList.displayName = "MenuNavList";
5953
5953
 
5954
- const DEFAULT_TRANSLATIONS$a = {
5954
+ const DEFAULT_TRANSLATIONS$b = {
5955
5955
  menuLabel: "Menú",
5956
5956
  closeSidebarAriaLabel: "Cerrar menú lateral",
5957
5957
  mobileNavAriaLabel: "Navegación móvil",
@@ -5969,7 +5969,7 @@ const DEFAULT_TRANSLATIONS$a = {
5969
5969
  const MenuComponent = $8WNJc$react.memo(function Menu({ commerceInfo, userInfo, helpButton, isOpenSidebar, onOpenSidebarChange, menuItems, mobileBottomBarGroupKey, showNativeMenu = true, onUserInfoClick, translations = {}, }) {
5970
5970
  const [isCollapsed, setIsCollapsed] = $8WNJc$react.useState(false);
5971
5971
  const [logoError, setLogoError] = $8WNJc$react.useState(false);
5972
- const t = { ...DEFAULT_TRANSLATIONS$a, ...translations };
5972
+ const t = { ...DEFAULT_TRANSLATIONS$b, ...translations };
5973
5973
  const selectedKey = $8WNJc$react.useMemo(() => {
5974
5974
  if (menuItems.selectedPath) {
5975
5975
  return (getSelectedKeyFromPath(menuItems.selectedPath, menuItems.items) ??
@@ -6249,7 +6249,7 @@ const MenuClawNavItem = ({ item, selectedKey, isCollapsed, onSelect, depth = 0,
6249
6249
  };
6250
6250
  MenuClawNavItem.displayName = "MenuClawNavItem";
6251
6251
 
6252
- const DEFAULT_TRANSLATIONS$9 = {
6252
+ const DEFAULT_TRANSLATIONS$a = {
6253
6253
  menuLabel: "Menú",
6254
6254
  closeSidebarAriaLabel: "Cerrar menú lateral",
6255
6255
  mobileNavAriaLabel: "Navegación móvil",
@@ -6287,7 +6287,7 @@ const MenuClaw = $8WNJc$react.memo(function MenuClaw({ userInfo, helpButton, men
6287
6287
  const isControlled = isCollapsedProp !== undefined;
6288
6288
  const [internalCollapsed, setInternalCollapsed] = $8WNJc$react.useState(defaultCollapsed ?? false);
6289
6289
  const collapsed = isCollapsedProp ?? internalCollapsed;
6290
- const t = { ...DEFAULT_TRANSLATIONS$9, ...translations };
6290
+ const t = { ...DEFAULT_TRANSLATIONS$a, ...translations };
6291
6291
  const selectedKey = useMemo(() => {
6292
6292
  if (menuItems.selectedPath) {
6293
6293
  return (getSelectedKeyFromPath(menuItems.selectedPath, menuItems.items) ??
@@ -7137,7 +7137,7 @@ function computeBars(data, palette = DEFAULT_RANKED_BAR_COLORS) {
7137
7137
  });
7138
7138
  }
7139
7139
 
7140
- const DEFAULT_TRANSLATIONS$8 = {
7140
+ const DEFAULT_TRANSLATIONS$9 = {
7141
7141
  emptyState: "Sin datos para mostrar",
7142
7142
  };
7143
7143
  /**
@@ -7147,7 +7147,7 @@ const DEFAULT_TRANSLATIONS$8 = {
7147
7147
  * etapa del funnel, etc.).
7148
7148
  */
7149
7149
  const RankedBarList = ({ data, colors, formatValue, showValue = true, barHeight = 8, translations = {}, className, }) => {
7150
- const t = { ...DEFAULT_TRANSLATIONS$8, ...translations };
7150
+ const t = { ...DEFAULT_TRANSLATIONS$9, ...translations };
7151
7151
  if (isRankedBarListEmpty(data)) {
7152
7152
  return (jsx("div", { className: className, children: jsx(P, { children: t.emptyState }) }));
7153
7153
  }
@@ -8638,6 +8638,80 @@ const SIZE_TEXT_CLASS = {
8638
8638
  // `min-w-0` es obligatorio: un ítem flex tiene `min-width: auto`, o sea no baja de su `min-content`,
8639
8639
  // y sin eso el texto desborda el trigger en vez de truncar con elipsis.
8640
8640
  const VALUE_ROOM_CLASS = "me-6 min-w-0 truncate";
8641
+ /*
8642
+ * EL ESTADO VACÍO DEL DESPLEGABLE (TECH-2459 · F5 · defecto reportado por el usuario y medido por
8643
+ * la sesión de backoffice: «se pulsa el campo y no pasa nada»).
8644
+ *
8645
+ * EL SÍNTOMA. Un `Select` cuya colección llega VACÍA no se abre: ni popover, ni mensaje, ni señal
8646
+ * de estar vivo, y sin error en consola. La sesión de backoffice lo midió en su app con el mismo
8647
+ * código de clic para todos los campos: tres con 5/3/6 opciones pasaban `aria-expanded` de `false`
8648
+ * a `true`, y el de 0 opciones se quedaba en `false`. Aquí está fijado por control negativo: sin
8649
+ * el `allowsEmptyCollection` de abajo, tres de los cuatro casos de `Select.empty-collection.test.tsx`
8650
+ * fallan y el CONTROL con opciones sigue pasando.
8651
+ *
8652
+ * LA CAUSA, y por qué NO es un olvido de HeroUI. La guarda es de react-aria y es explícita
8653
+ * (`react-stately@3.49.0`, `dist/private/select/useSelectState.mjs:108-111` y su gemela en
8654
+ * `toggle`):
8655
+ *
8656
+ * // Don't open if the collection is empty.
8657
+ * if (listState.collection.size !== 0 || props.allowsEmptyCollection) { … }
8658
+ *
8659
+ * O sea que react-aria trae la puerta de salida hecha —`allowsEmptyCollection`— y nadie la pasa:
8660
+ * cero apariciones en `@heroui/react/dist` (grep sobre el paquete instalado). No está en el código
8661
+ * de HeroUI, pero **sí en su API**: `SelectRootProps` extiende las props de
8662
+ * `react-aria-components/Select`, que son `Omit<AriaSelectProps, "children"|"label"|…>` sin omitir
8663
+ * ésta, y `AriaSelectProps` extiende el `SelectProps` de react-stately donde la prop se declara
8664
+ * (`react-aria/dist/types/src/select/useSelect.d.ts:6` →
8665
+ * `react-stately/dist/types/src/select/useSelectState.d.ts`). Y el `SelectRoot` de v3 hace
8666
+ * `<Select data-slot="select" {...props}>` (`@heroui/react/dist/components/select/select.js:37`),
8667
+ * que acaba en `useSelectState({...props})` (`react-aria-components/dist/private/Select.mjs:92`).
8668
+ * Basta pasarla.
8669
+ *
8670
+ * ⚠️ QUÉ HACÍA v2, VERIFICADO EN EL PAQUETE Y NO DEDUCIDO. v2 se abría, y era **a propósito**: su
8671
+ * `@heroui/select@2.4.22` no usa `react-stately` (no figura entre sus dependencias) sino el fork
8672
+ * propio `@heroui/use-aria-multiselect@2.4.15`, cuyo `useMultiSelectState` invierte la guarda
8673
+ * (`dist/use-multiselect-state.js:102`, guardas en `:112` y `:117`):
8674
+ *
8675
+ * const shouldHideContent = listState.collection.size === 0 && props.hideEmptyContent;
8676
+ * open(...) { if (shouldHideContent) return; … }
8677
+ * toggle(...) { if (shouldHideContent) return; … }
8678
+ *
8679
+ * es decir: abre SIEMPRE salvo que se lo prohíban, y la prohibición es opt-in con default
8680
+ * `hideEmptyContent = false` (`@heroui/select@2.4.22`, `dist/use-select.js:83`). Que exista esa
8681
+ * prop es la prueba de que abrir con la lista vacía era el comportamiento buscado, no un accidente.
8682
+ * Ninguna de las apps la pasa (0 usos de `hideEmptyContent`, medido 17-sep-2026 en los 5 repos
8683
+ * consumidores), así que aquí se reproduce el default de v2 sin exponer la palanca.
8684
+ *
8685
+ * EL TEXTO. Lo que v2 pintaba en ese hueco venía del listbox, no del select: `emptyContent` de
8686
+ * `@heroui/listbox@2.3.21` con `@default "No items."` (`dist/use-listbox.js:51` y el `@default` en
8687
+ * `dist/use-listbox.d.ts:64`) — un literal **inglés hardcodeado en HeroUI**, que es un género
8688
+ * distinto del `selectPlaceholder` que se neutralizó más abajo: ese sí era una cadena de
8689
+ * react-aria que el locale del navegador traducía. Aquí no hay nada de react-aria que traducir (su
8690
+ * bundle de cadenas trae cuatro claves y ninguna es un estado vacío; el `ListBox` de v3 solo pinta
8691
+ * algo si le pasas `renderEmptyState`, `react-aria-components/dist/private/ListBox.mjs:222-228`),
8692
+ * así que el texto lo aporta la fachada y va por el patrón i18n del repo (`translations` +
8693
+ * `DEFAULT_TRANSLATIONS` en español), no hardcodeado y no en inglés.
8694
+ */
8695
+ const DEFAULT_TRANSLATIONS$8 = {
8696
+ emptyContent: "No hay opciones",
8697
+ };
8698
+ // Medidas del estado vacío. Se toman las del ITEM de v3 (`@heroui/styles`,
8699
+ // `dist/components/list-box-item.css:6`: `min-h-9 … px-2 py-1.5`) y no las del slot `emptyContent`
8700
+ // de v2, y es una decisión medida: el literal de v2 era
8701
+ // `["h-10","px-2","py-1.5","w-full","h-full","text-foreground-400","text-start"]`
8702
+ // (`@heroui/theme@2.4.15`, `dist/components/listbox.js:89-97`), donde el `h-10` **nunca se
8703
+ // aplicaba** — el `tv` de v2 lleva `twMerge: true`, así que `h-full`, declarado después, lo
8704
+ // borraba. Reproducir `h-10` sería copiar una intención que v2 no cumplía; alinear con el item de
8705
+ // v3 deja el hueco vacío del mismo alto que tendría una opción, que es lo que se ve bien (§9.1).
8706
+ // Del literal de v2 se conservan lo que sí pintaba: padding, ancho completo, alineación al inicio
8707
+ // y el gris `text-foreground-400` (existe en el tema y se invierte en oscuro —
8708
+ // `src/styles/theme-packs.generated.css:21` claro / `:121` oscuro—, así que no se apaga).
8709
+ //
8710
+ // El `px-2.5` (10px) y no el `px-2` (8px) del item base: dentro del desplegable del Select,
8711
+ // `select.css:205-207` pisa el padding de los items con `px-2.5`, así que 10px es la sangría real
8712
+ // de una opción ahí (medido en el navegador: item `6px 28px 6px 10px`). Con `px-2` el texto del
8713
+ // hueco vacío arrancaba 2px a la izquierda del de las opciones.
8714
+ const EMPTY_CONTENT_CLASS = "flex min-h-9 w-full items-center px-2.5 py-1.5 text-start text-foreground-400";
8641
8715
  const RADIUS_CLASS$4 = {
8642
8716
  none: "rounded-none",
8643
8717
  sm: "rounded-[8px]",
@@ -8900,10 +8974,11 @@ placeholder = "", selectionMode = "single", selectedKeys, defaultSelectedKeys, o
8900
8974
  // `undefined` y el campo dejaba de llenar su contenedor: en una fila flex se encogía al ancho
8901
8975
  // de su contenido (medido por S2: 208px dentro de una fila de 592, 336px de hueco). En una
8902
8976
  // columna no se notaba porque el `stretch` del flex lo tapaba — pero afecta a cualquier app.
8903
- fullWidth = true, isOpen, defaultOpen, onOpenChange, className, classNames, "aria-label": ariaLabel, "aria-labelledby": ariaLabelledBy, "aria-describedby": ariaDescribedBy, "aria-details": ariaDetails, }) => {
8977
+ fullWidth = true, isOpen, defaultOpen, onOpenChange, className, classNames, translations = {}, "aria-label": ariaLabel, "aria-labelledby": ariaLabelledBy, "aria-describedby": ariaDescribedBy, "aria-details": ariaDetails, }) => {
8904
8978
  const generatedId = useId();
8905
8979
  const selectId = id || generatedId;
8906
8980
  const requiredField = isRequired;
8981
+ const t = { ...DEFAULT_TRANSLATIONS$8, ...translations };
8907
8982
  // Mismo criterio que el `group-data-[has-helper=true]` de v2: cuenta como "con ayuda" el campo
8908
8983
  // que efectivamente PINTA una descripción o un mensaje de error, no el que solo recibe la prop.
8909
8984
  const hasHelper = Boolean((description && !isInvalid) || (isInvalid && errorMessage));
@@ -8920,10 +8995,30 @@ fullWidth = true, isOpen, defaultOpen, onOpenChange, className, classNames, "ari
8920
8995
  "w-full", classNames?.trigger);
8921
8996
  const inner = (jsxs(Fragment, { children: [label && (jsx(Label, { className: cn(LABEL_CLASS$1, hasHelper
8922
8997
  ? LABEL_OFFSET_HELPER_CLASS[size]
8923
- : LABEL_OFFSET_CLASS[size], isInvalid ? INVALID_LABEL_CLASS$1 : "", classNames?.label), children: label })), jsxs(Select$1.Trigger, { className: triggerClassName, children: [jsx(Select$1.Value, { className: cn(SIZE_TEXT_CLASS[size], VALUE_ROOM_CLASS, isInvalid ? INVALID_TEXT_CLASS$1 : "") }), jsx(Select$1.Indicator, { className: "end-3" })] }), description && !isInvalid && (jsx(Description, { className: `${HELPER_CLASS$1} text-left`, children: description })), jsx(Select$1.Popover, { className: classNames?.popoverContent, children: jsx(ListBox, { children: withItemIndicator(withCollectionItemIds(children)) }) }), isInvalid && errorMessage && (jsx(FieldError, { className: `${HELPER_CLASS$1} text-left`, children: errorMessage }))] }));
8998
+ : LABEL_OFFSET_CLASS[size], isInvalid ? INVALID_LABEL_CLASS$1 : "", classNames?.label), children: label })), jsxs(Select$1.Trigger, { className: triggerClassName, children: [jsx(Select$1.Value, { className: cn(SIZE_TEXT_CLASS[size], VALUE_ROOM_CLASS, isInvalid ? INVALID_TEXT_CLASS$1 : "") }), jsx(Select$1.Indicator, { className: "end-3" })] }), description && !isInvalid && (jsx(Description, { className: `${HELPER_CLASS$1} text-left`, children: description })), jsx(Select$1.Popover, { className: classNames?.popoverContent, children: jsx(ListBox
8999
+ // El hueco que v2 llenaba con su `emptyContent` (ver el bloque de
9000
+ // `DEFAULT_TRANSLATIONS`). `renderEmptyState` es la vía de v3: el `ListBox` de
9001
+ // react-aria solo pinta algo con la colección vacía si se le pasa, y envuelve
9002
+ // el resultado en un `<div role="option" style="display:contents">`
9003
+ // (`react-aria-components/dist/private/ListBox.mjs:222-228`), o sea que la caja
9004
+ // que se ve es la nuestra.
9005
+ , {
9006
+ // El hueco que v2 llenaba con su `emptyContent` (ver el bloque de
9007
+ // `DEFAULT_TRANSLATIONS`). `renderEmptyState` es la vía de v3: el `ListBox` de
9008
+ // react-aria solo pinta algo con la colección vacía si se le pasa, y envuelve
9009
+ // el resultado en un `<div role="option" style="display:contents">`
9010
+ // (`react-aria-components/dist/private/ListBox.mjs:222-228`), o sea que la caja
9011
+ // que se ve es la nuestra.
9012
+ renderEmptyState: () => (jsx("div", { className: EMPTY_CONTENT_CLASS, children: t.emptyContent })), children: withItemIndicator(withCollectionItemIds(children)) }) }), isInvalid && errorMessage && (jsx(FieldError, { className: `${HELPER_CLASS$1} text-left`, children: errorMessage }))] }));
8924
9013
  const commonProps = {
8925
9014
  id: selectId,
8926
9015
  name,
9016
+ // La puerta de salida de la guarda de react-aria, sin la cual un campo con la lista vacía
9017
+ // no se abre (ver el bloque de `DEFAULT_TRANSLATIONS` para la cita y para qué hacía v2).
9018
+ // Incondicional a propósito: es el default de v2 (`hideEmptyContent = false`), y las apps
9019
+ // construyen la mayoría de sus colecciones desde datos, así que "vacío" es un estado
9020
+ // normal, no un error.
9021
+ allowsEmptyCollection: true,
8927
9022
  // Etiquetado accesible. Va en la RAÍZ y no en el disparador a propósito: el nombre del
8928
9023
  // disparador lo compone react-aria a partir de las props de la raíz (`useSelect` →
8929
9024
  // `useLabel`), y puesto en `Select.Trigger` se quedaría como un `aria-label` suelto que
@@ -1 +1 @@
1
- {"version":3,"file":"Select.d.ts","sourceRoot":"","sources":["../../../../src/components/select/Select.tsx"],"names":[],"mappings":"AASA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAG/B,OAAO,KAAK,EACX,WAAW,EAIX,MAAM,gBAAgB,CAAC;AAgTxB;;;;;;;;;GASG;AACH,eAAO,MAAM,MAAM,EAAE,KAAK,CAAC,EAAE,CAAC,WAAW,CAiSxC,CAAC;AAEF,eAAe,MAAM,CAAC"}
1
+ {"version":3,"file":"Select.d.ts","sourceRoot":"","sources":["../../../../src/components/select/Select.tsx"],"names":[],"mappings":"AASA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAG/B,OAAO,KAAK,EACX,WAAW,EAKX,MAAM,gBAAgB,CAAC;AA6XxB;;;;;;;;;GASG;AACH,eAAO,MAAM,MAAM,EAAE,KAAK,CAAC,EAAE,CAAC,WAAW,CAqTxC,CAAC;AAEF,eAAe,MAAM,CAAC"}
@@ -4,6 +4,27 @@ export type SelectSize = "sm" | "md" | "lg";
4
4
  /** Vocabulario v2 (HeroUI v3 solo tiene "primary"/"secondary"). */
5
5
  export type SelectVariant = "flat" | "bordered" | "faded" | "underlined";
6
6
  export type SelectRadius = "none" | "sm" | "md" | "lg" | "full";
7
+ /**
8
+ * Traducciones del componente (TECH-2459 · F5, defecto «un Select con la lista vacía no se abre»).
9
+ *
10
+ * Solo hay UNA clave, y el porqué importa: el estado vacío es el único texto que esta fachada
11
+ * aporta. El placeholder se neutraliza a propósito con cadena vacía (ver `Select.tsx`), porque ahí
12
+ * v2 no pintaba nada; aquí v2 SÍ pintaba texto —el `emptyContent` de `@heroui/listbox@2.3.21`, cuyo
13
+ * `@default` es el literal **inglés** `"No items."` (`dist/use-listbox.d.ts:64`)—, así que lo que
14
+ * procede es reponerlo traducido, no suprimirlo.
15
+ *
16
+ * No hay alternativa de react-aria que traducir: su paquete de cadenas solo trae cuatro
17
+ * (`react-aria-components/dist/private/intl/es-ES.mjs`: `colorSwatchPicker`, `dropzoneLabel`,
18
+ * `selectPlaceholder`, `tableResizer`) y ninguna es un estado vacío — el `ListBox` de v3 no pinta
19
+ * nada si no le pasas `renderEmptyState`. O sea que el texto tiene que ser nuestro.
20
+ */
21
+ export type SelectTranslations = {
22
+ /**
23
+ * Texto que muestra el desplegable cuando la colección de opciones está vacía.
24
+ * @default "No hay opciones"
25
+ */
26
+ emptyContent?: string;
27
+ };
7
28
  export interface SelectClassNames {
8
29
  label?: string;
9
30
  trigger?: string;
@@ -67,6 +88,14 @@ export interface SelectProps {
67
88
  className?: string;
68
89
  /** Overrides puntuales por slot (compat v2; usar `className` para el resto). */
69
90
  classNames?: SelectClassNames;
91
+ /**
92
+ * Traducciones i18n del componente. Si no se pasa, usa los valores por defecto en español.
93
+ * @example
94
+ * ```tsx
95
+ * <Select translations={{ emptyContent: t("select_empty") }}>{items}</Select>
96
+ * ```
97
+ */
98
+ translations?: SelectTranslations;
70
99
  "aria-label"?: string;
71
100
  "aria-labelledby"?: string;
72
101
  "aria-describedby"?: string;
@@ -1 +1 @@
1
- {"version":3,"file":"Select.types.d.ts","sourceRoot":"","sources":["../../../../src/components/select/Select.types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,GAAG,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AACpD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAEvC,MAAM,MAAM,UAAU,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;AAC5C,mEAAmE;AACnE,MAAM,MAAM,aAAa,GAAG,MAAM,GAAG,UAAU,GAAG,OAAO,GAAG,YAAY,CAAC;AACzE,MAAM,MAAM,YAAY,GAAG,MAAM,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,MAAM,CAAC;AAEhE,MAAM,WAAW,gBAAgB;IAChC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,cAAc,CAAC,EAAE,MAAM,CAAC;CACxB;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,MAAM,MAAM,eAAe,GAAG,SAAS,GAAG,SAAS,GAAG,EAAE,CAAC;AASzD,MAAM,WAAW,WAAW;IAC3B,QAAQ,CAAC,EAAE,SAAS,CAAC;IACrB,KAAK,CAAC,EAAE,SAAS,CAAC;IAClB,WAAW,CAAC,EAAE,SAAS,CAAC;IACxB,YAAY,CAAC,EAAE,SAAS,CAAC;IACzB,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,4CAA4C;IAC5C,OAAO,CAAC,EAAE,aAAa,CAAC;IACxB,+CAA+C;IAC/C,IAAI,CAAC,EAAE,UAAU,CAAC;IAClB,6CAA6C;IAC7C,MAAM,CAAC,EAAE,YAAY,CAAC;IACtB,wBAAwB;IACxB,aAAa,CAAC,EAAE,QAAQ,GAAG,UAAU,CAAC;IACtC;;;OAGG;IACH,YAAY,CAAC,EAAE,eAAe,CAAC;IAC/B,0FAA0F;IAC1F,mBAAmB,CAAC,EAAE,eAAe,CAAC;IACtC,sFAAsF;IACtF,iBAAiB,CAAC,EAAE,CAAC,IAAI,EAAE,SAAS,KAAK,IAAI,CAAC;IAC9C,YAAY,CAAC,EAAE,QAAQ,CAAC,GAAG,CAAC,CAAC;IAC7B,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,YAAY,CAAC,EAAE,CAAC,MAAM,EAAE,OAAO,KAAK,IAAI,CAAC;IACzC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,gFAAgF;IAChF,UAAU,CAAC,EAAE,gBAAgB,CAAC;IAkB9B,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,cAAc,CAAC,EAAE,MAAM,CAAC;CACxB"}
1
+ {"version":3,"file":"Select.types.d.ts","sourceRoot":"","sources":["../../../../src/components/select/Select.types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,GAAG,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AACpD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAEvC,MAAM,MAAM,UAAU,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;AAC5C,mEAAmE;AACnE,MAAM,MAAM,aAAa,GAAG,MAAM,GAAG,UAAU,GAAG,OAAO,GAAG,YAAY,CAAC;AACzE,MAAM,MAAM,YAAY,GAAG,MAAM,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,MAAM,CAAC;AAEhE;;;;;;;;;;;;;GAaG;AACH,MAAM,MAAM,kBAAkB,GAAG;IAChC;;;OAGG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;CACtB,CAAC;AAEF,MAAM,WAAW,gBAAgB;IAChC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,cAAc,CAAC,EAAE,MAAM,CAAC;CACxB;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,MAAM,MAAM,eAAe,GAAG,SAAS,GAAG,SAAS,GAAG,EAAE,CAAC;AASzD,MAAM,WAAW,WAAW;IAC3B,QAAQ,CAAC,EAAE,SAAS,CAAC;IACrB,KAAK,CAAC,EAAE,SAAS,CAAC;IAClB,WAAW,CAAC,EAAE,SAAS,CAAC;IACxB,YAAY,CAAC,EAAE,SAAS,CAAC;IACzB,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,4CAA4C;IAC5C,OAAO,CAAC,EAAE,aAAa,CAAC;IACxB,+CAA+C;IAC/C,IAAI,CAAC,EAAE,UAAU,CAAC;IAClB,6CAA6C;IAC7C,MAAM,CAAC,EAAE,YAAY,CAAC;IACtB,wBAAwB;IACxB,aAAa,CAAC,EAAE,QAAQ,GAAG,UAAU,CAAC;IACtC;;;OAGG;IACH,YAAY,CAAC,EAAE,eAAe,CAAC;IAC/B,0FAA0F;IAC1F,mBAAmB,CAAC,EAAE,eAAe,CAAC;IACtC,sFAAsF;IACtF,iBAAiB,CAAC,EAAE,CAAC,IAAI,EAAE,SAAS,KAAK,IAAI,CAAC;IAC9C,YAAY,CAAC,EAAE,QAAQ,CAAC,GAAG,CAAC,CAAC;IAC7B,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,YAAY,CAAC,EAAE,CAAC,MAAM,EAAE,OAAO,KAAK,IAAI,CAAC;IACzC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,gFAAgF;IAChF,UAAU,CAAC,EAAE,gBAAgB,CAAC;IAC9B;;;;;;OAMG;IACH,YAAY,CAAC,EAAE,kBAAkB,CAAC;IAkBlC,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,cAAc,CAAC,EAAE,MAAM,CAAC;CACxB"}
@@ -1,3 +1,3 @@
1
1
  export { Select, default } from "./Select";
2
- export type { SelectProps, SelectSelection } from "./Select.types";
2
+ export type { SelectProps, SelectSelection, SelectTranslations, } from "./Select.types";
3
3
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/components/select/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,UAAU,CAAC;AAG3C,YAAY,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/components/select/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,UAAU,CAAC;AAG3C,YAAY,EACX,WAAW,EACX,eAAe,EACf,kBAAkB,GAClB,MAAM,gBAAgB,CAAC"}
@@ -73,7 +73,7 @@ export { Button, type ButtonProps } from "./components/button";
73
73
  export { Card, type CardProps } from "./components/card";
74
74
  export { Input, type InputProps } from "./components/input";
75
75
  export { SearchInput, type SearchInputProps } from "./components/search-input";
76
- export { Select, type SelectProps } from "./components/select";
76
+ export { Select, type SelectProps, type SelectTranslations, } from "./components/select";
77
77
  export { Table, type TableProps } from "./components/table";
78
78
  export { Dropdown, DropdownSection, type DropdownProps, type DropdownSectionClassNames, type DropdownSectionProps, } from "./components/dropdown";
79
79
  export { Textarea, type TextareaProps } from "./components/textarea";