@beweco/aurora-ui 1.0.0-beta.136 → 1.0.0-beta.138

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs.js CHANGED
@@ -518,7 +518,7 @@ const SHAPE_BY_VARIANT = {
518
518
  // Por eso `SIZE_CLASS` vuelve al `px` del slot `base` de v2 y el sangrado del texto pasa a
519
519
  // `CONTENT_PADDING` (abajo), con `gap-0` para anular el `gap-0.5` que v3 mete en `.chip` y que v2
520
520
  // no tenía (en v2 TODA la separación salía de paddings).
521
- const SIZE_CLASS$7 = {
521
+ const SIZE_CLASS$6 = {
522
522
  sm: "h-6 px-1 gap-0 text-xs font-normal",
523
523
  md: "h-7 px-1 gap-0 text-sm font-normal",
524
524
  lg: "h-8 px-2 gap-0 text-base font-normal",
@@ -761,7 +761,7 @@ const Chip = ({ color = "default", variant = "solid", size = "md", radius = "ful
761
761
  // Con una cadena suelta ambas clases llegan al DOM y gana la que Tailwind emita más
762
762
  // tarde, no la del consumidor. Todas las clases de aquí son vocabulario v3 válido, así
763
763
  // que tailwind-merge las resuelve bien (la trampa de F1 era con clases v2 muertas).
764
- className: react.cn(SIZE_CLASS$7[size],
764
+ className: react.cn(SIZE_CLASS$6[size],
765
765
  // Después de `SIZE_CLASS` a propósito: el cuadrado de un carácter ENCOGE la caja
766
766
  // respecto al tamaño normal y tiene que ganar en tailwind-merge.
767
767
  isSquare ? ONE_CHAR_CLASS[size] : "", RADIUS_CLASS$9[radius], outlineClass, flatDarkFgClass, customClass, isDisabled ? "opacity-50 pointer-events-none" : "", classNames?.base, className), style: style, "aria-disabled": isDisabled || undefined, children: [startContent, jsxRuntime.jsx("span", { className: react.cn("flex-1 text-inherit font-normal", startContent
@@ -1188,12 +1188,12 @@ exports.HolidayType = void 0;
1188
1188
  // Mismo mecanismo que Input/Select (findings/campo-texto-responsive-sm-trap.md):
1189
1189
  // `.date-input-group__input` fija su propio `text-base sm:text-sm` — se repite la clase de
1190
1190
  // tamaño de TEXTO directo en `DateField.Input`, no solo en el `DateField.Group` contenedor.
1191
- const SIZE_CLASS$6 = {
1191
+ const SIZE_CLASS$5 = {
1192
1192
  sm: "min-h-8 h-8",
1193
1193
  md: "min-h-10 h-10",
1194
1194
  lg: "min-h-12 h-12",
1195
1195
  };
1196
- const SIZE_TEXT_CLASS$3 = {
1196
+ const SIZE_TEXT_CLASS$4 = {
1197
1197
  sm: "text-sm",
1198
1198
  md: "text-sm",
1199
1199
  lg: "text-base",
@@ -1266,23 +1266,23 @@ function DatePicker({ label, description, errorMessage, id, size = "md", variant
1266
1266
  return (jsxRuntime.jsxs(react.DatePicker, { ...props, id: autoId, "aria-label": ariaLabel, isInvalid: isInvalid, isRequired: isRequired, isDisabled: isDisabled, className: ["flex flex-col gap-1", fullWidth ? "w-full" : "", className]
1267
1267
  .filter(Boolean)
1268
1268
  .join(" "), children: [label && (jsxRuntime.jsx(react.Label, { className: "text-default-600 text-xs font-medium", children: label })), jsxRuntime.jsxs(react.DateField.Group, { fullWidth: fullWidth, className: [
1269
- SIZE_CLASS$6[size],
1269
+ SIZE_CLASS$5[size],
1270
1270
  RADIUS_CLASS$8[radius],
1271
1271
  VARIANT_CLASS$5[variant],
1272
1272
  FIELD_FOCUS_CLASS$5,
1273
1273
  ]
1274
1274
  .filter(Boolean)
1275
- .join(" "), children: [jsxRuntime.jsx(react.DateField.Input, { className: SIZE_TEXT_CLASS$3[size], children: (segment) => jsxRuntime.jsx(react.DateField.Segment, { segment: segment }) }), jsxRuntime.jsx(react.DateField.Suffix, { children: jsxRuntime.jsx(react.DatePicker.Trigger, { children: endContent !== undefined ? (jsxRuntime.jsx(react.DatePicker.TriggerIndicator, { children: endContent })) : (jsxRuntime.jsx(react.DatePicker.TriggerIndicator, {})) }) })] }), description && !isInvalid && jsxRuntime.jsx(react.Description, { children: description }), jsxRuntime.jsxs(react.DatePicker.Popover, { children: [jsxRuntime.jsxs(react.Calendar, { "aria-label": calendarLabel, children: [jsxRuntime.jsxs(react.Calendar.Header, { children: [jsxRuntime.jsxs(react.Calendar.YearPickerTrigger, { children: [jsxRuntime.jsx(react.Calendar.YearPickerTriggerHeading, {}), jsxRuntime.jsx(react.Calendar.YearPickerTriggerIndicator, {})] }), jsxRuntime.jsx(react.Calendar.NavButton, { slot: "previous" }), jsxRuntime.jsx(react.Calendar.NavButton, { slot: "next" })] }), jsxRuntime.jsxs(react.Calendar.Grid, { children: [jsxRuntime.jsx(react.Calendar.GridHeader, { children: (day) => jsxRuntime.jsx(react.Calendar.HeaderCell, { children: day }) }), jsxRuntime.jsx(react.Calendar.GridBody, { children: (date) => (jsxRuntime.jsx(react.Calendar.Cell, { date: date, className: CALENDAR_CELL_CLASS$1 })) })] })] }), calendarBottomContent] }), isInvalid && errorMessage && jsxRuntime.jsx(react.FieldError, { children: errorMessage })] }));
1275
+ .join(" "), children: [jsxRuntime.jsx(react.DateField.Input, { className: SIZE_TEXT_CLASS$4[size], children: (segment) => jsxRuntime.jsx(react.DateField.Segment, { segment: segment }) }), jsxRuntime.jsx(react.DateField.Suffix, { children: jsxRuntime.jsx(react.DatePicker.Trigger, { children: endContent !== undefined ? (jsxRuntime.jsx(react.DatePicker.TriggerIndicator, { children: endContent })) : (jsxRuntime.jsx(react.DatePicker.TriggerIndicator, {})) }) })] }), description && !isInvalid && jsxRuntime.jsx(react.Description, { children: description }), jsxRuntime.jsxs(react.DatePicker.Popover, { children: [jsxRuntime.jsxs(react.Calendar, { "aria-label": calendarLabel, children: [jsxRuntime.jsxs(react.Calendar.Header, { children: [jsxRuntime.jsxs(react.Calendar.YearPickerTrigger, { children: [jsxRuntime.jsx(react.Calendar.YearPickerTriggerHeading, {}), jsxRuntime.jsx(react.Calendar.YearPickerTriggerIndicator, {})] }), jsxRuntime.jsx(react.Calendar.NavButton, { slot: "previous" }), jsxRuntime.jsx(react.Calendar.NavButton, { slot: "next" })] }), jsxRuntime.jsxs(react.Calendar.Grid, { children: [jsxRuntime.jsx(react.Calendar.GridHeader, { children: (day) => jsxRuntime.jsx(react.Calendar.HeaderCell, { children: day }) }), jsxRuntime.jsx(react.Calendar.GridBody, { children: (date) => (jsxRuntime.jsx(react.Calendar.Cell, { date: date, className: CALENDAR_CELL_CLASS$1 })) })] })] }), calendarBottomContent] }), isInvalid && errorMessage && jsxRuntime.jsx(react.FieldError, { children: errorMessage })] }));
1276
1276
  }
1277
1277
 
1278
1278
  // Mismo mecanismo que DatePicker.tsx / Input.tsx / Select.tsx (findings/
1279
1279
  // campo-texto-responsive-sm-trap.md).
1280
- const SIZE_CLASS$5 = {
1280
+ const SIZE_CLASS$4 = {
1281
1281
  sm: "min-h-8 h-8",
1282
1282
  md: "min-h-10 h-10",
1283
1283
  lg: "min-h-12 h-12",
1284
1284
  };
1285
- const SIZE_TEXT_CLASS$2 = {
1285
+ const SIZE_TEXT_CLASS$3 = {
1286
1286
  sm: "text-sm",
1287
1287
  md: "text-sm",
1288
1288
  lg: "text-base",
@@ -1358,13 +1358,13 @@ function DateRangePicker({ label, description, errorMessage, id, size = "md", va
1358
1358
  return (jsxRuntime.jsxs(react.DateRangePicker, { ...props, id: autoId, "aria-label": ariaLabel, isInvalid: isInvalid, isRequired: isRequired, isDisabled: isDisabled, className: ["flex flex-col gap-1", fullWidth ? "w-full" : "", className]
1359
1359
  .filter(Boolean)
1360
1360
  .join(" "), children: [label && (jsxRuntime.jsx(react.Label, { className: "text-default-600 text-xs font-medium", children: label })), jsxRuntime.jsxs(react.DateField.Group, { fullWidth: fullWidth, className: [
1361
- SIZE_CLASS$5[size],
1361
+ SIZE_CLASS$4[size],
1362
1362
  RADIUS_CLASS$7[radius],
1363
1363
  VARIANT_CLASS$4[variant],
1364
1364
  FIELD_FOCUS_CLASS$4,
1365
1365
  ]
1366
1366
  .filter(Boolean)
1367
- .join(" "), children: [jsxRuntime.jsx(react.DateField.Input, { slot: "start", className: SIZE_TEXT_CLASS$2[size], children: (segment) => jsxRuntime.jsx(react.DateField.Segment, { segment: segment }) }), jsxRuntime.jsx(react.DateRangePicker.RangeSeparator, {}), jsxRuntime.jsx(react.DateField.Input, { slot: "end", className: SIZE_TEXT_CLASS$2[size], children: (segment) => jsxRuntime.jsx(react.DateField.Segment, { segment: segment }) }), jsxRuntime.jsx(react.DateField.Suffix, { children: jsxRuntime.jsx(react.DateRangePicker.Trigger, { children: endContent !== undefined ? (jsxRuntime.jsx(react.DateRangePicker.TriggerIndicator, { children: endContent })) : (jsxRuntime.jsx(react.DateRangePicker.TriggerIndicator, {})) }) })] }), description && !isInvalid && jsxRuntime.jsx(react.Description, { children: description }), jsxRuntime.jsxs(react.DateRangePicker.Popover, { children: [jsxRuntime.jsxs(react.RangeCalendar, { "aria-label": calendarLabel, children: [jsxRuntime.jsxs(react.RangeCalendar.Header, { children: [jsxRuntime.jsxs(react.RangeCalendar.YearPickerTrigger, { children: [jsxRuntime.jsx(react.RangeCalendar.YearPickerTriggerHeading, {}), jsxRuntime.jsx(react.RangeCalendar.YearPickerTriggerIndicator, {})] }), jsxRuntime.jsx(react.RangeCalendar.NavButton, { slot: "previous" }), jsxRuntime.jsx(react.RangeCalendar.NavButton, { slot: "next" })] }), jsxRuntime.jsxs(react.RangeCalendar.Grid, { children: [jsxRuntime.jsx(react.RangeCalendar.GridHeader, { children: (day) => jsxRuntime.jsx(react.RangeCalendar.HeaderCell, { children: day }) }), jsxRuntime.jsx(react.RangeCalendar.GridBody, { children: (date) => (jsxRuntime.jsx(react.RangeCalendar.Cell, { date: date, className: CALENDAR_CELL_CLASS })) })] })] }), calendarBottomContent] }), isInvalid && errorMessage && jsxRuntime.jsx(react.FieldError, { children: errorMessage })] }));
1367
+ .join(" "), children: [jsxRuntime.jsx(react.DateField.Input, { slot: "start", className: SIZE_TEXT_CLASS$3[size], children: (segment) => jsxRuntime.jsx(react.DateField.Segment, { segment: segment }) }), jsxRuntime.jsx(react.DateRangePicker.RangeSeparator, {}), jsxRuntime.jsx(react.DateField.Input, { slot: "end", className: SIZE_TEXT_CLASS$3[size], children: (segment) => jsxRuntime.jsx(react.DateField.Segment, { segment: segment }) }), jsxRuntime.jsx(react.DateField.Suffix, { children: jsxRuntime.jsx(react.DateRangePicker.Trigger, { children: endContent !== undefined ? (jsxRuntime.jsx(react.DateRangePicker.TriggerIndicator, { children: endContent })) : (jsxRuntime.jsx(react.DateRangePicker.TriggerIndicator, {})) }) })] }), description && !isInvalid && jsxRuntime.jsx(react.Description, { children: description }), jsxRuntime.jsxs(react.DateRangePicker.Popover, { children: [jsxRuntime.jsxs(react.RangeCalendar, { "aria-label": calendarLabel, children: [jsxRuntime.jsxs(react.RangeCalendar.Header, { children: [jsxRuntime.jsxs(react.RangeCalendar.YearPickerTrigger, { children: [jsxRuntime.jsx(react.RangeCalendar.YearPickerTriggerHeading, {}), jsxRuntime.jsx(react.RangeCalendar.YearPickerTriggerIndicator, {})] }), jsxRuntime.jsx(react.RangeCalendar.NavButton, { slot: "previous" }), jsxRuntime.jsx(react.RangeCalendar.NavButton, { slot: "next" })] }), jsxRuntime.jsxs(react.RangeCalendar.Grid, { children: [jsxRuntime.jsx(react.RangeCalendar.GridHeader, { children: (day) => jsxRuntime.jsx(react.RangeCalendar.HeaderCell, { children: day }) }), jsxRuntime.jsx(react.RangeCalendar.GridBody, { children: (date) => (jsxRuntime.jsx(react.RangeCalendar.Cell, { date: date, className: CALENDAR_CELL_CLASS })) })] })] }), calendarBottomContent] }), isInvalid && errorMessage && jsxRuntime.jsx(react.FieldError, { children: errorMessage })] }));
1368
1368
  }
1369
1369
 
1370
1370
  // TECH-2459 · F3.0 — v3 divide el `Input` monolítico de v2 en `TextField` (label/description/
@@ -1379,7 +1379,7 @@ function DateRangePicker({ label, description, errorMessage, id, size = "md", va
1379
1379
  // `md: h-10 min-h-10` · `lg: h-12 min-h-12` (los dos últimos heredan el `px-3` de la base).
1380
1380
  // Verificado además contra los PNG de v2: `input--small` 32px de alto y 10px del borde exterior al
1381
1381
  // texto (2 de borde + 8 de padding) · `--medium` 40px y 14px (2 + 12) · `--large` 48px y 14px.
1382
- const SIZE_CLASS$4 = {
1382
+ const SIZE_CLASS$3 = {
1383
1383
  sm: "min-h-8 h-8",
1384
1384
  md: "min-h-10 h-10",
1385
1385
  lg: "min-h-12 h-12",
@@ -1416,7 +1416,7 @@ const PADDING_FULL_CLASS$1 = {
1416
1416
  // medía 40px de alto en vez de 32, porque 20px de línea + 16 de padding no caben en `h-8`).
1417
1417
  // El hueco entre el contenido de los extremos y el texto lo pone `ps-1.5`/`pe-1.5`, que es
1418
1418
  // exactamente lo que hacía v2 (`data-[has-start-content=true]:ps-1.5`).
1419
- const SIZE_TEXT_CLASS$1 = {
1419
+ const SIZE_TEXT_CLASS$2 = {
1420
1420
  sm: "text-sm",
1421
1421
  md: "text-sm",
1422
1422
  lg: "text-base",
@@ -1734,11 +1734,11 @@ fullWidth = true, value, defaultValue, onChange, onValueChange, className, class
1734
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
1735
  // El `className` del consumidor, en la raíz y en último lugar: el slot `base` de
1736
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,
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$3[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
1738
  // Incondicional, como el `inputWrapper` de v2: la caja del campo llena
1739
1739
  // siempre su raíz, y es la raíz la que responde a `fullWidth` y al ancho que
1740
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 }))] }));
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$2[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 }))] }));
1742
1742
  });
