@beweco/aurora-ui 1.0.0-beta.131 → 1.0.0-beta.133
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 +241 -126
- package/dist/index.esm.js +241 -126
- package/dist/types/components/input/Input.d.ts.map +1 -1
- package/dist/types/components/menu/_internal/menu-nav-list/MenuNavList.d.ts.map +1 -1
- package/dist/types/components/pagination/Pagination.d.ts +8 -0
- package/dist/types/components/pagination/Pagination.d.ts.map +1 -1
- package/dist/types/components/pagination/Pagination.types.d.ts +35 -0
- package/dist/types/components/pagination/Pagination.types.d.ts.map +1 -1
- package/dist/types/components/select/Select.d.ts.map +1 -1
- package/dist/types/components/select/Select.types.d.ts +4 -0
- package/dist/types/components/select/Select.types.d.ts.map +1 -1
- package/dist/types/components/textarea/Textarea.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
|
@@ -857,6 +857,64 @@ const NAV_END_CLASS = `${CELL_CLASS} w-9 px-0 rounded-e-[12px] rounded-s-none`;
|
|
|
857
857
|
* `--color-default` de Aurora (falso amigo, decisiones-aceptadas-f1.md D-F1-3).
|
|
858
858
|
*/
|
|
859
859
|
const ACTIVE_LINK_CLASS = "rounded-[12px] bg-primary text-primary-foreground hover:bg-primary active:bg-primary";
|
|
860
|
+
/**
|
|
861
|
+
* Traducciones por defecto en español. Todas son `aria-label`: este componente no tiene más
|
|
862
|
+
* texto visible que los números de página.
|
|
863
|
+
*/
|
|
864
|
+
const DEFAULT_TRANSLATIONS$f = {
|
|
865
|
+
previousPageAriaLabel: "Página anterior",
|
|
866
|
+
nextPageAriaLabel: "Página siguiente",
|
|
867
|
+
jumpBackwardAriaLabel: "Retroceder {count} páginas",
|
|
868
|
+
jumpForwardAriaLabel: "Avanzar {count} páginas",
|
|
869
|
+
};
|
|
870
|
+
/**
|
|
871
|
+
* TECH-2459 · elipsis navegable — páginas que salta la elipsis. Literal de v2:
|
|
872
|
+
* `dotsJump = 5`, default desestructurado en `@heroui/pagination@2.2.20`
|
|
873
|
+
* (`dist/use-pagination.js:57`, la versión que traía `@heroui/react@2.7.11`).
|
|
874
|
+
*
|
|
875
|
+
* El destino se recorta a `[1, total]` exactamente como allí (`dist/pagination.js:530`):
|
|
876
|
+
*
|
|
877
|
+
* onPress: () => isBefore
|
|
878
|
+
* ? setPage(activePage - dotsJump >= 1 ? activePage - dotsJump : 1)
|
|
879
|
+
* : setPage(activePage + dotsJump <= total ? activePage + dotsJump : total)
|
|
880
|
+
*
|
|
881
|
+
* Aquí lo hace `goTo`, que ya aplica el mismo clamp.
|
|
882
|
+
*/
|
|
883
|
+
const DOTS_JUMP = 5;
|
|
884
|
+
/**
|
|
885
|
+
* El intercambio de iconos de la elipsis, tal cual lo declaraba el `tv` de v2
|
|
886
|
+
* (`@heroui/theme@2.4.15`, `dist/components/pagination.js:186-192`):
|
|
887
|
+
*
|
|
888
|
+
* forwardIcon: ["hidden", "group-hover:block",
|
|
889
|
+
* "group-data-[focus-visible=true]:block", "data-[before=true]:rotate-180"]
|
|
890
|
+
* ellipsis: "group-hover:hidden group-data-[focus-visible=true]:hidden"
|
|
891
|
+
*
|
|
892
|
+
* `data-focus-visible` SÍ sobrevive a v3 — no es de los atributos que react-aria renombró
|
|
893
|
+
* (`data-hover` → `data-hovered`, skill `auraui-components` trampa 3): el `Button` de
|
|
894
|
+
* react-aria-components lo escribe (`dist/private/Button.mjs:101`) y el propio
|
|
895
|
+
* `.pagination__link` de v3 lo usa (`@heroui/styles/dist/components/pagination.css:51-53`).
|
|
896
|
+
* El hover va por `group-hover:` (CSS `:hover`), igual que en ese mismo CSS.
|
|
897
|
+
*/
|
|
898
|
+
const DOTS_REST_CLASS = "group-hover:hidden group-data-[focus-visible=true]:hidden";
|
|
899
|
+
const DOTS_JUMP_ICON_CLASS = "hidden group-hover:block group-data-[focus-visible=true]:block data-[before=true]:rotate-180";
|
|
900
|
+
/**
|
|
901
|
+
* Los tres puntos en reposo: copia literal del `EllipsisIcon` de v2
|
|
902
|
+
* (`@heroui/shared-icons@2.1.9`, `dist/ellipsis.js`) — tres `circle` de `r=1` en (5,12),
|
|
903
|
+
* (12,12) y (19,12) sobre un `viewBox` de 24 y `1em` de lado.
|
|
904
|
+
*
|
|
905
|
+
* NO vale el `…` (U+2026) que pinta `Pagination.Ellipsis` de v3
|
|
906
|
+
* (`@heroui/react/dist/components/pagination/pagination.js:231`, children hardcodeado y
|
|
907
|
+
* posterior al spread de props, o sea imposible de sustituir): ese glifo se apoya en la línea
|
|
908
|
+
* base y queda más bajo que los puntos de v2. Medido sobre
|
|
909
|
+
* `vrt/__baselines__/custom-components-pagination--default-light.png`.
|
|
910
|
+
*/
|
|
911
|
+
const EllipsisDotsIcon = () => (jsxRuntime.jsxs("svg", { "aria-hidden": "true", className: DOTS_REST_CLASS, fill: "none", height: "1em", shapeRendering: "geometricPrecision", strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: "1.5", viewBox: "0 0 24 24", width: "1em", children: [jsxRuntime.jsx("circle", { cx: "12", cy: "12", fill: "currentColor", r: "1" }), jsxRuntime.jsx("circle", { cx: "19", cy: "12", fill: "currentColor", r: "1" }), jsxRuntime.jsx("circle", { cx: "5", cy: "12", fill: "currentColor", r: "1" })] }));
|
|
912
|
+
/**
|
|
913
|
+
* El doble galón que v2 mostraba al pasar el ratón: `ForwardIcon` de
|
|
914
|
+
* `@heroui/shared-icons@2.1.9` (`dist/forward.js`), los dos trazos `M13 17l5-5-5-5` y
|
|
915
|
+
* `M6 17l5-5-5-5` → `»`. Con `data-before="true"` gira 180° y se lee `«`, igual que en v2.
|
|
916
|
+
*/
|
|
917
|
+
const JumpIcon = ({ isBefore }) => (jsxRuntime.jsxs("svg", { "aria-hidden": "true", className: DOTS_JUMP_ICON_CLASS, "data-before": isBefore ? "true" : undefined, fill: "none", focusable: "false", height: "1em", role: "presentation", shapeRendering: "geometricPrecision", stroke: "currentColor", strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: "1.5", viewBox: "0 0 24 24", width: "1em", children: [jsxRuntime.jsx("path", { d: "M13 17l5-5-5-5" }), jsxRuntime.jsx("path", { d: "M6 17l5-5-5-5" })] }));
|
|
860
918
|
/**
|
|
861
919
|
* Rango de páginas con elipsis, **igual que el algoritmo de HeroUI v2** (v3 no trae ninguno: sus
|
|
862
920
|
* piezas son puramente presentacionales). Reproduce `siblings: 1` y `boundaries: 1`, sus defaults.
|
|
@@ -902,14 +960,23 @@ function getPaginationRange(total, current) {
|
|
|
902
960
|
* `date-picker` en D-G1-3). Esta fachada reconstruye el comportamiento con estado (rango de
|
|
903
961
|
* páginas + elipsis) manteniendo la API pública v2 (`total`/`page`/`onChange`).
|
|
904
962
|
*
|
|
963
|
+
* Reconstruido además (reportado desde `bewe-analytics`, tabla de 34 páginas):
|
|
964
|
+
* - **La elipsis vuelve a ser un control**: salta ±`DOTS_JUMP` (5) páginas y al pasar el ratón
|
|
965
|
+
* o recibir foco de teclado cambia los tres puntos por el doble galón `«`/`»`, como v2. Las
|
|
966
|
+
* apps no pueden arreglarlo por su lado: esta fachada compone la paginación por dentro.
|
|
967
|
+
* - **Nombre accesible en anterior/siguiente**: sus galones son `aria-hidden` en v3, así que sin
|
|
968
|
+
* `aria-label` los dos botones llegaban vacíos al árbol de accesibilidad.
|
|
969
|
+
*
|
|
970
|
+
|
|
905
971
|
* ⚠️ `size` se acepta pero no se aplica: mismo bug preexistente que v2 (destructurado, nunca
|
|
906
972
|
* reenviado a HeroUI) — fuera de scope de esta migración, reportado aparte (ver
|
|
907
973
|
* findings/decisiones-aceptadas-g2.md y f5-L7-sueltos.md · P-17). v3 SÍ tiene la prop (emite
|
|
908
974
|
* `.pagination--sm|--md|--lg`), así que arreglarlo es un cambio de tamaño visible: ticket aparte.
|
|
909
975
|
*/
|
|
910
|
-
const Pagination = ({ total, page, initialPage = 1, onChange, size: _size = "sm", className, isDisabled, }) => {
|
|
976
|
+
const Pagination = ({ total, page, initialPage = 1, onChange, size: _size = "sm", className, isDisabled, translations = {}, }) => {
|
|
911
977
|
const [internalPage, setInternalPage] = $8WNJc$react.useState(initialPage);
|
|
912
978
|
const currentPage = page ?? internalPage;
|
|
979
|
+
const t = { ...DEFAULT_TRANSLATIONS$f, ...translations };
|
|
913
980
|
const goTo = (next) => {
|
|
914
981
|
const clamped = Math.min(Math.max(next, 1), total);
|
|
915
982
|
if (page === undefined) {
|
|
@@ -918,13 +985,38 @@ const Pagination = ({ total, page, initialPage = 1, onChange, size: _size = "sm"
|
|
|
918
985
|
onChange?.(clamped);
|
|
919
986
|
};
|
|
920
987
|
const items = getPaginationRange(total, currentPage);
|
|
988
|
+
// Qué elipsis retrocede y cuál avanza, con el mismo criterio de v2:
|
|
989
|
+
// `isBefore = index < range.indexOf(activePage)` (`@heroui/pagination@2.2.20`,
|
|
990
|
+
// `dist/pagination.js:464`). Si `page` viene fuera de rango, `indexOf` da -1 y todas las
|
|
991
|
+
// elipsis avanzan — también igual que v2, que no protegía ese caso.
|
|
992
|
+
const activeIndex = items.indexOf(currentPage);
|
|
921
993
|
return (
|
|
922
994
|
// `w-fit` reproduce el `max-w-fit` del wrapper de v2: `.pagination` de v3 es `w-full` +
|
|
923
995
|
// `justify-between`, así que dentro de un `flex justify-center` (como el de AccordionList) la
|
|
924
996
|
// tira se quedaba pegada a la izquierda en vez de centrarse.
|
|
925
|
-
jsxRuntime.jsx(react.Pagination, { className: ["w-fit", className].filter(Boolean).join(" "), children: jsxRuntime.jsxs(react.Pagination.Content, { className: STRIP_CLASS, children: [jsxRuntime.jsx(react.Pagination.Item, { children: jsxRuntime.jsx(react.Pagination.Previous
|
|
926
|
-
|
|
927
|
-
|
|
997
|
+
jsxRuntime.jsx(react.Pagination, { className: ["w-fit", className].filter(Boolean).join(" "), children: jsxRuntime.jsxs(react.Pagination.Content, { className: STRIP_CLASS, children: [jsxRuntime.jsx(react.Pagination.Item, { children: jsxRuntime.jsx(react.Pagination.Previous
|
|
998
|
+
// v3 mete el galón en un `<span aria-hidden="true">`
|
|
999
|
+
// (`pagination.js:165`), así que sin esto el botón se queda SIN nombre
|
|
1000
|
+
// accesible: un lector de pantalla lo anuncia como "botón" a secas. v2 sí
|
|
1001
|
+
// lo nombraba ("previous page button", `use-pagination.js:213`), pero en
|
|
1002
|
+
// inglés y hardcodeado; aquí va por `translations` (regla obligatoria de
|
|
1003
|
+
// i18n del repo, skill `auraui-i18n`).
|
|
1004
|
+
, { "aria-label": t.previousPageAriaLabel, className: NAV_START_CLASS, isDisabled: isDisabled || currentPage <= 1, onPress: () => goTo(currentPage - 1), children: jsxRuntime.jsx(react.Pagination.PreviousIcon, {}) }) }), items.map((item, index) => {
|
|
1005
|
+
if (item === "ellipsis") {
|
|
1006
|
+
const isBefore = index < activeIndex;
|
|
1007
|
+
const label = isBefore
|
|
1008
|
+
? t.jumpBackwardAriaLabel
|
|
1009
|
+
: t.jumpForwardAriaLabel;
|
|
1010
|
+
return (
|
|
1011
|
+
// biome-ignore lint/suspicious/noArrayIndexKey: solo hay como máximo 2 elipsis, posición estable
|
|
1012
|
+
jsxRuntime.jsx(react.Pagination.Item, { children: jsxRuntime.jsxs(react.Pagination.Link, { "aria-label": label.replace("{count}", String(DOTS_JUMP)), className: `group ${CELL_CLASS} ${INNER_RADIUS_CLASS}`, isDisabled: isDisabled, onPress: () => goTo(isBefore
|
|
1013
|
+
? currentPage - DOTS_JUMP
|
|
1014
|
+
: currentPage + DOTS_JUMP), children: [jsxRuntime.jsx(EllipsisDotsIcon, {}), jsxRuntime.jsx(JumpIcon, { isBefore: isBefore })] }) }, `ellipsis-${index}`));
|
|
1015
|
+
}
|
|
1016
|
+
return (jsxRuntime.jsx(react.Pagination.Item, { children: jsxRuntime.jsx(react.Pagination.Link, { className: `${CELL_CLASS} ${item === currentPage ? ACTIVE_LINK_CLASS : INNER_RADIUS_CLASS}`, isActive: item === currentPage, isDisabled: isDisabled, onPress: () => goTo(item), children: item }) }, item));
|
|
1017
|
+
}), jsxRuntime.jsx(react.Pagination.Item, { children: jsxRuntime.jsx(react.Pagination.Next
|
|
1018
|
+
/* Mismo motivo que en `Previous`: el galón es `aria-hidden`. */
|
|
1019
|
+
, { "aria-label": t.nextPageAriaLabel, className: NAV_END_CLASS, isDisabled: isDisabled || currentPage >= total, onPress: () => goTo(currentPage + 1), children: jsxRuntime.jsx(react.Pagination.NextIcon, {}) }) })] }) }));
|
|
928
1020
|
};
|
|
929
1021
|
|
|
930
1022
|
// TECH-2459 · F5 · P-01 — Valores tomados del tema v2 REAL (`@heroui/theme@2.4.15`,
|
|
@@ -1501,9 +1593,13 @@ const FIELD_SHADOW_CLASS$2 = "shadow-xs";
|
|
|
1501
1593
|
// borde transparente de 2px NO es inocuo, porque con `box-sizing: border-box` come 2px por
|
|
1502
1594
|
// lado y empuja el texto (medido: la sangría salía 14px en vez de los 12px de la referencia).
|
|
1503
1595
|
// 2. `underlined` usa `!px-1` (4px), no el padding del tamaño, y su sombra es
|
|
1504
|
-
// `0 1px 0 0 rgba(0,0,0,.05)` en vez de `shadow-sm`.
|
|
1505
|
-
//
|
|
1506
|
-
//
|
|
1596
|
+
// `0 1px 0 0 rgba(0,0,0,.05)` en vez de `shadow-sm`. **El `!` sigue siendo imprescindible aunque
|
|
1597
|
+
// estas clases ya pasen por `cn`**: tailwind-merge 3 trata la forma con `!` como un grupo
|
|
1598
|
+
// APARTE de la forma sin `!`, así que NO resuelve `px-3` contra `px-1!` — medido con el `cn`
|
|
1599
|
+
// real del paquete instalado, `cn("px-3","px-1!")` → `"px-3 px-1!"`, las dos en el DOM. Quien
|
|
1600
|
+
// decide es entonces la `!important`, no el orden del array ni el del CSS generado (sin el `!`
|
|
1601
|
+
// ganaba `px-3`; medido computado con el `!`: `padding-inline: 4px`). No lo quites al ver que la
|
|
1602
|
+
// cadena ya se fusiona.
|
|
1507
1603
|
// 3. `isInvalid` en v2 teñía TODO de `danger` —borde, label y texto/placeholder—, no solo el
|
|
1508
1604
|
// borde; y en `flat` cambiaba el fondo a `danger-50`.
|
|
1509
1605
|
// ⚠️ `bordered` NO lleva fondo, a propósito (TECH-2459 · F5, hallazgo de S2 + queja directa del
|
|
@@ -1531,6 +1627,44 @@ const VARIANT_CLASS$3 = {
|
|
|
1531
1627
|
// píxeles del placeholder son `113,113,122` (el gris `default-500`), no el rosa `243,18,96`.
|
|
1532
1628
|
const INVALID_LABEL_CLASS$2 = "text-danger";
|
|
1533
1629
|
const INVALID_TEXT_CLASS$2 = "text-danger";
|
|
1630
|
+
// ── El `className` del consumidor: FUSIONADO y en la RAÍZ (TECH-2459 · P-?) ──────────────────
|
|
1631
|
+
//
|
|
1632
|
+
// Dos defectos en uno, y los dos se veían igual: el ancho que pide la app no llegaba nunca.
|
|
1633
|
+
//
|
|
1634
|
+
// 1. **Se concatenaba en vez de fusionarse.** Esta fachada montaba sus `className` con
|
|
1635
|
+
// `[...].filter(Boolean).join(" ")`, así que `w-full` (de `fullWidth`, que por defecto es
|
|
1636
|
+
// `true`) y el `w-48` del consumidor llegaban LOS DOS al DOM y decidía el orden del CSS
|
|
1637
|
+
// emitido, no el del array — y gana `w-full`. Medido en el banco de pruebas: un campo con
|
|
1638
|
+
// `className="w-48"` dentro de una fila flex de 592px medía 290px, exactamente lo mismo que su
|
|
1639
|
+
// hermano sin `className`; en un bloque de 1287px, un `w-56` medía 1287px.
|
|
1640
|
+
// v2 NO se comportaba así: `useInput` (`@heroui/input@2.4.22`,
|
|
1641
|
+
// `dist/chunk-F2LJ3HQE.mjs:68,166`) metía el `className` del consumidor en
|
|
1642
|
+
// `slots.base({ class: baseStyles })`, y el `tv` de v2 lleva `twMerge: true` por defecto
|
|
1643
|
+
// (`@heroui/theme@2.4.15`, `dist/utils/tv.js:56`) → tailwind-merge BORRABA el `w-full` y el
|
|
1644
|
+
// ancho del consumidor ganaba. Por eso aquí se usa `cn` (mismo tailwind-merge, vía
|
|
1645
|
+
// `tailwind-variants`) con el `className` del consumidor en ÚLTIMA posición, igual que las 33
|
|
1646
|
+
// fachadas del repo que ya lo hacen (`Chip.tsx:398`, `Checkbox.tsx:224`).
|
|
1647
|
+
//
|
|
1648
|
+
// 2. **Aterrizaba en la pieza equivocada.** El `className` se emitía en el `InputGroup` (el
|
|
1649
|
+
// equivalente del slot `inputWrapper` de v2), no en la raíz. En v2 iba al slot `base`
|
|
1650
|
+
// —`"group flex flex-col data-[hidden=true]:hidden"` + el `w-full` de `fullWidth`
|
|
1651
|
+
// (`@heroui/theme@2.4.15`, `dist/components/input.js:7452`)—, que es justo donde vive el ancho.
|
|
1652
|
+
// Fusionar en el `InputGroup` no habría bastado: la raíz seguiría con su `w-full` y el campo
|
|
1653
|
+
// quedaría a 192px DESBORDANDO un hueco de 83. Así que el `className` se muda a la raíz, que es
|
|
1654
|
+
// donde lo tenía v2; quien quiera tocar la caja del campo sigue teniendo `classNames.inputWrapper`,
|
|
1655
|
+
// el mismo canal que en v2 (10+ usos reales en las apps: `bg-content1`, `h-9`, `shadow-none`…).
|
|
1656
|
+
//
|
|
1657
|
+
// Consecuencia del punto 2: el `w-full` del `InputGroup` deja de depender de `fullWidth` y pasa a
|
|
1658
|
+
// ser incondicional — que es LITERALMENTE lo que tenía v2 (`inputWrapper: "relative w-full
|
|
1659
|
+
// inline-flex …"`, sin variante que lo condicione). `fullWidth` decide solo el ancho de la RAÍZ,
|
|
1660
|
+
// como en su `tv`. Sin esto, un `fullWidth={false}` con un `w-48` del consumidor dejaría la caja
|
|
1661
|
+
// del campo encogida a su contenido dentro de una raíz de 192px.
|
|
1662
|
+
//
|
|
1663
|
+
// ⚠️ Lo que `cn` NO resuelve, y por eso el `!` de la variante `underlined` sigue siendo necesario:
|
|
1664
|
+
// tailwind-merge 3 trata la forma con `!` como un grupo APARTE de la forma sin `!`, así que
|
|
1665
|
+
// `px-3` y `px-1!` sobreviven las dos (comprobado con el `cn` real del paquete instalado) y sigue
|
|
1666
|
+
// ganando la `!important`. Está anotado también en el punto 2 de `VARIANT_CLASS`, que es donde se
|
|
1667
|
+
// lee al tocar esas clases.
|
|
1534
1668
|
/**
|
|
1535
1669
|
* Input genérico reutilizable basado en HeroUI v3 (TextField + InputGroup + Input) siguiendo las
|
|
1536
1670
|
* reglas de diseño BeweOS (TECH-2459 · F3.0).
|
|
@@ -1597,45 +1731,14 @@ fullWidth = true, value, defaultValue, onChange, onValueChange, className, class
|
|
|
1597
1731
|
onChange(fakeEvent);
|
|
1598
1732
|
}
|
|
1599
1733
|
};
|
|
1600
|
-
return (jsxRuntime.jsxs(react.TextField, { id: inputId, name: name, isInvalid: isInvalid, isRequired: isRequired, isDisabled: isDisabled, isReadOnly: isReadOnly, className:
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
labelFlotante ? LABEL_FLOAT_CLASS : "",
|
|
1609
|
-
classNames?.label,
|
|
1610
|
-
]
|
|
1611
|
-
.filter(Boolean)
|
|
1612
|
-
.join(" "), children: label })), jsxRuntime.jsxs(react.InputGroup, { isInvalid: isInvalid, isDisabled: isDisabled, isReadOnly: isReadOnly, className: [
|
|
1613
|
-
SIZE_CLASS$4[size],
|
|
1614
|
-
radius === "full" ? PADDING_FULL_CLASS$1[size] : PADDING_CLASS$2[size],
|
|
1615
|
-
variant === "underlined" ? UNDERLINED_INNER_CLASS$1[size] : "",
|
|
1616
|
-
RADIUS_CLASS$6[radius],
|
|
1617
|
-
VARIANT_CLASS$3[variant],
|
|
1618
|
-
FIELD_FOCUS_CLASS$3,
|
|
1619
|
-
FIELD_SHADOW_CLASS$2,
|
|
1620
|
-
fullWidth ? "w-full" : "",
|
|
1621
|
-
classNames?.inputWrapper,
|
|
1622
|
-
className,
|
|
1623
|
-
]
|
|
1624
|
-
.filter(Boolean)
|
|
1625
|
-
.join(" "), children: [startContent && (jsxRuntime.jsx(react.InputGroup.Prefix, { className: "border-none bg-transparent px-0", children: startContent })), jsxRuntime.jsx(react.InputGroup.Input, { ...rest, ref: ref, required: required, type: type, placeholder: labelFlotante ? " " : placeholder, value: value, defaultValue: defaultValue, onChange: handleChange, className: [
|
|
1626
|
-
"bg-transparent p-0",
|
|
1627
|
-
SIZE_TEXT_CLASS$1[size],
|
|
1628
|
-
isInvalid ? INVALID_TEXT_CLASS$2 : "",
|
|
1629
|
-
startContent ? "ps-1.5" : "",
|
|
1630
|
-
endContent || showClear ? "pe-1.5" : "",
|
|
1631
|
-
classNames?.input,
|
|
1632
|
-
]
|
|
1633
|
-
.filter(Boolean)
|
|
1634
|
-
.join(" ") }), showClear && (jsxRuntime.jsx(react.CloseButton, { "aria-label": "Limpiar", className: "h-4 w-4 min-h-0 p-0.5 mr-1", onPress: handleClear })), endContent && (jsxRuntime.jsx(react.InputGroup.Suffix, { className: "border-none bg-transparent px-0", children: endContent }))] }), description && !isInvalid && (jsxRuntime.jsx(react.Description, { className: [HELPER_CLASS$2, "text-left", classNames?.description]
|
|
1635
|
-
.filter(Boolean)
|
|
1636
|
-
.join(" "), children: description })), isInvalid && errorMessage && (jsxRuntime.jsx(react.FieldError, { className: [HELPER_CLASS$2, "text-left", classNames?.errorMessage]
|
|
1637
|
-
.filter(Boolean)
|
|
1638
|
-
.join(" "), children: errorMessage }))] }));
|
|
1734
|
+
return (jsxRuntime.jsxs(react.TextField, { id: inputId, name: name, isInvalid: isInvalid, isRequired: isRequired, isDisabled: isDisabled, isReadOnly: isReadOnly, className: react.cn(FIELD_STACK_CLASS$3[size], fullWidth ? "w-full" : "",
|
|
1735
|
+
// El `className` del consumidor, en la raíz y en último lugar: el slot `base` de
|
|
1736
|
+
// v2 y la posición que le deja ganar en tailwind-merge (ver bloque de arriba).
|
|
1737
|
+
className), children: [label && (jsxRuntime.jsx(react.Label, { className: react.cn(LABEL_CLASS$4, LABEL_LEFT_CLASS[variant], variant === "underlined" ? "top-0" : LABEL_OFFSET_CLASS$1[size], isInvalid ? INVALID_LABEL_CLASS$2 : "", labelFlotante ? LABEL_INSIDE_CLASS[size] : "", labelFlotante ? LABEL_FLOAT_CLASS : "", classNames?.label), children: label })), jsxRuntime.jsxs(react.InputGroup, { isInvalid: isInvalid, isDisabled: isDisabled, isReadOnly: isReadOnly, className: react.cn(SIZE_CLASS$4[size], radius === "full" ? PADDING_FULL_CLASS$1[size] : PADDING_CLASS$2[size], variant === "underlined" ? UNDERLINED_INNER_CLASS$1[size] : "", RADIUS_CLASS$6[radius], VARIANT_CLASS$3[variant], FIELD_FOCUS_CLASS$3, FIELD_SHADOW_CLASS$2,
|
|
1738
|
+
// Incondicional, como el `inputWrapper` de v2: la caja del campo llena
|
|
1739
|
+
// siempre su raíz, y es la raíz la que responde a `fullWidth` y al ancho que
|
|
1740
|
+
// pase el consumidor.
|
|
1741
|
+
"w-full", classNames?.inputWrapper), children: [startContent && (jsxRuntime.jsx(react.InputGroup.Prefix, { className: "border-none bg-transparent px-0", children: startContent })), jsxRuntime.jsx(react.InputGroup.Input, { ...rest, ref: ref, required: required, type: type, placeholder: labelFlotante ? " " : placeholder, value: value, defaultValue: defaultValue, onChange: handleChange, className: react.cn("bg-transparent p-0", SIZE_TEXT_CLASS$1[size], isInvalid ? INVALID_TEXT_CLASS$2 : "", startContent ? "ps-1.5" : "", endContent || showClear ? "pe-1.5" : "", classNames?.input) }), showClear && (jsxRuntime.jsx(react.CloseButton, { "aria-label": "Limpiar", className: "h-4 w-4 min-h-0 p-0.5 mr-1", onPress: handleClear })), endContent && (jsxRuntime.jsx(react.InputGroup.Suffix, { className: "border-none bg-transparent px-0", children: endContent }))] }), description && !isInvalid && (jsxRuntime.jsx(react.Description, { className: react.cn(HELPER_CLASS$2, "text-left", classNames?.description), children: description })), isInvalid && errorMessage && (jsxRuntime.jsx(react.FieldError, { className: react.cn(HELPER_CLASS$2, "text-left", classNames?.errorMessage), children: errorMessage }))] }));
|
|
1639
1742
|
});
|
|
1640
1743
|
Input.displayName = "Input";
|
|
1641
1744
|
|
|
@@ -5596,7 +5699,17 @@ const MenuMobileBottomBar = $8WNJc$react.memo(function MenuMobileBottomBar({ ite
|
|
|
5596
5699
|
// `flex group gap-2 items-center justify-between … px-2 py-1.5 w-full`). `.list-box-item` de v3
|
|
5597
5700
|
// es `justify-start gap-3`, así que el `endContent` (el `+`, el chip `New`, el chip numérico)
|
|
5598
5701
|
// dejaba de irse al borde derecho y quedaba pegado al texto, y la etiqueta se corría 4px.
|
|
5599
|
-
|
|
5702
|
+
//
|
|
5703
|
+
// TECH-2459 · `group` — misma cita de v2 que la de arriba: su slot `base` empezaba por
|
|
5704
|
+
// `flex group …`. Es la pieza de la que dependen los CUATRO `group-data-[selected=true]:*` de
|
|
5705
|
+
// este fichero (la etiqueta de primer nivel en `ITEM_TITLE_CLASS` y los tres iconos). Sin un
|
|
5706
|
+
// ancestro con la clase `group`, esa variante de Tailwind no casa NUNCA y las cuatro reglas son
|
|
5707
|
+
// código muerto: medido en el navegador, el ítem de primer nivel SELECCIONADO se quedaba en
|
|
5708
|
+
// `default-500` (#71717a claro / #a1a1aa oscuro) donde v2 lo pintaba en `default-600`
|
|
5709
|
+
// (#52525b / #d4d4d8), así que el ítem activo no se distinguía de sus hermanos más que por la
|
|
5710
|
+
// píldora de fondo. No lo delata ni el compilador ni un grep del código: la clase está escrita
|
|
5711
|
+
// y es correcta, solo le falta el ancla.
|
|
5712
|
+
const ITEM_BASE_CLASS = "group px-3 min-h-11 rounded-[14px] h-[44px] gap-2 justify-between data-[selected=true]:bg-primary-100";
|
|
5600
5713
|
// TECH-2459 · F5 · P-13a — override del ítem COLAPSADO. Venía de `getItemClasses(isCollapsed)`
|
|
5601
5714
|
// (`MenuNavList.styles.ts` de v2, fichero que la migración eliminó): `w-11 h-11 gap-0 p-0`. Es lo
|
|
5602
5715
|
// que centraba el icono: un cuadrado exacto de 44×44 SIN padding. Sin él, el ítem colapsado
|
|
@@ -5740,7 +5853,7 @@ const MenuNavList = $8WNJc$react.forwardRef(({ items, isCollapsed, defaultSelect
|
|
|
5740
5853
|
// `MenuClawNavItem` (HTML semántico + `onClick`, sin `ListBox`).
|
|
5741
5854
|
const renderSubItem = $8WNJc$react.useCallback((item) => {
|
|
5742
5855
|
const isSelected = item.key === selectedKey;
|
|
5743
|
-
return (jsxRuntime.jsx("li", { children: jsxRuntime.jsxs("button", { type: "button", "data-menu-subitem-selected": isSelected ? "true" : "false", "aria-current": isSelected ? "page" : undefined, className: react.cn("flex w-full items-center justify-between gap-2 min-h-9 rounded-[14px] px-3 py-2 menu-nav-list__sub-item text-left", "text-default-500 hover:text-default-700 transition-colors outline-none", "focus-visible:z-10 focus-visible:outline-2 focus-visible:outline-focus focus-visible:outline-offset-2", isSelected && "selected"), onClick: () => handleItemPress(item), children: [jsxRuntime.jsx(react.Label, { children: item.title }), item.endContent ?? null] }) }, item.key));
|
|
5856
|
+
return (jsxRuntime.jsx("li", { children: jsxRuntime.jsxs("button", { type: "button", "data-menu-subitem-selected": isSelected ? "true" : "false", "aria-current": isSelected ? "page" : undefined, className: react.cn("flex w-full items-center justify-between gap-2 min-h-9 rounded-[14px] px-3 py-2 menu-nav-list__sub-item text-left", "text-default-500 hover:text-default-700 transition-colors outline-none", "focus-visible:z-10 focus-visible:outline-2 focus-visible:outline-focus focus-visible:outline-offset-2", isSelected && "selected"), onClick: () => handleItemPress(item), children: [jsxRuntime.jsx(react.Label, { className: "text-sm font-normal text-inherit", children: item.title }), item.endContent ?? null] }) }, item.key));
|
|
5744
5857
|
}, [handleItemPress, selectedKey]);
|
|
5745
5858
|
// Renders a nested item, i.e., an item that has sub-items.
|
|
5746
5859
|
// biome-ignore lint/correctness/useExhaustiveDependencies: <explanation>
|
|
@@ -8630,9 +8743,11 @@ const FIELD_STACK_CLASS$1 = {
|
|
|
8630
8743
|
* metido en un hueco estrecho), no una reposición. El solape del chevron se arregla en
|
|
8631
8744
|
* `VALUE_ROOM_CLASS`, que es donde v2 lo tenía resuelto.
|
|
8632
8745
|
*
|
|
8633
|
-
* Va antes del `className` del consumidor
|
|
8634
|
-
*
|
|
8635
|
-
*
|
|
8746
|
+
* Va antes del `className` del consumidor, y ahora las dos pasan por `cn`: quien necesite otro
|
|
8747
|
+
* mínimo pasa su propio `min-w-*` y tailwind-merge BORRA este — comprobado con el `cn` real
|
|
8748
|
+
* (`cn("min-w-40","min-w-[180px]")` → `"min-w-[180px]"`). Antes las dos llegaban al DOM y decidía
|
|
8749
|
+
* el orden en que Tailwind 4 las emitiera, no el del array; es el mismo mecanismo que tenía el
|
|
8750
|
+
* `min-w-40` del `tv` de v2, que también fusionaba.
|
|
8636
8751
|
*/
|
|
8637
8752
|
const ROOT_MIN_WIDTH_CLASS = "min-w-40";
|
|
8638
8753
|
// v3 no tiene el vocabulario `variant` de v2 (solo "primary"/"secondary", sin borde visible por
|
|
@@ -8643,9 +8758,13 @@ const ROOT_MIN_WIDTH_CLASS = "min-w-40";
|
|
|
8643
8758
|
// borde transparente de 2px NO es inocuo, porque con `box-sizing: border-box` come 2px por
|
|
8644
8759
|
// lado y empuja el texto (medido: la sangría salía 14px en vez de los 12px de la referencia).
|
|
8645
8760
|
// 2. `underlined` usa `!px-1` (4px), no el padding del tamaño, y su sombra es
|
|
8646
|
-
// `0 1px 0 0 rgba(0,0,0,.05)` en vez de `shadow-sm`.
|
|
8647
|
-
//
|
|
8648
|
-
//
|
|
8761
|
+
// `0 1px 0 0 rgba(0,0,0,.05)` en vez de `shadow-sm`. **El `!` sigue siendo imprescindible aunque
|
|
8762
|
+
// estas clases ya pasen por `cn`**: tailwind-merge 3 trata la forma con `!` como un grupo
|
|
8763
|
+
// APARTE de la forma sin `!`, así que NO resuelve `px-3` contra `px-1!` — medido con el `cn`
|
|
8764
|
+
// real del paquete instalado, `cn("px-3","px-1!")` → `"px-3 px-1!"`, las dos en el DOM. Quien
|
|
8765
|
+
// decide es entonces la `!important`, no el orden del array ni el del CSS generado (sin el `!`
|
|
8766
|
+
// ganaba `px-3`; medido computado con el `!`: `padding-inline: 4px`). No lo quites al ver que la
|
|
8767
|
+
// cadena ya se fusiona.
|
|
8649
8768
|
// 3. `isInvalid` en v2 teñía TODO de `danger` —borde, label y texto/placeholder—, no solo el
|
|
8650
8769
|
// borde; y en `flat` cambiaba el fondo a `danger-50`.
|
|
8651
8770
|
// ⚠️ `bordered` NO lleva fondo, a propósito (TECH-2459 · F5, hallazgo de S2 + queja directa del
|
|
@@ -8781,49 +8900,39 @@ placeholder = "", selectionMode = "single", selectedKeys, defaultSelectedKeys, o
|
|
|
8781
8900
|
// `undefined` y el campo dejaba de llenar su contenedor: en una fila flex se encogía al ancho
|
|
8782
8901
|
// de su contenido (medido por S2: 208px dentro de una fila de 592, 336px de hueco). En una
|
|
8783
8902
|
// columna no se notaba porque el `stretch` del flex lo tapaba — pero afecta a cualquier app.
|
|
8784
|
-
fullWidth = true, isOpen, defaultOpen, onOpenChange, className, classNames, }) => {
|
|
8903
|
+
fullWidth = true, isOpen, defaultOpen, onOpenChange, className, classNames, "aria-label": ariaLabel, "aria-labelledby": ariaLabelledBy, "aria-describedby": ariaDescribedBy, "aria-details": ariaDetails, }) => {
|
|
8785
8904
|
const generatedId = $8WNJc$react.useId();
|
|
8786
8905
|
const selectId = id || generatedId;
|
|
8787
8906
|
const requiredField = isRequired;
|
|
8788
8907
|
// Mismo criterio que el `group-data-[has-helper=true]` de v2: cuenta como "con ayuda" el campo
|
|
8789
8908
|
// que efectivamente PINTA una descripción o un mensaje de error, no el que solo recibe la prop.
|
|
8790
8909
|
const hasHelper = Boolean((description && !isInvalid) || (isInvalid && errorMessage));
|
|
8791
|
-
const triggerClassName = [
|
|
8792
|
-
|
|
8793
|
-
|
|
8794
|
-
|
|
8795
|
-
|
|
8796
|
-
|
|
8797
|
-
|
|
8798
|
-
|
|
8799
|
-
|
|
8800
|
-
|
|
8801
|
-
|
|
8802
|
-
|
|
8803
|
-
|
|
8804
|
-
|
|
8805
|
-
]
|
|
8806
|
-
.filter(Boolean)
|
|
8807
|
-
.join(" ");
|
|
8808
|
-
const inner = (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [label && (jsxRuntime.jsx(react.Label, { className: [
|
|
8809
|
-
LABEL_CLASS$1,
|
|
8810
|
-
hasHelper
|
|
8811
|
-
? LABEL_OFFSET_HELPER_CLASS[size]
|
|
8812
|
-
: LABEL_OFFSET_CLASS[size],
|
|
8813
|
-
isInvalid ? INVALID_LABEL_CLASS$1 : "",
|
|
8814
|
-
classNames?.label,
|
|
8815
|
-
]
|
|
8816
|
-
.filter(Boolean)
|
|
8817
|
-
.join(" "), children: label })), jsxRuntime.jsxs(react.Select.Trigger, { className: triggerClassName, children: [jsxRuntime.jsx(react.Select.Value, { className: [
|
|
8818
|
-
SIZE_TEXT_CLASS[size],
|
|
8819
|
-
VALUE_ROOM_CLASS,
|
|
8820
|
-
isInvalid ? INVALID_TEXT_CLASS$1 : "",
|
|
8821
|
-
]
|
|
8822
|
-
.filter(Boolean)
|
|
8823
|
-
.join(" ") }), 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 }))] }));
|
|
8910
|
+
const triggerClassName = react.cn(SIZE_CLASS$1[size], radius === "full" ? PADDING_FULL_CLASS[size] : PADDING_CLASS[size], TRIGGER_ALIGN_CLASS, RADIUS_CLASS$4[radius], VARIANT_CLASS$1[variant],
|
|
8911
|
+
// En v2 el estado inválido MANDABA también con el campo enfocado
|
|
8912
|
+
// (`group-data-[focus=true]:!border-danger`), así que el borde de foco no se aplica cuando
|
|
8913
|
+
// el campo está en error: se queda el `danger`.
|
|
8914
|
+
isInvalid ? INVALID_VARIANT_CLASS[variant] : FOCUS_BORDER_CLASS, FIELD_FOCUS_CLASS$1, FIELD_SHADOW_CLASS$1,
|
|
8915
|
+
// Incondicional, como el slot `trigger` de v2 (`"relative px-3 gap-3 w-full inline-flex …"`,
|
|
8916
|
+
// sin variante que lo condicione): el disparador llena siempre su raíz, y es la raíz la que
|
|
8917
|
+
// responde a `fullWidth` y al ancho que pase el consumidor. v3 hace lo mismo por su cuenta
|
|
8918
|
+
// con `.select__trigger--full-width`, pero esa regla vive en la capa `components` y la pisa
|
|
8919
|
+
// cualquier utilidad.
|
|
8920
|
+
"w-full", classNames?.trigger);
|
|
8921
|
+
const inner = (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [label && (jsxRuntime.jsx(react.Label, { className: react.cn(LABEL_CLASS$1, hasHelper
|
|
8922
|
+
? 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 }))] }));
|
|
8824
8924
|
const commonProps = {
|
|
8825
8925
|
id: selectId,
|
|
8826
8926
|
name,
|
|
8927
|
+
// Etiquetado accesible. Va en la RAÍZ y no en el disparador a propósito: el nombre del
|
|
8928
|
+
// disparador lo compone react-aria a partir de las props de la raíz (`useSelect` →
|
|
8929
|
+
// `useLabel`), y puesto en `Select.Trigger` se quedaría como un `aria-label` suelto que
|
|
8930
|
+
// compite con el `aria-labelledby` que esa misma pieza ya emite. Comprobado en el ÁRBOL DE
|
|
8931
|
+
// ACCESIBILIDAD, no reconstruido a mano — ver `Select.test.tsx` y el reporte del lane.
|
|
8932
|
+
"aria-label": ariaLabel,
|
|
8933
|
+
"aria-labelledby": ariaLabelledBy,
|
|
8934
|
+
"aria-describedby": ariaDescribedBy,
|
|
8935
|
+
"aria-details": ariaDetails,
|
|
8827
8936
|
placeholder,
|
|
8828
8937
|
disabledKeys,
|
|
8829
8938
|
isInvalid,
|
|
@@ -8833,15 +8942,18 @@ fullWidth = true, isOpen, defaultOpen, onOpenChange, className, classNames, }) =
|
|
|
8833
8942
|
defaultOpen,
|
|
8834
8943
|
onOpenChange,
|
|
8835
8944
|
fullWidth,
|
|
8836
|
-
|
|
8837
|
-
|
|
8838
|
-
|
|
8839
|
-
|
|
8840
|
-
|
|
8841
|
-
|
|
8842
|
-
|
|
8843
|
-
|
|
8844
|
-
|
|
8945
|
+
// `cn`, no una concatenación: el `className` del consumidor tiene que GANAR a las clases
|
|
8946
|
+
// de la fachada, y con una cadena suelta llegan las dos al DOM y decide el orden en que
|
|
8947
|
+
// Tailwind 4 las emite. Con `fullWidth` por defecto a `true`, eso dejaba `w-full` ganando
|
|
8948
|
+
// a cualquier ancho que pasara la app: medido, un `className="w-48"` en una fila flex de
|
|
8949
|
+
// 592px daba 290px (lo mismo que sin clase) y un `w-56` en un bloque de 1287px daba 1287.
|
|
8950
|
+
// v2 fusionaba: `useSelect` metía el `className` en `slots.base({ class: baseStyles })`
|
|
8951
|
+
// (`@heroui/select@2.4.22`, `dist/chunk-ONGPOZUT.mjs:171,202`) y el `tv` de v2 lleva
|
|
8952
|
+
// `twMerge: true` por defecto (`@heroui/theme@2.4.15`, `dist/utils/tv.js:56`).
|
|
8953
|
+
className: react.cn(FIELD_STACK_CLASS$1[size],
|
|
8954
|
+
// `w-full` y `min-w-40` son las dos ramas EXCLUYENTES de `fullWidth` en el `tv` de v2
|
|
8955
|
+
// (ver `ROOT_MIN_WIDTH_CLASS`).
|
|
8956
|
+
fullWidth ? "w-full" : ROOT_MIN_WIDTH_CLASS, className),
|
|
8845
8957
|
};
|
|
8846
8958
|
if (selectionMode === "multiple") {
|
|
8847
8959
|
return (jsxRuntime.jsx(react.Select, { ...commonProps, selectionMode: "multiple", value: toArray(selectedKeys), defaultValue: toArray(defaultSelectedKeys), onChange: (keys) => onSelectionChange?.(new Set(keys)), children: inner }));
|
|
@@ -9132,9 +9244,13 @@ const FIELD_STACK_CLASS = "flex flex-col gap-1.5";
|
|
|
9132
9244
|
// borde transparente de 2px NO es inocuo, porque con `box-sizing: border-box` come 2px por
|
|
9133
9245
|
// lado y empuja el texto (medido: la sangría salía 14px en vez de los 12px de la referencia).
|
|
9134
9246
|
// 2. `underlined` usa `!px-1` (4px), no el padding del tamaño, y su sombra es
|
|
9135
|
-
// `0 1px 0 0 rgba(0,0,0,.05)` en vez de `shadow-sm`.
|
|
9136
|
-
//
|
|
9137
|
-
//
|
|
9247
|
+
// `0 1px 0 0 rgba(0,0,0,.05)` en vez de `shadow-sm`. **El `!` sigue siendo imprescindible aunque
|
|
9248
|
+
// estas clases ya pasen por `cn`**: tailwind-merge 3 trata la forma con `!` como un grupo
|
|
9249
|
+
// APARTE de la forma sin `!`, así que NO resuelve `px-3` contra `px-1!` — medido con el `cn`
|
|
9250
|
+
// real del paquete instalado, `cn("px-3","px-1!")` → `"px-3 px-1!"`, las dos en el DOM. Quien
|
|
9251
|
+
// decide es entonces la `!important`, no el orden del array ni el del CSS generado (sin el `!`
|
|
9252
|
+
// ganaba `px-3`; medido computado con el `!`: `padding-inline: 4px`). No lo quites al ver que la
|
|
9253
|
+
// cadena ya se fusiona.
|
|
9138
9254
|
// 3. `isInvalid` en v2 teñía TODO de `danger` —borde, label y texto/placeholder—, no solo el
|
|
9139
9255
|
// borde; y en `flat` cambiaba el fondo a `danger-50`.
|
|
9140
9256
|
// ⚠️ `bordered` NO lleva fondo, a propósito (TECH-2459 · F5, hallazgo de S2 + queja directa del
|
|
@@ -9159,6 +9275,24 @@ const VARIANT_CLASS = {
|
|
|
9159
9275
|
// placeholder en el gris `default-500` (`113,113,122`) pese a ser un campo inválido.
|
|
9160
9276
|
const INVALID_LABEL_CLASS = "text-danger";
|
|
9161
9277
|
const INVALID_TEXT_CLASS = "text-danger";
|
|
9278
|
+
// ── El `className` del consumidor: FUSIONADO y en la RAÍZ ────────────────────────────────────
|
|
9279
|
+
//
|
|
9280
|
+
// Mismo defecto doble que en `Input.tsx` (ver ahí el razonamiento completo y las medidas), porque
|
|
9281
|
+
// en v2 este componente ERA el mismo: el `Textarea` de v2 es el `Input` con `isMultiline`, y pasa
|
|
9282
|
+
// por el mismo `useInput` (`@heroui/input@2.4.22`, `dist/chunk-F2LJ3HQE.mjs:68,166`), que mete el
|
|
9283
|
+
// `className` del consumidor en `slots.base({ class: baseStyles })` — fusionado con tailwind-merge
|
|
9284
|
+
// y en la RAÍZ, no en el elemento del campo.
|
|
9285
|
+
//
|
|
9286
|
+
// Aquí el `className` se emitía sobre el `<textarea>` y, además, concatenado: `w-full` y el ancho
|
|
9287
|
+
// del consumidor llegaban los dos al DOM y ganaba `w-full`. Medido: un `Textarea` con
|
|
9288
|
+
// `className="w-48"` en una fila flex de 592px medía 290px, lo mismo que su hermano sin clase.
|
|
9289
|
+
//
|
|
9290
|
+
// El `w-full` del `<textarea>` pasa a ser incondicional porque también lo era en v2: su slot
|
|
9291
|
+
// `input` es `["w-full font-normal bg-transparent …"]`, sin variante que lo condicione
|
|
9292
|
+
// (`@heroui/theme@2.4.15`, `dist/components/input.js`). `fullWidth` decide solo el ancho de la raíz.
|
|
9293
|
+
//
|
|
9294
|
+
// El canal para tocar la caja del campo sigue siendo `classNames.input`/`classNames.inputWrapper`,
|
|
9295
|
+
// igual que en v2.
|
|
9162
9296
|
/**
|
|
9163
9297
|
* Textarea genérico reutilizable basado en HeroUI v3 (TextField + TextArea) siguiendo las reglas
|
|
9164
9298
|
* de diseño BeweOS (TECH-2459 · F3 · G1-forms). La API pública v2 (label, description,
|
|
@@ -9190,32 +9324,13 @@ fullWidth = true, value, defaultValue, onChange, onValueChange, className, class
|
|
|
9190
9324
|
onChange?.(event);
|
|
9191
9325
|
onValueChange?.(event.target.value);
|
|
9192
9326
|
};
|
|
9193
|
-
return (jsxRuntime.jsxs(react.TextField, { id: textareaId, name: name, isInvalid: isInvalid, isRequired: isRequired, isDisabled: isDisabled, isReadOnly: isReadOnly, className:
|
|
9194
|
-
|
|
9195
|
-
|
|
9196
|
-
|
|
9197
|
-
|
|
9198
|
-
|
|
9199
|
-
]
|
|
9200
|
-
.filter(Boolean)
|
|
9201
|
-
.join(" "), children: label })), jsxRuntime.jsx(react.TextArea, { ...rest, ref: ref, placeholder: placeholder, value: value, defaultValue: defaultValue, onChange: handleChange, className: [
|
|
9202
|
-
SIZE_CLASS[size],
|
|
9203
|
-
RESIZE_CLASS,
|
|
9204
|
-
RADIUS_CLASS$3[radius],
|
|
9205
|
-
VARIANT_CLASS[variant],
|
|
9206
|
-
isInvalid ? INVALID_TEXT_CLASS : "",
|
|
9207
|
-
FIELD_FOCUS_CLASS,
|
|
9208
|
-
FIELD_SHADOW_CLASS,
|
|
9209
|
-
fullWidth ? "w-full" : "",
|
|
9210
|
-
classNames?.input ?? classNames?.inputWrapper,
|
|
9211
|
-
className,
|
|
9212
|
-
]
|
|
9213
|
-
.filter(Boolean)
|
|
9214
|
-
.join(" ") }), description && !isInvalid && (jsxRuntime.jsx(react.Description, { className: [HELPER_CLASS, "text-left", classNames?.description]
|
|
9215
|
-
.filter(Boolean)
|
|
9216
|
-
.join(" "), children: description })), isInvalid && errorMessage && (jsxRuntime.jsx(react.FieldError, { className: [HELPER_CLASS, "text-left", classNames?.errorMessage]
|
|
9217
|
-
.filter(Boolean)
|
|
9218
|
-
.join(" "), children: errorMessage }))] }));
|
|
9327
|
+
return (jsxRuntime.jsxs(react.TextField, { id: textareaId, name: name, isInvalid: isInvalid, isRequired: isRequired, isDisabled: isDisabled, isReadOnly: isReadOnly, className: react.cn(FIELD_STACK_CLASS, fullWidth ? "w-full" : "",
|
|
9328
|
+
// El `className` del consumidor, en la raíz y en último lugar: el slot `base` de
|
|
9329
|
+
// v2 y la posición que le deja ganar en tailwind-merge (ver bloque de arriba).
|
|
9330
|
+
className), children: [label && (jsxRuntime.jsx(react.Label, { className: react.cn(LABEL_CLASS, isInvalid ? INVALID_LABEL_CLASS : "", classNames?.label), children: label })), jsxRuntime.jsx(react.TextArea, { ...rest, ref: ref, placeholder: placeholder, value: value, defaultValue: defaultValue, onChange: handleChange, className: react.cn(SIZE_CLASS[size], RESIZE_CLASS, RADIUS_CLASS$3[radius], VARIANT_CLASS[variant], isInvalid ? INVALID_TEXT_CLASS : "", FIELD_FOCUS_CLASS, FIELD_SHADOW_CLASS,
|
|
9331
|
+
// Incondicional, como el slot `input` de v2: el campo llena siempre su raíz,
|
|
9332
|
+
// y es la raíz la que responde a `fullWidth` y al ancho del consumidor.
|
|
9333
|
+
"w-full", classNames?.input ?? classNames?.inputWrapper) }), description && !isInvalid && (jsxRuntime.jsx(react.Description, { className: react.cn(HELPER_CLASS, "text-left", classNames?.description), children: description })), isInvalid && errorMessage && (jsxRuntime.jsx(react.FieldError, { className: react.cn(HELPER_CLASS, "text-left", classNames?.errorMessage), children: errorMessage }))] }));
|
|
9219
9334
|
});
|
|
9220
9335
|
Textarea.displayName = "Textarea";
|
|
9221
9336
|
|