@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/assets/css/styles.css +1 -1
- package/dist/index.cjs.js +113 -18
- package/dist/index.esm.js +113 -18
- package/dist/types/components/select/Select.d.ts.map +1 -1
- package/dist/types/components/select/Select.types.d.ts +29 -0
- package/dist/types/components/select/Select.types.d.ts.map +1 -1
- package/dist/types/components/select/index.d.ts +1 -1
- package/dist/types/components/select/index.d.ts.map +1 -1
- package/dist/types/index.d.ts +1 -1
- package/dist/types/index.d.ts.map +1 -1
- package/package.json +1 -1
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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
|
|
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,
|
|
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;
|
|
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 +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,
|
|
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"}
|
package/dist/types/index.d.ts
CHANGED
|
@@ -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";
|