1743
1743
  Input.displayName = "Input";
1744
1744
 
@@ -2285,7 +2285,7 @@ const OPCIONES_CLASS = [
2285
2285
  * componente el label era propio de Aurora y llevaba un `mb-2` fijo (8px) para los tres. Por eso
2286
2286
  * `FIELD_STACK_CLASS` es una constante y no un registro.
2287
2287
  */
2288
- const SIZE_CLASS$3 = {
2288
+ const SIZE_CLASS$2 = {
2289
2289
  sm: "min-h-8 h-8",
2290
2290
  md: "min-h-10 h-10",
2291
2291
  lg: "min-h-12 h-12",
@@ -2458,7 +2458,7 @@ isClearable = true, isReadOnly, inputValue, selectedKey, onInputChange, onSelect
2458
2458
  inputRef.current?.focus();
2459
2459
  };
2460
2460
  return (jsxRuntime.jsxs(react.ComboBox, { id: autoId, isDisabled: isDisabled, isReadOnly: isReadOnly, inputValue: inputValue, selectedKey: selectedKey, onInputChange: onInputChange, onSelectionChange: onSelectionChange, className: [FIELD_STACK_CLASS$2, className].filter(Boolean).join(" "), ...props, children: [label && jsxRuntime.jsx(react.Label, { className: LABEL_CLASS$3, children: label }), jsxRuntime.jsxs(react.ComboBox.InputGroup, { className: [
2461
- SIZE_CLASS$3[size],
2461
+ SIZE_CLASS$2[size],
2462
2462
  PADDING_CLASS$1[size],
2463
2463
  variant === "underlined" ? UNDERLINED_INNER_CLASS[size] : "",
2464
2464
  FIELD_CLASS$1,
@@ -5301,7 +5301,7 @@ KanbanCard.displayName = "KanbanCard";
5301
5301
  * `.button`/`.card`/`.chip`) y además arreglaría el Badge crudo de HeroUI que documenta la story
5302
5302
  * `HeroUI Components/Badge`; queda reportado para ese lane.
5303
5303
  */
5304
- const SIZE_CLASS$2 = {
5304
+ const SIZE_CLASS$1 = {
5305
5305
  sm: "min-h-4 min-w-4", // v2: w-4 h-4 = 16px
5306
5306
  md: "min-h-5 min-w-5", // v2: w-5 h-5 = 20px
5307
5307
  lg: "min-h-6 min-w-6", // v2: w-6 h-6 = 24px
@@ -5359,7 +5359,7 @@ const DEFAULT_COLOR_STYLE = {
5359
5359
  };
5360
5360
  const BadgeRoot = ({ size = "md", color = "default", variant = "primary", className, style, content, isInvisible, children, ...props }) => {
5361
5361
  const needsDefaultToken = color === "default" && variant === "primary";
5362
- const badge = (jsxRuntime.jsx(react.Badge, { ...props, color: color, variant: variant, size: size, className: ["border-2", SIZE_CLASS$2[size], className]
5362
+ const badge = (jsxRuntime.jsx(react.Badge, { ...props, color: color, variant: variant, size: size, className: ["border-2", SIZE_CLASS$1[size], className]
5363
5363
  .filter(Boolean)
5364
5364
  .join(" "), style: needsDefaultToken ? { ...DEFAULT_COLOR_STYLE, ...style } : style, children: isV2Api(content, isInvisible) ? content : children }));
5365
5365
  // API v3: los `children` SON el contenido del badge y el anclaje lo compone el consumidor con
@@ -8588,9 +8588,9 @@ const SearchInput = ({ searchPlaceholder = "Buscar...", searchIcon = "solar:magn
8588
8588
  };
8589
8589
 
8590
8590
  /** El indicador ya montado a mano por el consumidor: no se duplica. */
8591
- function hasIndicator(children) {
8591
+ function hasIndicator$1(children) {
8592
8592
  if (Array.isArray(children)) {
8593
- return children.some(hasIndicator);
8593
+ return children.some(hasIndicator$1);
8594
8594
  }
8595
8595
  return $8WNJc$react.isValidElement(children) && children.type === react.ListBoxItem.Indicator;
8596
8596
  }
@@ -8626,7 +8626,7 @@ function withItemIndicator(children) {
8626
8626
  const next = (values) => (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [render(values), jsxRuntime.jsx(react.ListBoxItem.Indicator, {})] }));
8627
8627
  return $8WNJc$react.cloneElement(element, { children: next });
8628
8628
  }
8629
- if (hasIndicator(original)) {
8629
+ if (hasIndicator$1(original)) {
8630
8630
  return element;
8631
8631
  }
8632
8632
  const next = (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [original, jsxRuntime.jsx(react.ListBoxItem.Indicator, {})] }));
@@ -8640,7 +8640,7 @@ function withItemIndicator(children) {
8640
8640
  // min-h-10` · `lg: h-12 min-h-12` — las mismas medidas que el Input, como era de esperar.
8641
8641
  // Verificado contra el PNG de v2: `select--medium` mide 40px de alto y 14px del borde exterior al
8642
8642
  // texto (2 de borde + 12 de padding).
8643
- const SIZE_CLASS$1 = {
8643
+ const SIZE_CLASS = {
8644
8644
  sm: "min-h-8 h-8 text-sm",
8645
8645
  md: "min-h-10 h-10 text-sm",
8646
8646
  lg: "min-h-12 h-12 text-base",
@@ -8668,7 +8668,7 @@ const TRIGGER_ALIGN_CLASS = "items-center py-0";
8668
8668
  // `.input-group__input` (ver Input.tsx) — el tamaño de texto del trigger no llega al texto
8669
8669
  // seleccionado por herencia. Se repite aquí, directo sobre `Select.Value` (comprobado con
8670
8670
  // `className` computado en el DOM).
8671
- const SIZE_TEXT_CLASS = {
8671
+ const SIZE_TEXT_CLASS$1 = {
8672
8672
  sm: "text-sm",
8673
8673
  md: "text-sm",
8674
8674
  lg: "text-base",
@@ -9057,7 +9057,7 @@ fullWidth = true, isOpen, defaultOpen, onOpenChange, className, classNames, tran
9057
9057
  // Mismo criterio que el `group-data-[has-helper=true]` de v2: cuenta como "con ayuda" el campo
9058
9058
  // que efectivamente PINTA una descripción o un mensaje de error, no el que solo recibe la prop.
9059
9059
  const hasHelper = Boolean((description && !isInvalid) || (isInvalid && errorMessage));
9060
- 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],
9060
+ const triggerClassName = react.cn(SIZE_CLASS[size], radius === "full" ? PADDING_FULL_CLASS[size] : PADDING_CLASS[size], TRIGGER_ALIGN_CLASS, RADIUS_CLASS$4[radius], VARIANT_CLASS$1[variant],
9061
9061
  // En v2 el estado inválido MANDABA también con el campo enfocado
9062
9062
  // (`group-data-[focus=true]:!border-danger`), así que el borde de foco no se aplica cuando
9063
9063
  // el campo está en error: se queda el `danger`.
@@ -9070,7 +9070,7 @@ fullWidth = true, isOpen, defaultOpen, onOpenChange, className, classNames, tran
9070
9070
  "w-full", classNames?.trigger);
9071
9071
  const inner = (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [label && (jsxRuntime.jsx(react.Label, { className: react.cn(LABEL_CLASS$1, hasHelper
9072
9072
  ? LABEL_OFFSET_HELPER_CLASS[size]
9073
- : 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
9073
+ : 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$1[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
9074
9074
  // El hueco que v2 llenaba con su `emptyContent` (ver el bloque de
9075
9075
  // `DEFAULT_TRANSLATIONS`). `renderEmptyState` es la vía de v3: el `ListBox` de
9076
9076
  // react-aria solo pinta algo con la colección vacía si se le pasa, y envuelve
@@ -9276,17 +9276,17 @@ const OWN_SCROLL = new Set([
9276
9276
  react.Table.ResizableContainer,
9277
9277
  ]);
9278
9278
  /** Identidad por `child.type` contra los objetos reales de HeroUI (mismo patrón que la fachada `Alert`). */
9279
- function isPart$1(node, parts) {
9279
+ function isPart$2(node, parts) {
9280
9280
  return $8WNJc$react.isValidElement(node) && parts.has(node.type);
9281
9281
  }
9282
9282
  const TableFacade = ({ children, ...props }) => {
9283
9283
  const nodes = $8WNJc$react.Children.toArray(children);
9284
9284
  // Sin hijos, o con un contenedor de scroll ya montado a mano: el root tal cual.
9285
- if (nodes.length === 0 || nodes.some((node) => isPart$1(node, OWN_SCROLL))) {
9285
+ if (nodes.length === 0 || nodes.some((node) => isPart$2(node, OWN_SCROLL))) {
9286
9286
  return jsxRuntime.jsx(react.Table, { ...props, children: children });
9287
9287
  }
9288
- const scrollable = nodes.filter((node) => !isPart$1(node, OUTSIDE_SCROLL));
9289
- const outsideScroll = nodes.filter((node) => isPart$1(node, OUTSIDE_SCROLL));
9288
+ const scrollable = nodes.filter((node) => !isPart$2(node, OUTSIDE_SCROLL));
9289
+ const outsideScroll = nodes.filter((node) => isPart$2(node, OUTSIDE_SCROLL));
9290
9290
  return (jsxRuntime.jsxs(react.Table, { ...props, children: [jsxRuntime.jsx(react.Table.ScrollContainer, { children: scrollable }), outsideScroll] }));
9291
9291
  };
9292
9292
  TableFacade.displayName = "Aurora.Table";
@@ -9378,18 +9378,70 @@ function AuraTable({ columns, items, renderCell, children, className, ...props }
9378
9378
  // borde/fondo/tamaño de texto viven todos en el MISMO elemento — ver `textarea.css`). Por eso no
9379
9379
  // hace falta repetir la clase de tamaño de texto en un hijo aparte (el hallazgo
9380
9380
  // `campo-texto-responsive-sm-trap.md` no aplica: no hay wrapper distinto del elemento con texto).
9381
+ // El tamaño de TEXTO depende de `size`; el ALTO depende de `minRows` — por eso van en dos
9382
+ // constantes separadas y no en una sola clase con el alto a fuego (ver el bloque siguiente).
9383
+ const SIZE_TEXT_CLASS = {
9384
+ sm: "text-sm",
9385
+ md: "text-sm",
9386
+ lg: "text-base",
9387
+ };
9381
9388
  // Alto mínimo con la fórmula de v2, no con un paso redondo de Tailwind: v2 no fijaba altura al
9382
9389
  // multilínea (`isMultiline` → `inputWrapper: "!h-auto"`), la altura salía de `minRows` (3 por
9383
9390
  // defecto) por el alto de línea, más el `py-2` que el compound `outside + isMultiline` añadía y los
9384
9391
  // 2px de borde de cada lado:
9385
- // sm/md (texto 14px, línea 20) → 3·20 + 16 + 4 = 80px
9386
- // lg (texto 16px, línea 24) → 3·24 + 16 + 4 = 92px
9392
+ // sm/md (texto 14px, línea 20) → minRows·20 + 16 + 4 → 3 líneas = 80px
9393
+ // lg (texto 16px, línea 24) → minRows·24 + 16 + 4 → 3 líneas = 92px
9387
9394
  // Comprobado contra el PNG de v2: `textarea--default` (que pasa `size="lg"`) mide exactamente 92px.
9388
- const SIZE_CLASS = {
9389
- sm: "min-h-20 text-sm",
9390
- md: "min-h-20 text-sm",
9391
- lg: "min-h-[92px] text-base",
9392
- };
9395
+ //
9396
+ // Hasta TECH-2459 esto eran dos clases fijas (`min-h-20` / `min-h-[92px]`) que ya llevaban el
9397
+ // `minRows = 3` metido dentro, así que el campo medía 80/92px pidiera el consumidor las líneas que
9398
+ // pidiera: en `beweossmbs` (la única app sin migrar) hay 27 usos de `minRows` reclamando SEIS
9399
+ // alturas distintas —1, 2, 3, 4, 6 y 8 líneas— y solo los 8 de "3 líneas" salían bien. Con la
9400
+ // fórmula, `minRows = 3` sigue dando 80/92px exactos: ninguna app que no pase la prop se mueve.
9401
+ const SIZE_LINE_HEIGHT = {
9402
+ sm: 20,
9403
+ md: 20,
9404
+ lg: 24,
9405
+ };
9406
+ // El `py-2` que `.textarea` de v3 se aplica a sí mismo: 8px arriba + 8 abajo (verificado en
9407
+ // `node_modules/@heroui/styles/dist/components/textarea.css`, no deducido del PNG).
9408
+ const FIELD_PADDING_Y = 16;
9409
+ // Los 2px de borde por lado del `bordered`/`faded` de v2 (`box-sizing: border-box`).
9410
+ const FIELD_BORDER_Y = 4;
9411
+ // El default de v2, literal: `({ minRows = 3, maxRows = 8, … })` en `@heroui/input@2.4.22`,
9412
+ // `dist/chunk-RIE4DNUQ.mjs`.
9413
+ const DEFAULT_MIN_ROWS = 3;
9414
+ // ── `minRows` gana al atributo nativo `rows` ─────────────────────────────────────────────────
9415
+ //
9416
+ // Las dos props dicen lo mismo —cuántas líneas de alto tiene la caja al aparecer— así que la
9417
+ // fachada las unifica en un solo número (`minRows ?? rows ?? 3`) y las trata igual. Cuando llegan
9418
+ // LAS DOS manda `minRows`, y eso no es un gusto: es lo que hacía v2, medido en el paquete v2
9419
+ // instalado (`beweossmbs/node_modules`), por dos caminos independientes.
9420
+ //
9421
+ // 1. v2 desestructuraba `minRows` y se lo pasaba a `TextareaAutosize`, que fija la altura con
9422
+ // `node.style.setProperty("height", height + "px", "important")` — un inline con
9423
+ // `!important` que ningún atributo `rows` puede discutir (`react-textarea-autosize@8.5.9`,
9424
+ // `calculateNodeHeight` + `resizeTextarea`).
9425
+ // 2. Y el `rows` del consumidor no llegaba ni al DOM: `useInput` filtraba las props ajenas con
9426
+ // `filterDOMProps(otherProps, { enabled: true, labelable: true })`, cuya lista blanca es
9427
+ // `id` + los cuatro `aria-*` etiquetables + `data-*` (`@react-aria/utils`,
9428
+ // `dist/filterDOMProps.mjs`), y `useTextField` tampoco lo emite. En v2 `rows` era INERTE.
9429
+ //
9430
+ // En v3 sí llega (viaja en `...rest` hasta el `<textarea>`), de modo que aquí se desestructura para
9431
+ // que no pelee con el alto mínimo: con `minRows={2} rows={8}`, el atributo daría 8 líneas de alto
9432
+ // intrínseco y ganaría a un `min-height` de 2. Se emite UN solo `rows`, el de la cuenta ya
9433
+ // resuelta, así el atributo nativo y el `min-height` nunca se contradicen (y el campo sale con su
9434
+ // alto incluso antes de que cargue la hoja de estilos: el intrínseco de N filas es
9435
+ // N·línea + 16 + 4 con borde, y menos sin él, así que jamás supera al `min-height`).
9436
+ //
9437
+ // `rows` se sigue aceptando —y con el mismo significado— porque hay 9 usos reales en las apps (3 en
9438
+ // `beweossmbs`, 1 en `beweosagency`, 5 en `backoffice-beweos`, verificados con el import de
9439
+ // `@beweco/aurora-ui` delante) y ninguno pasa las dos props a la vez.
9440
+ const resolveRows = (minRows, rows) =>
9441
+ // `Math.max(1, …)` y el redondeo son por el atributo: `rows` exige un entero ≥ 1 y el navegador
9442
+ // ignora cualquier otra cosa. v2 acababa igual de rebote (su altura era el máximo entre el
9443
+ // mínimo calculado y una línea de contenido).
9444
+ Math.max(1, Math.round(minRows ?? rows ?? DEFAULT_MIN_ROWS));
9393
9445
  // v2 NO dejaba redimensionar el multilínea: su `tv` ponía `input: "resize-none …"` en la variante
9394
9446
  // `isMultiline` (literal de `dist/components/input.js`). v3 deja el `<textarea>` con el
9395
9447
  // comportamiento por defecto del navegador, que además de ser arrastrable PINTA el tirador en la
@@ -9511,7 +9563,10 @@ const INVALID_TEXT_CLASS = "text-danger";
9511
9563
  * y acababa colgando el ref del contenedor y buscando el `<textarea>` con una query, y (b) era
9512
9564
  * accidental: cualquier prop que se añadiese al destructuring lo habría cortado en silencio.
9513
9565
  */
9514
- const Textarea = $8WNJc$react.forwardRef(({ label, description, errorMessage, id, size = "md", variant = "bordered", radius = "md", placeholder, isInvalid = false, isRequired = false, isDisabled = false, isReadOnly = false,
9566
+ const Textarea = $8WNJc$react.forwardRef(({ label, description, errorMessage, id, size = "md", variant = "bordered", radius = "md",
9567
+ // Sin default en el destructuring a propósito: hace falta distinguir "no me la han pasado"
9568
+ // de un valor, para que `rows` solo se mire cuando `minRows` no viene (ver `resolveRows`).
9569
+ minRows, rows, placeholder, isInvalid = false, isRequired = false, isDisabled = false, isReadOnly = false,
9515
9570
  // `fullWidth` por defecto a `true`: HeroUI v2 lo traía en sus `defaultVariants`
9516
9571
  // (verificado en el paquete v2 compilado, `dist/components/input.js`:
9517
9572
  // `defaultVariants: { variant:"flat", color:"default", size:"md", fullWidth:true, … }`, con
@@ -9519,9 +9574,18 @@ const Textarea = $8WNJc$react.forwardRef(({ label, description, errorMessage, id
9519
9574
  // `undefined` y el campo dejaba de llenar su contenedor: en una fila flex se encogía al ancho
9520
9575
  // de su contenido (medido por S2: 208px dentro de una fila de 592, 336px de hueco). En una
9521
9576
  // columna no se notaba porque el `stretch` del flex lo tapaba — pero afecta a cualquier app.
9522
- fullWidth = true, value, defaultValue, onChange, onValueChange, className, classNames, name, ...rest }, ref) => {
9577
+ fullWidth = true, value, defaultValue, onChange, onValueChange, className, classNames, name, style, ...rest }, ref) => {
9523
9578
  const generatedId = $8WNJc$react.useId();
9524
9579
  const textareaId = id || generatedId;
9580
+ const fieldRows = resolveRows(minRows, rows);
9581
+ // Inline y no una clase: el valor es un número arbitrario y Tailwind 4 solo emite las clases
9582
+ // que encuentra escritas en el fuente, así que `min-h-[${n}px]` no existiría en el CSS. El
9583
+ // `style` del consumidor va DESPUÉS para que pueda pisarlo (y una clase con `!` también gana,
9584
+ // que es el canal documentado en el README: `classNames={{ input: "min-h-40!" }}`).
9585
+ const fieldStyle = {
9586
+ minHeight: `${fieldRows * SIZE_LINE_HEIGHT[size] + FIELD_PADDING_Y + FIELD_BORDER_Y}px`,
9587
+ ...style,
9588
+ };
9525
9589
  const handleChange = (event) => {
9526
9590
  onChange?.(event);
9527
9591
  onValueChange?.(event.target.value);
@@ -9529,7 +9593,7 @@ fullWidth = true, value, defaultValue, onChange, onValueChange, className, class
9529
9593
  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" : "",
9530
9594
  // El `className` del consumidor, en la raíz y en último lugar: el slot `base` de
9531
9595
  // v2 y la posición que le deja ganar en tailwind-merge (ver bloque de arriba).
9532
- 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,
9596
+ 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, rows: fieldRows, style: fieldStyle, placeholder: placeholder, value: value, defaultValue: defaultValue, onChange: handleChange, className: react.cn(SIZE_TEXT_CLASS[size], RESIZE_CLASS, RADIUS_CLASS$3[radius], VARIANT_CLASS[variant], isInvalid ? INVALID_TEXT_CLASS : "", FIELD_FOCUS_CLASS, FIELD_SHADOW_CLASS,
9533
9597
  // Incondicional, como el slot `input` de v2: el campo llena siempre su raíz,
9534
9598
  // y es la raíz la que responde a `fullWidth` y al ancho del consumidor.
9535
9599
  "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 }))] }));
@@ -14675,18 +14739,18 @@ const OWN_POPOVER = new Set([
14675
14739
  react.Dropdown.SubmenuTrigger,
14676
14740
  ]);
14677
14741
  /** Identidad por `child.type` contra los objetos reales de HeroUI (patrón de la fachada `Table`). */
14678
- function isPart(node, parts) {
14742
+ function isPart$1(node, parts) {
14679
14743
  return $8WNJc$react.isValidElement(node) && parts.has(node.type);
14680
14744
  }
14681
14745
  const DropdownFacade = ({ children, ...props }) => {
14682
14746
  const nodes = $8WNJc$react.Children.toArray(children);
14683
- const menus = nodes.filter((node) => isPart(node, INSIDE_POPOVER));
14747
+ const menus = nodes.filter((node) => isPart$1(node, INSIDE_POPOVER));
14684
14748
  // Nada que envolver (sin hijos, `children` como función, un popover ya montado a mano, o
14685
14749
  // simplemente ningún menú suelto): el root tal cual, byte a byte como el crudo de v3.
14686
- if (menus.length === 0 || nodes.some((node) => isPart(node, OWN_POPOVER))) {
14750
+ if (menus.length === 0 || nodes.some((node) => isPart$1(node, OWN_POPOVER))) {
14687
14751
  return jsxRuntime.jsx(react.Dropdown, { ...props, children: children });
14688
14752
  }
14689
- return (jsxRuntime.jsxs(react.Dropdown, { ...props, children: [nodes.filter((node) => !isPart(node, INSIDE_POPOVER)), jsxRuntime.jsx(react.Dropdown.Popover, { offset: 7, placement: "bottom", children: menus })] }));
14753
+ return (jsxRuntime.jsxs(react.Dropdown, { ...props, children: [nodes.filter((node) => !isPart$1(node, INSIDE_POPOVER)), jsxRuntime.jsx(react.Dropdown.Popover, { offset: 7, placement: "bottom", children: menus })] }));
14690
14754
  };
14691
14755
  DropdownFacade.displayName = "Aurora.Dropdown";
14692
14756
  /**
@@ -14717,6 +14781,229 @@ const Dropdown = Object.assign(DropdownFacade, {
14717
14781
  Trigger: react.Dropdown.Trigger,
14718
14782
  });
14719
14783
 
14784
+ // TECH-2459 — Fachada `Tabs` / `Tab`: monta las DOS piezas de la anatomía de v3 que, si la app
14785
+ // se las olvida, dejan la pestaña seleccionada sin distinguir. Hasta ahora `Tabs` y `Tab` eran
14786
+ // reexport crudo de v3 (`src/index.tsx`, bloque `from "@heroui/react"`) y no existía
14787
+ // `src/components/tabs/`: la anatomía entera quedaba a cargo de cada app.
14788
+ //
14789
+ // EL DEFECTO, medido por grep real en los repos de apps (no de memoria):
14790
+ //
14791
+ // | app | `<Tabs>` | `<Tabs.List>` | `<Tabs.ListContainer>` | tabs | `<Tabs.Indicator>` | resultado |
14792
+ // |-------------------|----------|---------------|------------------------|------|--------------------|-----------|
14793
+ // | beweosagency | 5 | 5 | 5 | 14 | **0** | la seleccionada sin píldora |
14794
+ // | backoffice-beweos | 5 | 5 | 5 | 18 | 18 (a mano) | bien |
14795
+ // | beweanalytics | 3 | 3 | 3 | 8 | 8 (a mano) | bien |
14796
+ // | beweossmbs / loginos / linda-business | 0 | 0 | 0 | 0 | 0 | no usan tabs |
14797
+ //
14798
+ // (conteo de tags de APERTURA; beweanalytics escribe `Tab` suelto en vez de `Tabs.Tab`.)
14799
+ // Dos lecturas que salen de esta tabla y que deciden el diseño:
14800
+ // · **26 indicadores montados a mano** contra 14 que faltan. La idempotencia no es un adorno:
14801
+ // sin ella esas 26 pestañas se quedarían con DOS indicadores.
14802
+ // · **`Tabs.List` y `Tabs.ListContainer` van 1:1 en las tres apps**, o sea que hoy NADIE usa la
14803
+ // forma delgada. Garantizar el carril es, para el código que existe hoy, un no-op medido; su
14804
+ // valor es que la próxima pantalla que se escriba no pueda caer en la forma delgada por
14805
+ // descuido.
14806
+ //
14807
+ // Las tres migraciones siguieron la misma guía; dos descubrieron que hacía falta el indicador y
14808
+ // una no, porque **nada en la API obliga a montarlo ni avisa**: compila en verde y se degrada en
14809
+ // silencio. Es el mismo patrón —y el mismo argumento— que ya justificó las fachadas `Table`
14810
+ // (`Table.ScrollContainer` ausente → columnas inalcanzables) y `Dropdown`
14811
+ // (`Dropdown.Popover` ausente → menú en línea).
14812
+ //
14813
+ // El criterio del usuario que lo convierte en trabajo de la librería y no de las apps:
14814
+ // «se supone que solo es llamar al componente dentro de la librería y ya se visualiza correcto;
14815
+ // asegúrate de que las apps no estén metiéndole fixes por separado».
14816
+ //
14817
+ // LAS DOS PIEZAS ESTÁN ACOPLADAS, y por eso la fachada monta las dos o ninguna:
14818
+ //
14819
+ // · `Tabs.Indicator` es quien pinta la píldora del seleccionado. Su CSS es
14820
+ // `.tabs__indicator { absolute start-0 top-0 size-full rounded-3xl bg-segment shadow-surface;
14821
+ // z-index:-1 }` (`@heroui/styles/dist/components/tabs.css:215-228`), y `--segment` es
14822
+ // `var(--white)` en claro y `oklch(39.64% .01 285.93)` (#46464c) en oscuro
14823
+ // (`@heroui/styles/dist/heroui.min.css`).
14824
+ // · `Tabs.ListContainer` es quien pinta el CARRIL sobre el que se ve esa píldora
14825
+ // (`.tabs__list-container { relative bg-default }`, `tabs.css:16-17`, que
14826
+ // `neutralize-v3.css` baja a `--color-default-100` = #f4f4f5 por contraste).
14827
+ // **Una píldora blanca sobre fondo blanco no se ve**, y el número: medido en Storybook, en
14828
+ // claro la píldora queda a **1.00:1** contra la página en la forma delgada y a **1.10:1**
14829
+ // contra el carril en la completa — sin carril lo único que sobrevive de ella es su sombra,
14830
+ // que en oscuro es transparente del todo. Garantizar solo el indicador cambiaría un defecto
14831
+ // por otro. La app que migró en la forma delgada ya lo documentó:
14832
+ // `beweanalytics/src/clientes/ui/pages/clientes.page.tsx:59-67`.
14833
+ //
14834
+ // Y `Tabs.Indicator` va DENTRO de cada `Tab`, nunca en la lista: `TabList` envuelve en
14835
+ // `SharedElementTransition` solo el `CollectionRoot`
14836
+ // (`react-aria-components/dist/private/Tabs.mjs:152`), así que un indicador suelto en la lista
14837
+ // lanza «<SharedElement> must be rendered inside a <SharedElementTransition>» y **la barra entera
14838
+ // desaparece del DOM**. De ahí que la inserción del indicador viva en la fachada del `Tab` y no
14839
+ // en la del root.
14840
+ //
14841
+ // LO QUE ESTA FACHADA NO HACE, y conste para que no se busque: no repone NADA del aspecto de v2
14842
+ // (radios, gap, padding, peso, la pastilla de v2). Eso se hizo en la `beta.132` y se retiró en la
14843
+ // `.133` por decisión del usuario (`.docs/migrate-heroui/DECISIONES.md` §9.1, «el objetivo NO es
14844
+ // identidad de píxel» y, sobre este componente, «a nivel visual debe verse como v3»); el mapa de
14845
+ // las 6 reglas retiradas sigue escrito al final de `src/styles/neutralize-v3.css`. La pieza que
14846
+ // aquí se monta es la de v3 —su propio `SelectionIndicator`, que además anima—, no una imitación
14847
+ // de v2 por CSS: es exactamente lo que la nota de esa retirada señala como la vía buena («la
14848
+ // pastilla de verdad, la que además anima, se tiene montando `Tabs.Indicator`, que es lo que v3
14849
+ // espera»). Lo que cambia es QUIÉN la monta: la librería en vez de cada app.
14850
+ /**
14851
+ * Identidad por `child.type` contra los objetos reales de HeroUI — mismo mecanismo que las
14852
+ * fachadas `Table` y `Dropdown`, y nunca por `displayName` ni por nombre de función (la
14853
+ * minificación los renombra y las piezas de v3 no declaran `displayName`).
14854
+ *
14855
+ * Es FIABLE para detectar lo que la app ya puso porque las apps tienen prohibido importar de
14856
+ * `@heroui/*` (regla de oro de la migración): su única vía para un `Tabs.List`/`Tabs.ListContainer`
14857
+ * es el estático de Aurora, que es **este mismo objeto** (los estáticos de abajo apuntan a los de
14858
+ * HeroUI, no a copias). El mismo argumento vale para el indicador, que tiene su propia función
14859
+ * (`hasIndicator`, más abajo) porque ahí además hay que descender por los Fragments.
14860
+ *
14861
+ * Se declara como predicado de tipo —y no `boolean` como en `Dropdown`— porque el mapeo en sitio
14862
+ * de la lista necesita la `key` del elemento.
14863
+ */
14864
+ function isPart(node, parts) {
14865
+ return $8WNJc$react.isValidElement(node) && parts.has(node.type);
14866
+ }
14867
+ // ---------------------------------------------------------------------------------------------
14868
+ // Fachada del `Tab`: inserta el `Tabs.Indicator` que falte
14869
+ // ---------------------------------------------------------------------------------------------
14870
+ /**
14871
+ * ¿Hay ya un `Tabs.Indicator` entre los hijos? **Desciende por los Fragments**, que es lo único
14872
+ * que `Children.toArray` no aplana (los arrays sí — un `.map()` queda cubierto, medido).
14873
+ *
14874
+ * POR QUÉ AQUÍ SÍ SE DESCIENDE Y EN EL ROOT NO, que es la asimetría deliberada de esta fachada:
14875
+ * aquí solo hace falta DETECTAR (el indicador se añade al nivel del propio `Tab`, como hermano);
14876
+ * en el root haría falta INSERTAR dentro del Fragment del consumidor, o sea reescribir su árbol,
14877
+ * que es una intervención de otro orden. Y los modos de fallo también son distintos:
14878
+ * · sin esto, un indicador oculto en un Fragment daba DOS. Medido en el navegador: cajas y
14879
+ * estilos computados idénticos (85.7×32 en x=20, las dos se mueven juntas a x=165.9 al
14880
+ * pulsar), consola limpia, geometría de la tira sin cambio — pero la SOMBRA se compone dos
14881
+ * veces y el halo de 1-3 px alrededor de la píldora se oscurece: comparación de píxeles
14882
+ * contra la referencia sana, 608 px distintos de 39200 (1.55%) con delta máximo 17/255 en
14883
+ * claro (`rgb(225,225,226)` → `rgb(208,208,209)`), y 112 px con delta máximo 8/255 en oscuro
14884
+ * (ahí la sombra de v3 es transparente, así que solo es antialias del borde redondeado).
14885
+ * Cosmético, pero medible, y evitarlo cuesta esta función.
14886
+ * · sin descender en el root, una lista oculta en un Fragment deja la FORMA DELGADA, que es
14887
+ * una forma legítima de v3 y además el comportamiento de hoy: no hay regresión que evitar.
14888
+ *
14889
+ * No desciende por componentes del consumidor —solo por Fragments— porque los hijos de un
14890
+ * componente cualquiera todavía no se han renderizado y puede que no los renderice nunca.
14891
+ */
14892
+ function hasIndicator(children) {
14893
+ return $8WNJc$react.Children.toArray(children).some((node) => {
14894
+ if (!$8WNJc$react.isValidElement(node)) {
14895
+ return false;
14896
+ }
14897
+ if (node.type === react.Tabs.Indicator) {
14898
+ return true;
14899
+ }
14900
+ return node.type === $8WNJc$react.Fragment && hasIndicator(node.props.children);
14901
+ });
14902
+ }
14903
+ const TabFacade = ({ children, ...props }) => {
14904
+ // `children` como función es la forma de render props de react-aria (`TabRenderProps`). No se
14905
+ // puede inspeccionar, y menos aún combinar con un hermano —React rechaza una función como
14906
+ // hijo junto a un elemento—, así que se pasa tal cual. Un consumidor que la use monta el
14907
+ // indicador él (es la vía de escape de este caso). Exposición hoy: 0 usos en los 6 repos.
14908
+ if (typeof children === "function") {
14909
+ return jsxRuntime.jsx(react.Tabs.Tab, { ...props, children: children });
14910
+ }
14911
+ if (hasIndicator(children)) {
14912
+ return jsxRuntime.jsx(react.Tabs.Tab, { ...props, children: children });
14913
+ }
14914
+ // El indicador va PRIMERO por legibilidad del marcado, no por efecto: es `position:absolute`
14915
+ // con `z-index:-1`, o sea fuera del flujo y detrás del texto, así que el orden no cambia el
14916
+ // render (las dos apps que lo montan a mano difieren —analytics lo pone antes de la etiqueta,
14917
+ // `AnalyticsCard.tsx:177` después— y se ven igual).
14918
+ //
14919
+ // `{children}` se pasa SIN normalizar (nada de `Children.toArray`) para no reescribir las
14920
+ // keys del consumidor: hacerlo cambiaría la reconciliación de una lista de pestañas
14921
+ // condicional. Verificado en el navegador que la anidación no produce aviso de key.
14922
+ return (jsxRuntime.jsxs(react.Tabs.Tab, { ...props, children: [jsxRuntime.jsx(react.Tabs.Indicator, {}), children] }));
14923
+ };
14924
+ TabFacade.displayName = "Aurora.Tab";
14925
+ /**
14926
+ * `Tab` de Aurora: el `Tabs.Tab` de v3 **con el `Tabs.Indicator` que faltaba**.
14927
+ *
14928
+ * Es el MISMO componente que `Tabs.Tab` (el estático de abajo apunta aquí). Las dos superficies
14929
+ * de entrada importan porque las apps usan las dos: beweanalytics importa `Tab` suelto
14930
+ * (`import { Tab, Tabs } from "@beweco/aurora-ui"`, 8 usos) y agency/backoffice escriben
14931
+ * `Tabs.Tab`. En v3 los dos nombres salen del mismo módulo y son el mismo objeto
14932
+ * (`@heroui/react/dist/components/tabs/index.js`: `Tabs = Object.assign(TabsRoot, { …, Tab })`,
14933
+ * y `dist/index.js:92-93` los exporta por separado), así que servir fachadas distintas por cada
14934
+ * nombre habría sido el «falso amigo» que ya costó rondas en Alert/Checkbox/Switch.
14935
+ */
14936
+ const Tab = TabFacade;
14937
+ // ---------------------------------------------------------------------------------------------
14938
+ // Fachada del root: inserta el `Tabs.ListContainer` que falte
14939
+ // ---------------------------------------------------------------------------------------------
14940
+ /** Lo que va DENTRO del contenedor: la lista. */
14941
+ const INSIDE_CONTAINER = new Set([react.Tabs.List]);
14942
+ /** Si el consumidor ya monta el contenedor, la fachada no envuelve nada. */
14943
+ const OWN_CONTAINER = new Set([react.Tabs.ListContainer]);
14944
+ const TabsFacade = ({ children, ...props }) => {
14945
+ const nodes = $8WNJc$react.Children.toArray(children);
14946
+ const needsContainer = nodes.some((node) => isPart(node, INSIDE_CONTAINER)) &&
14947
+ !nodes.some((node) => isPart(node, OWN_CONTAINER));
14948
+ // Nada que envolver (sin hijos, `children` como función, un contenedor ya montado a mano, o
14949
+ // ninguna lista suelta): el root tal cual, byte a byte como el crudo de v3.
14950
+ if (!needsContainer) {
14951
+ return jsxRuntime.jsx(react.Tabs, { ...props, children: children });
14952
+ }
14953
+ // Se envuelve EN SITIO, no reagrupando como hace `Dropdown` —única desviación deliberada
14954
+ // respecto a ese precedente—: el popover del dropdown es `position:absolute`, así que allí da
14955
+ // igual en qué orden queden los hijos del root. Aquí `.tabs` es `flex` + `flex-col` en
14956
+ // horizontal (`tabs.css:2-8`), de modo que el ORDEN EN EL DOM es el orden visual; react-aria
14957
+ // lo vigila además por su cuenta (con el panel antes de la lista avisa por consola «There is
14958
+ // no tab id, please check if you have rendered the tab panel before the tab list»). O sea que
14959
+ // el orden de los hijos del root es observable, y reagrupar lo cambiaría sin ganar nada:
14960
+ // envolver en sitio es la intervención estrictamente menor y deja la geometría idéntica en
14961
+ // todos los casos.
14962
+ return (jsxRuntime.jsx(react.Tabs, { ...props, children: nodes.map((node) => isPart(node, INSIDE_CONTAINER) ? (jsxRuntime.jsx(react.Tabs.ListContainer, { children: node }, node.key)) : (node)) }));
14963
+ };
14964
+ TabsFacade.displayName = "Aurora.Tabs";
14965
+ /**
14966
+ * `Tabs` de Aurora: el root de v3 **más el `Tabs.ListContainer` que faltaba**, con el compound
14967
+ * completo colgado como estáticos y `Tabs.Tab` apuntando a la FACHADA del tab.
14968
+ *
14969
+ * ```tsx
14970
+ * // Mínimo: la fachada monta el carril y el indicador, y se ve correcto sin añadir nada.
14971
+ * <Tabs>
14972
+ * <Tabs.List aria-label="Vista">
14973
+ * <Tabs.Tab id="a">Alfa</Tabs.Tab>
14974
+ * </Tabs.List>
14975
+ * </Tabs>
14976
+ *
14977
+ * // Anatomía completa a mano: la fachada la respeta y no duplica nada.
14978
+ * <Tabs>
14979
+ * <Tabs.ListContainer>
14980
+ * <Tabs.List aria-label="Vista">
14981
+ * <Tabs.Tab id="a"><Tabs.Indicator />Alfa</Tabs.Tab>
14982
+ * </Tabs.List>
14983
+ * </Tabs.ListContainer>
14984
+ * </Tabs>
14985
+ * ```
14986
+ *
14987
+ * `Tabs.Root` queda deliberadamente crudo (el root pelado de v3, sin contenedor), igual que en
14988
+ * `Table` y `Dropdown`: vía de escape para la **forma delgada** de v3 (lista sin contenedor, sin
14989
+ * scroller ni chevrones), que sigue alcanzable y a la que `neutralize-v3.css` dedica su regla
14990
+ * `:not(:has(.tabs__list-container))`. Ojo: en la forma delgada el `variant="secondary"` de v3 no
14991
+ * pinta —todas sus reglas cuelgan de `> .tabs__list-container` (`tabs.css:234-282`)—, así que
14992
+ * pasar por la fachada es además lo que hace funcionar esa variante.
14993
+ *
14994
+ * `Tabs.Indicator` se conserva exportado (como estático y vía `Tab`) para quien quiera componerlo
14995
+ * a mano: es el objeto EXACTO de `@heroui/react`.
14996
+ */
14997
+ const Tabs = Object.assign(TabsFacade, {
14998
+ Indicator: react.Tabs.Indicator,
14999
+ List: react.Tabs.List,
15000
+ ListContainer: react.Tabs.ListContainer,
15001
+ Panel: react.Tabs.Panel,
15002
+ Root: react.Tabs.Root,
15003
+ Separator: react.Tabs.Separator,
15004
+ Tab: TabFacade,
15005
+ });
15006
+
14720
15007
  /**
14721
15008
  * Tooltip - Componente BeweOS basado en HeroUI.
14722
15009
  *
@@ -15617,10 +15904,6 @@ Object.defineProperty(exports, "Spinner", {
15617
15904
  enumerable: true,
15618
15905
  get: function () { return react.Spinner; }
15619
15906
  });
15620
- Object.defineProperty(exports, "Tab", {
15621
- enumerable: true,
15622
- get: function () { return react.Tab; }
15623
- });
15624
15907
  Object.defineProperty(exports, "TableBody", {
15625
15908
  enumerable: true,
15626
15909
  get: function () { return react.TableBody; }
@@ -15641,10 +15924,6 @@ Object.defineProperty(exports, "TableRow", {
15641
15924
  enumerable: true,
15642
15925
  get: function () { return react.TableRow; }
15643
15926
  });
15644
- Object.defineProperty(exports, "Tabs", {
15645
- enumerable: true,
15646
- get: function () { return react.Tabs; }
15647
- });
15648
15927
  Object.defineProperty(exports, "cn", {
15649
15928
  enumerable: true,
15650
15929
  get: function () { return react.cn; }
@@ -15746,7 +16025,9 @@ exports.StepIndicator = StepIndicator;
15746
16025
  exports.Switch = Switch;
15747
16026
  exports.SwitchComponent = Switch;
15748
16027
  exports.THEME_COLOR_HEX_MAP = THEME_COLOR_HEX_MAP;
16028
+ exports.Tab = Tab;
15749
16029
  exports.Table = Table;
16030
+ exports.Tabs = Tabs;
15750
16031
  exports.TagsFilter = TagsFilter;
15751
16032
  exports.Textarea = Textarea;
15752
16033
  exports.ThemeContext = ThemeContext;