@beweco/aurora-ui 1.0.0-beta.147 → 1.0.0-beta.149
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 +1007 -307
- package/dist/index.esm.js +1005 -309
- package/dist/types/components/beta-badge/BetaBadge.d.ts +9 -0
- package/dist/types/components/beta-badge/BetaBadge.d.ts.map +1 -0
- package/dist/types/components/beta-badge/BetaBadge.types.d.ts +31 -0
- package/dist/types/components/beta-badge/BetaBadge.types.d.ts.map +1 -0
- package/dist/types/components/beta-badge/beta-badge.styles.d.ts +2 -0
- package/dist/types/components/beta-badge/beta-badge.styles.d.ts.map +1 -0
- package/dist/types/components/beta-badge/index.d.ts +3 -0
- package/dist/types/components/beta-badge/index.d.ts.map +1 -0
- package/dist/types/components/linda-mascot/LindaMascot.d.ts +17 -0
- package/dist/types/components/linda-mascot/LindaMascot.d.ts.map +1 -0
- package/dist/types/components/linda-mascot/LindaMascot.types.d.ts +58 -0
- package/dist/types/components/linda-mascot/LindaMascot.types.d.ts.map +1 -0
- package/dist/types/components/linda-mascot/index.d.ts +4 -0
- package/dist/types/components/linda-mascot/index.d.ts.map +1 -0
- package/dist/types/components/linda-mascot/linda-mascot.constants.d.ts +55 -0
- package/dist/types/components/linda-mascot/linda-mascot.constants.d.ts.map +1 -0
- package/dist/types/components/linda-mascot/linda-mascot.styles.d.ts +2 -0
- package/dist/types/components/linda-mascot/linda-mascot.styles.d.ts.map +1 -0
- package/dist/types/components/toast/GlobalToast.d.ts.map +1 -1
- package/dist/types/index.d.ts +2 -0
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/utils/theme-scope.d.ts +18 -0
- package/dist/types/utils/theme-scope.d.ts.map +1 -0
- package/dist/types/utils/use-injected-style.d.ts +25 -0
- package/dist/types/utils/use-injected-style.d.ts.map +1 -0
- package/package.json +1 -1
package/dist/index.esm.js
CHANGED
|
@@ -2,7 +2,7 @@ import { Card as Card$1, Breadcrumbs, ProgressBar, Label, ListBoxSection, ListBo
|
|
|
2
2
|
export { Accordion, AccordionItem, Avatar, Breadcrumbs, CardFooter, CardHeader, CheckboxGroup, CloseButton, DropdownItem, DropdownMenu, DropdownTrigger, Link, Popover, PopoverContent, PopoverTrigger, Radio, RadioGroup, Skeleton, Slider, Spinner, TableBody, TableCell, TableColumn, TableHeader, TableRow } from '@heroui/react';
|
|
3
3
|
import { cnMerge } from 'tailwind-variants';
|
|
4
4
|
import { jsx, jsxs, Fragment as Fragment$1 } from 'react/jsx-runtime';
|
|
5
|
-
import $8WNJc$react, { Children, isValidElement, Fragment, useState, useRef, useCallback, useEffect, createElement, useId, forwardRef, useMemo, cloneElement, useImperativeHandle, createContext, useContext, useLayoutEffect, memo, useReducer, version } from 'react';
|
|
5
|
+
import $8WNJc$react, { Children, isValidElement, Fragment, useState, useRef, useCallback, useEffect, createElement, useId, forwardRef, useMemo, cloneElement, useInsertionEffect, useImperativeHandle, createContext, useContext, useLayoutEffect, memo, useReducer, version } from 'react';
|
|
6
6
|
import { Icon } from '@iconify/react';
|
|
7
7
|
export { loadIcons } from '@iconify/react';
|
|
8
8
|
import { ResponsiveContainer, AreaChart, CartesianGrid, XAxis, Tooltip as Tooltip$1, Area, LineChart, YAxis, Line, PieChart, Pie, Cell } from 'recharts';
|
|
@@ -1406,7 +1406,7 @@ const ACTIVE_SHADOW_CLASS = {
|
|
|
1406
1406
|
* Traducciones por defecto en español. Todas son `aria-label`: este componente no tiene más
|
|
1407
1407
|
* texto visible que los números de página.
|
|
1408
1408
|
*/
|
|
1409
|
-
const DEFAULT_TRANSLATIONS$
|
|
1409
|
+
const DEFAULT_TRANSLATIONS$h = {
|
|
1410
1410
|
previousPageAriaLabel: "Página anterior",
|
|
1411
1411
|
nextPageAriaLabel: "Página siguiente",
|
|
1412
1412
|
jumpBackwardAriaLabel: "Retroceder {count} páginas",
|
|
@@ -1437,7 +1437,7 @@ const EN_TRANSLATIONS = {
|
|
|
1437
1437
|
function baseTranslations(locale) {
|
|
1438
1438
|
return locale.toLowerCase().startsWith("en")
|
|
1439
1439
|
? EN_TRANSLATIONS
|
|
1440
|
-
: DEFAULT_TRANSLATIONS$
|
|
1440
|
+
: DEFAULT_TRANSLATIONS$h;
|
|
1441
1441
|
}
|
|
1442
1442
|
/**
|
|
1443
1443
|
* TECH-2459 · elipsis navegable — páginas que salta la elipsis. Literal de v2:
|
|
@@ -3201,7 +3201,7 @@ function withV2ItemContent(children) {
|
|
|
3201
3201
|
}
|
|
3202
3202
|
|
|
3203
3203
|
/** Traducciones por defecto en español (mismo literal que la ✕ de la fachada `Input`). */
|
|
3204
|
-
const DEFAULT_TRANSLATIONS$
|
|
3204
|
+
const DEFAULT_TRANSLATIONS$g = {
|
|
3205
3205
|
clearButtonAriaLabel: "Limpiar",
|
|
3206
3206
|
emptyContent: "No se encontraron resultados",
|
|
3207
3207
|
};
|
|
@@ -3559,7 +3559,7 @@ labelPlacement: _labelPlacement, scrollShadowProps, ...props }) => {
|
|
|
3559
3559
|
: icon === undefined
|
|
3560
3560
|
? DEFAULT_ICON
|
|
3561
3561
|
: icon;
|
|
3562
|
-
const t = { ...DEFAULT_TRANSLATIONS$
|
|
3562
|
+
const t = { ...DEFAULT_TRANSLATIONS$g, ...translations };
|
|
3563
3563
|
// La regla de v2 para la lista vacía (ver `EMPTY_CONTENT_CLASS`).
|
|
3564
3564
|
const hideEmptyContent = Boolean(allowsCustomValue) && listboxProps?.emptyContent == null;
|
|
3565
3565
|
const emptyContent = listboxProps?.emptyContent ?? t.emptyContent;
|
|
@@ -3697,274 +3697,49 @@ labelPlacement: _labelPlacement, scrollShadowProps, ...props }) => {
|
|
|
3697
3697
|
};
|
|
3698
3698
|
|
|
3699
3699
|
/**
|
|
3700
|
-
*
|
|
3701
|
-
*
|
|
3702
|
-
*
|
|
3703
|
-
|
|
3704
|
-
|
|
3705
|
-
*
|
|
3706
|
-
|
|
3707
|
-
|
|
3708
|
-
|
|
3709
|
-
|
|
3710
|
-
|
|
3711
|
-
|
|
3712
|
-
|
|
3713
|
-
|
|
3714
|
-
|
|
3715
|
-
|
|
3716
|
-
|
|
3717
|
-
{ key: "pink", name: "Rosa", value: "#EC4899" },
|
|
3718
|
-
{ key: "rose", name: "Rosa claro", value: "#F43F5E" },
|
|
3719
|
-
{ key: "orange", name: "Naranja", value: "#F97316" },
|
|
3720
|
-
// Neutral colors
|
|
3721
|
-
{ key: "red", name: "Rojo", value: "#EF4444" },
|
|
3722
|
-
{ key: "amber", name: "Ámbar", value: "#F59E0B" },
|
|
3723
|
-
{ key: "yellow", name: "Amarillo", value: "#EAB308" },
|
|
3724
|
-
// Gray scale (useful for text/backgrounds)
|
|
3725
|
-
{ key: "gray-dark", name: "Gris oscuro", value: "#374151" },
|
|
3726
|
-
{ key: "gray", name: "Gris", value: "#6B7280" },
|
|
3727
|
-
{ key: "gray-light", name: "Gris claro", value: "#9CA3AF" },
|
|
3728
|
-
];
|
|
3729
|
-
|
|
3730
|
-
/**
|
|
3731
|
-
* Default translations (Spanish)
|
|
3732
|
-
*/
|
|
3733
|
-
const defaultTranslations$e = {
|
|
3734
|
-
label: "Color",
|
|
3735
|
-
placeholder: "Ingresa código hex",
|
|
3736
|
-
customColorLabel: "Color personalizado",
|
|
3737
|
-
predefinedColorsLabel: "Colores predefinidos",
|
|
3738
|
-
recentColorsLabel: "Colores recientes",
|
|
3739
|
-
noRecentColors: "No hay colores recientes",
|
|
3740
|
-
invalidColorFormat: "El código de color no es un formato HEX válido (Ej: #FF5733 o FFA07A)",
|
|
3741
|
-
colorPreview: "Vista previa del color",
|
|
3742
|
-
};
|
|
3743
|
-
/**
|
|
3744
|
-
* Validates hex color format (#RRGGBB or #RGB)
|
|
3745
|
-
*/
|
|
3746
|
-
const isValidHexColor = (color) => {
|
|
3747
|
-
const hexRegex = /^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/;
|
|
3748
|
-
return hexRegex.test(color);
|
|
3749
|
-
};
|
|
3750
|
-
/**
|
|
3751
|
-
* Normalizes hex color to 6-digit format (#RGB -> #RRGGBB)
|
|
3752
|
-
*/
|
|
3753
|
-
const normalizeHexColor = (color) => {
|
|
3754
|
-
if (!color.startsWith("#")) {
|
|
3755
|
-
color = `#${color}`;
|
|
3700
|
+
* Deja una `<style id>` en el `<head>` con las reglas de un componente, una sola vez.
|
|
3701
|
+
*
|
|
3702
|
+
* Existe para los componentes que no pueden depender de `@beweco/aurora-ui/styles`: los
|
|
3703
|
+
* que llegan a un microfrontend por Module Federation (chatbot-claw recibe aurora-ui del
|
|
3704
|
+
* host con `import: false`) o se montan sueltos. Si sus reglas vivieran en `styles.css`,
|
|
3705
|
+
* funcionarían solo cuando el host carga ese CSS y además en la misma versión que el JS
|
|
3706
|
+
* que viaja por MF; cuando no, el componente sale sin animación ni tema y nadie se entera.
|
|
3707
|
+
* Aquí la regla viaja con el componente, como ya hace `SocialMediaCarousel`.
|
|
3708
|
+
*
|
|
3709
|
+
* Idempotente por `id`: cien mascotas en un hilo dejan una sola `<style>`. Nunca reescribe
|
|
3710
|
+
* una `<style>` existente: si en la página conviven dos copias del paquete, se pisarían en
|
|
3711
|
+
* cada montaje. Para que una versión nueva del CSS entre, el id lleva su huella
|
|
3712
|
+
* (`styleIdFor`).
|
|
3713
|
+
*/
|
|
3714
|
+
const injectStyle = (doc, id, css) => {
|
|
3715
|
+
if (!doc?.head) {
|
|
3716
|
+
return;
|
|
3756
3717
|
}
|
|
3757
|
-
|
|
3758
|
-
|
|
3759
|
-
const r = color[1];
|
|
3760
|
-
const g = color[2];
|
|
3761
|
-
const b = color[3];
|
|
3762
|
-
return `#${r}${r}${g}${g}${b}${b}`.toUpperCase();
|
|
3718
|
+
if (doc.getElementById(id)) {
|
|
3719
|
+
return;
|
|
3763
3720
|
}
|
|
3764
|
-
|
|
3721
|
+
const style = doc.createElement("style");
|
|
3722
|
+
style.id = id;
|
|
3723
|
+
style.textContent = css;
|
|
3724
|
+
doc.head.appendChild(style);
|
|
3725
|
+
};
|
|
3726
|
+
/** `<base>-<huella del css>`: estable para el mismo CSS, distinto si cambia. */
|
|
3727
|
+
const styleIdFor = (base, css) => {
|
|
3728
|
+
let hash = 5381;
|
|
3729
|
+
for (let i = 0; i < css.length; i++) {
|
|
3730
|
+
hash = ((hash << 5) + hash + css.charCodeAt(i)) | 0;
|
|
3731
|
+
}
|
|
3732
|
+
return `${base}-${(hash >>> 0).toString(36)}`;
|
|
3765
3733
|
};
|
|
3766
3734
|
/**
|
|
3767
|
-
*
|
|
3768
|
-
*
|
|
3735
|
+
* `useInsertionEffect` corre antes de pintar y antes que los efectos de layout: las reglas
|
|
3736
|
+
* están en el documento cuando el SVG aparece por primera vez, sin parpadeo sin estilo.
|
|
3737
|
+
* En servidor no corre (y `renderToStaticMarkup` en los tests tampoco lo ejecuta).
|
|
3769
3738
|
*/
|
|
3770
|
-
const
|
|
3771
|
-
|
|
3772
|
-
|
|
3773
|
-
|
|
3774
|
-
* Features:
|
|
3775
|
-
* - Native color picker input
|
|
3776
|
-
* - Text input for hex codes
|
|
3777
|
-
* - Predefined color palette
|
|
3778
|
-
* - Recent colors with localStorage persistence
|
|
3779
|
-
* - Full i18n support
|
|
3780
|
-
* - Dark mode compatible
|
|
3781
|
-
* - Accessible
|
|
3782
|
-
*
|
|
3783
|
-
* @component
|
|
3784
|
-
* @example
|
|
3785
|
-
* ```tsx
|
|
3786
|
-
* <ColorPicker
|
|
3787
|
-
* value={brandColor}
|
|
3788
|
-
* onChange={setBrandColor}
|
|
3789
|
-
* label="Color de marca"
|
|
3790
|
-
* enableRecentColors
|
|
3791
|
-
* />
|
|
3792
|
-
* ```
|
|
3793
|
-
*/
|
|
3794
|
-
const ColorPicker = forwardRef(({ value, onChange, label, required = false, disabled = false, error = false, errorText, predefinedColors = DEFAULT_PREDEFINED_COLORS, enableRecentColors = true, maxRecentColors = 6, recentColorsStorageKey = "aurora-ui-recent-colors", showPredefinedColors = true, translations = {}, className = "", id, name, }, ref) => {
|
|
3795
|
-
const generatedId = useId();
|
|
3796
|
-
const inputId = id || generatedId;
|
|
3797
|
-
const t = { ...defaultTranslations$e, ...translations };
|
|
3798
|
-
// Local state for text input — holds the hex digits WITHOUT the leading "#".
|
|
3799
|
-
// The "#" is rendered as a fixed startContent prefix.
|
|
3800
|
-
const [textValue, setTextValue] = useState(stripHash(value));
|
|
3801
|
-
const [isInvalidFormat, setIsInvalidFormat] = useState(false);
|
|
3802
|
-
// Recent colors state
|
|
3803
|
-
const [recentColors, setRecentColors] = useState([]);
|
|
3804
|
-
// Load recent colors from localStorage on mount
|
|
3805
|
-
useEffect(() => {
|
|
3806
|
-
if (!enableRecentColors)
|
|
3807
|
-
return;
|
|
3808
|
-
try {
|
|
3809
|
-
const stored = localStorage.getItem(recentColorsStorageKey);
|
|
3810
|
-
if (stored) {
|
|
3811
|
-
const colors = JSON.parse(stored);
|
|
3812
|
-
setRecentColors(colors);
|
|
3813
|
-
}
|
|
3814
|
-
}
|
|
3815
|
-
catch (error) {
|
|
3816
|
-
console.error("Error loading recent colors:", error);
|
|
3817
|
-
}
|
|
3818
|
-
}, [enableRecentColors, recentColorsStorageKey]);
|
|
3819
|
-
// Save to recent colors when value changes
|
|
3820
|
-
useEffect(() => {
|
|
3821
|
-
if (!enableRecentColors || !value || !isValidHexColor(value))
|
|
3822
|
-
return;
|
|
3823
|
-
const normalizedValue = normalizeHexColor(value);
|
|
3824
|
-
// Add to recent colors if not already present
|
|
3825
|
-
setRecentColors((prev) => {
|
|
3826
|
-
// Remove if already exists
|
|
3827
|
-
const filtered = prev.filter((c) => c !== normalizedValue);
|
|
3828
|
-
// Add to beginning
|
|
3829
|
-
const updated = [normalizedValue, ...filtered].slice(0, maxRecentColors);
|
|
3830
|
-
// Save to localStorage
|
|
3831
|
-
try {
|
|
3832
|
-
localStorage.setItem(recentColorsStorageKey, JSON.stringify(updated));
|
|
3833
|
-
}
|
|
3834
|
-
catch (error) {
|
|
3835
|
-
console.error("Error saving recent colors:", error);
|
|
3836
|
-
}
|
|
3837
|
-
return updated;
|
|
3838
|
-
});
|
|
3839
|
-
}, [value, enableRecentColors, maxRecentColors, recentColorsStorageKey]);
|
|
3840
|
-
// Sync text value when prop value changes
|
|
3841
|
-
useEffect(() => {
|
|
3842
|
-
setTextValue(stripHash(value));
|
|
3843
|
-
setIsInvalidFormat(false);
|
|
3844
|
-
}, [value]);
|
|
3845
|
-
/**
|
|
3846
|
-
* Handle color picker (native input) change
|
|
3847
|
-
*/
|
|
3848
|
-
const handleColorPickerChange = (e) => {
|
|
3849
|
-
const newColor = normalizeHexColor(e.target.value);
|
|
3850
|
-
setTextValue(stripHash(newColor));
|
|
3851
|
-
setIsInvalidFormat(false);
|
|
3852
|
-
onChange(newColor);
|
|
3853
|
-
};
|
|
3854
|
-
/**
|
|
3855
|
-
* Handle text input change — passive: no validation, normalization, or
|
|
3856
|
-
* onChange propagation while typing. We only update local state and clear
|
|
3857
|
-
* any stale error so the user can correct an invalid value without the
|
|
3858
|
-
* red border lingering. Validation (and stripping of the leading "#")
|
|
3859
|
-
* runs on blur.
|
|
3860
|
-
*
|
|
3861
|
-
* While typing we let the user see whatever they pressed — including a
|
|
3862
|
-
* leading "#" — so the input feels responsive. To prevent a double "#"
|
|
3863
|
-
* on screen, the fixed prefix in `startContent` is hidden while
|
|
3864
|
-
* `textValue` already starts with "#". The cap is `7` (1 "#" + 6 hex)
|
|
3865
|
-
* when the user opted into a leading "#", otherwise `6`.
|
|
3866
|
-
*/
|
|
3867
|
-
const handleTextChange = (newValue) => {
|
|
3868
|
-
const max = newValue.startsWith("#") ? 7 : 6;
|
|
3869
|
-
setTextValue(newValue.slice(0, max));
|
|
3870
|
-
setIsInvalidFormat(false);
|
|
3871
|
-
};
|
|
3872
|
-
/**
|
|
3873
|
-
* Validate the current hex input. Updates `isInvalidFormat` and, when
|
|
3874
|
-
* the input is a valid hex, normalizes the display and propagates via
|
|
3875
|
-
* `onChange`. Returns whether the input is acceptable so a caller can
|
|
3876
|
-
* decide to block submission.
|
|
3877
|
-
*
|
|
3878
|
-
* Empty + not required → valid (no error, no propagation).
|
|
3879
|
-
* Empty + required → invalid (error).
|
|
3880
|
-
* Valid (3 or 6 hex chars, with or without leading "#") → normalize, sync
|
|
3881
|
-
* display to canonical form, propagate, valid.
|
|
3882
|
-
* Otherwise → invalid (error, no propagation).
|
|
3883
|
-
*/
|
|
3884
|
-
const validate = useCallback(() => {
|
|
3885
|
-
// User may have typed a leading "#" — strip it so it doesn't end up
|
|
3886
|
-
// as "##" once we prepend "#" for the regex check.
|
|
3887
|
-
const trimmed = stripHash(textValue.trim());
|
|
3888
|
-
if (trimmed === "") {
|
|
3889
|
-
const valid = !required;
|
|
3890
|
-
setIsInvalidFormat(!valid);
|
|
3891
|
-
// Drop any residual "#" the user typed but never followed up on,
|
|
3892
|
-
// so the field reads as truly empty after blur.
|
|
3893
|
-
setTextValue("");
|
|
3894
|
-
return valid;
|
|
3895
|
-
}
|
|
3896
|
-
const candidate = `#${trimmed}`;
|
|
3897
|
-
if (!isValidHexColor(candidate)) {
|
|
3898
|
-
setIsInvalidFormat(true);
|
|
3899
|
-
return false;
|
|
3900
|
-
}
|
|
3901
|
-
const normalized = normalizeHexColor(candidate);
|
|
3902
|
-
setIsInvalidFormat(false);
|
|
3903
|
-
setTextValue(stripHash(normalized));
|
|
3904
|
-
onChange(normalized);
|
|
3905
|
-
return true;
|
|
3906
|
-
}, [textValue, required, onChange]);
|
|
3907
|
-
useImperativeHandle(ref, () => ({ validate }), [validate]);
|
|
3908
|
-
const handleTextBlur = () => {
|
|
3909
|
-
validate();
|
|
3910
|
-
};
|
|
3911
|
-
/**
|
|
3912
|
-
* Handle predefined color selection
|
|
3913
|
-
*/
|
|
3914
|
-
const handlePredefinedColorClick = (colorValue) => {
|
|
3915
|
-
if (disabled)
|
|
3916
|
-
return;
|
|
3917
|
-
const normalized = normalizeHexColor(colorValue);
|
|
3918
|
-
setTextValue(stripHash(normalized));
|
|
3919
|
-
setIsInvalidFormat(false);
|
|
3920
|
-
onChange(normalized);
|
|
3921
|
-
};
|
|
3922
|
-
/**
|
|
3923
|
-
* Render a color circle button
|
|
3924
|
-
*/
|
|
3925
|
-
const renderColorCircle = (colorValue, colorName, isSelected) => (jsx("button", { type: "button", disabled: disabled, onClick: () => handlePredefinedColorClick(colorValue), className: `
|
|
3926
|
-
w-8 h-8 rounded-full border-2 cursor-pointer transition-all
|
|
3927
|
-
focus:outline-none focus:ring-2 focus:ring-primary-500 focus:ring-offset-1
|
|
3928
|
-
${disabled ? "opacity-50 cursor-not-allowed" : "hover:scale-110"}
|
|
3929
|
-
${isSelected
|
|
3930
|
-
? "border-primary-500 scale-110 ring-2 ring-primary-500 ring-offset-1"
|
|
3931
|
-
: "border-default-700"}
|
|
3932
|
-
`, style: { backgroundColor: colorValue }, title: colorName, "aria-label": colorName }, colorValue));
|
|
3933
|
-
return (jsxs("div", { className: `flex flex-col gap-3 ${className}`, children: [label && (jsxs("label", { htmlFor: inputId, className: "block text-sm font-medium text-foreground", children: [label, required && jsx("span", { className: "text-danger ml-1", children: "*" })] })), jsxs("div", { className: "flex flex-col gap-1", children: [jsxs("div", { className: "flex items-center gap-2", children: [jsx("input", { type: "color", id: inputId, name: name, value: `#${stripHash(textValue)}`, onChange: handleColorPickerChange, disabled: disabled, className: `
|
|
3934
|
-
w-10 h-10 rounded-lg cursor-pointer border-2 flex-shrink-0
|
|
3935
|
-
transition-colors appearance-none bg-transparent p-0.5
|
|
3936
|
-
focus:border-primary-500
|
|
3937
|
-
${disabled ? "opacity-50 cursor-not-allowed" : "hover:border-default-400"}
|
|
3938
|
-
${error
|
|
3939
|
-
? "border-danger dark:border-danger-400"
|
|
3940
|
-
: "border-default-300 dark:border-default-700"}
|
|
3941
|
-
`, "aria-label": t.customColorLabel, title: t.customColorLabel }), jsx(Input, { type: "text", value: textValue, onValueChange: handleTextChange, onBlur: handleTextBlur, placeholder: t.placeholder, disabled: disabled, isDisabled: disabled, isInvalid: error || isInvalidFormat, size: "sm", variant: "bordered",
|
|
3942
|
-
// `min-w-0` (TECH-2459, beweossmbs QA 24-sep): el campo es un ítem de la fila
|
|
3943
|
-
// flex de la muestra de color y tiene que poder encoger en huecos estrechos
|
|
3944
|
-
// (Agenda → Colors: 180px). El `w-full min-w-0` del `<input>` en la fachada
|
|
3945
|
-
// ya lo arregla; esto es el suelo explícito en la raíz del campo.
|
|
3946
|
-
className: "min-w-0", classNames: {
|
|
3947
|
-
input: "uppercase font-mono text-sm",
|
|
3948
|
-
inputWrapper: "min-h-8 h-8 focus-within:!border-primary-500",
|
|
3949
|
-
}, startContent: textValue.startsWith("#") ? null : (jsx("span", { className: "text-default-400 text-sm", children: "#" })) })] }), (error || isInvalidFormat) && (jsx("p", { className: "text-xs text-danger", children: isInvalidFormat ? t.invalidColorFormat : errorText }))] }), showPredefinedColors && predefinedColors.length > 0 && (jsxs("div", { children: [jsx("p", { className: "text-xs font-medium text-default-600 mb-2", children: t.predefinedColorsLabel }), jsx("div", { className: "flex flex-wrap gap-2", children: predefinedColors.map((color) => renderColorCircle(color.value, color.name, normalizeHexColor(value) === normalizeHexColor(color.value))) })] })), enableRecentColors && recentColors.length > 0 && (jsxs("div", { children: [jsx("p", { className: "text-xs font-medium text-default-600 mb-2", children: t.recentColorsLabel }), jsx("div", { className: "flex flex-wrap gap-2", children: recentColors.map((color) => renderColorCircle(color, color, normalizeHexColor(value) === color)) })] }))] }));
|
|
3950
|
-
});
|
|
3951
|
-
ColorPicker.displayName = "ColorPicker";
|
|
3952
|
-
|
|
3953
|
-
const ThemeContext = createContext({
|
|
3954
|
-
mode: "light",
|
|
3955
|
-
color: "blue",
|
|
3956
|
-
customHex: null,
|
|
3957
|
-
// biome-ignore lint/suspicious/noEmptyBlockStatements: <explanation>
|
|
3958
|
-
setMode: () => { },
|
|
3959
|
-
// biome-ignore lint/suspicious/noEmptyBlockStatements: <explanation>
|
|
3960
|
-
setColor: () => { },
|
|
3961
|
-
});
|
|
3962
|
-
const useThemeContext = () => {
|
|
3963
|
-
const context = useContext(ThemeContext);
|
|
3964
|
-
if (!context) {
|
|
3965
|
-
throw new Error("useThemeContext debe ser usado dentro de un ThemeProvider");
|
|
3966
|
-
}
|
|
3967
|
-
return context;
|
|
3739
|
+
const useInjectedStyle = (id, css) => {
|
|
3740
|
+
useInsertionEffect(() => {
|
|
3741
|
+
injectStyle(typeof document === "undefined" ? undefined : document, id, css);
|
|
3742
|
+
}, [id, css]);
|
|
3968
3743
|
};
|
|
3969
3744
|
|
|
3970
3745
|
const themeColors = {
|
|
@@ -4765,6 +4540,406 @@ const themeColors = {
|
|
|
4765
4540
|
},
|
|
4766
4541
|
};
|
|
4767
4542
|
|
|
4543
|
+
/**
|
|
4544
|
+
* Los mismos nombres de tema que resuelve HeroUI v3 (`@heroui/styles`):
|
|
4545
|
+
* `:root, .light, .default, [data-theme=light], [data-theme=default]` para el claro y
|
|
4546
|
+
* `.dark, [data-theme=dark]` para el oscuro. `default` es un alias del claro en v3; en v2 no
|
|
4547
|
+
* existía. Más los 14 packs de aurora, cada uno en el lado de su `extend`, que son las clases
|
|
4548
|
+
* que `theme-packs.generated.css` declara y `applyThemeClass` pone en `<html>`.
|
|
4549
|
+
*/
|
|
4550
|
+
const V3_ALIASES = {
|
|
4551
|
+
light: [".default", '[data-theme="default"]'],
|
|
4552
|
+
dark: [],
|
|
4553
|
+
};
|
|
4554
|
+
const namesOf = (mode) => [
|
|
4555
|
+
`.${mode}`,
|
|
4556
|
+
`[data-theme="${mode}"]`,
|
|
4557
|
+
...V3_ALIASES[mode],
|
|
4558
|
+
...Object.entries(themeColors)
|
|
4559
|
+
.filter(([, theme]) => theme.extend === mode)
|
|
4560
|
+
.map(([name]) => `.${name}`),
|
|
4561
|
+
];
|
|
4562
|
+
/**
|
|
4563
|
+
* Selectores para que un componente con `appearance="auto"` siga al tema del ancestro MÁS
|
|
4564
|
+
* CERCANO.
|
|
4565
|
+
*
|
|
4566
|
+
* Los temas no solo se llaman `dark`/`light`: el `ThemeProvider` de esta librería pone en
|
|
4567
|
+
* `<html>` el tema completo (`blue-dark`, `purple-light`…) y nunca la clase `dark` suelta,
|
|
4568
|
+
* así que mirar solo `.dark` dejaría la pieza en claro sobre un tema oscuro de aurora.
|
|
4569
|
+
*
|
|
4570
|
+
* - `dark`: cualquier ancestro oscuro, o uno oscuro dentro de uno claro.
|
|
4571
|
+
* - `light`: uno claro dentro de uno oscuro (un panel claro en una página oscura). Tiene
|
|
4572
|
+
* una clase más que el oscuro simple, así que gana; va después en la hoja para ganar
|
|
4573
|
+
* también al «oscuro dentro de claro» cuando hay tres niveles.
|
|
4574
|
+
*/
|
|
4575
|
+
const themeScopeSelectors = (target) => {
|
|
4576
|
+
const dark = namesOf("dark");
|
|
4577
|
+
const light = namesOf("light");
|
|
4578
|
+
const pairs = (outer, inner) => outer.flatMap((o) => inner.map((i) => `${o} ${i} ${target}`));
|
|
4579
|
+
return {
|
|
4580
|
+
dark: [...dark.map((d) => `${d} ${target}`), ...pairs(light, dark)].join(",\n"),
|
|
4581
|
+
light: pairs(dark, light).join(",\n"),
|
|
4582
|
+
};
|
|
4583
|
+
};
|
|
4584
|
+
|
|
4585
|
+
/**
|
|
4586
|
+
* Reglas del sello Beta. Viajan con el componente (se inyectan en `<head>`), igual que las
|
|
4587
|
+
* de `LindaMascot`: el sello también se pinta dentro del microfrontend del chat.
|
|
4588
|
+
*
|
|
4589
|
+
* Borde de 1 px con el gradiente iridiscente de la mascota muy atenuado: dos fondos, el
|
|
4590
|
+
* neutro recortado al `padding-box` y el gradiente al `border-box`, con el borde
|
|
4591
|
+
* transparente para que se vea el de debajo. Es neutral de marca: lo verá también Luna.
|
|
4592
|
+
*/
|
|
4593
|
+
const AUTO_THEME$1 = themeScopeSelectors('.aurora-beta-badge[data-appearance="auto"]');
|
|
4594
|
+
const BETA_BADGE_CSS = `
|
|
4595
|
+
.aurora-beta-badge {
|
|
4596
|
+
--bb-bg: #F6F5FB;
|
|
4597
|
+
--bb-fg: #4B4F6E;
|
|
4598
|
+
--bb-edge: 52%;
|
|
4599
|
+
display: inline-flex;
|
|
4600
|
+
align-items: center;
|
|
4601
|
+
justify-content: center;
|
|
4602
|
+
box-sizing: border-box;
|
|
4603
|
+
border: 1px solid transparent;
|
|
4604
|
+
border-radius: 999px;
|
|
4605
|
+
background:
|
|
4606
|
+
linear-gradient(var(--bb-bg), var(--bb-bg)) padding-box,
|
|
4607
|
+
linear-gradient(
|
|
4608
|
+
100deg,
|
|
4609
|
+
color-mix(in srgb, #FF8AD8 var(--bb-edge), var(--bb-bg)),
|
|
4610
|
+
color-mix(in srgb, #A48BFF var(--bb-edge), var(--bb-bg)) 38%,
|
|
4611
|
+
color-mix(in srgb, #6FE3FF var(--bb-edge), var(--bb-bg)) 70%,
|
|
4612
|
+
color-mix(in srgb, #FFD08A var(--bb-edge), var(--bb-bg))
|
|
4613
|
+
) border-box;
|
|
4614
|
+
color: var(--bb-fg);
|
|
4615
|
+
font-family: inherit;
|
|
4616
|
+
font-weight: 600;
|
|
4617
|
+
line-height: 1;
|
|
4618
|
+
letter-spacing: 0.06em;
|
|
4619
|
+
text-transform: uppercase;
|
|
4620
|
+
white-space: nowrap;
|
|
4621
|
+
vertical-align: middle;
|
|
4622
|
+
user-select: none;
|
|
4623
|
+
flex-shrink: 0;
|
|
4624
|
+
}
|
|
4625
|
+
/* Tema: sigue al ancestro claro/oscuro más cercano (ver \`themeScopeSelectors\`). */
|
|
4626
|
+
${AUTO_THEME$1.dark},
|
|
4627
|
+
.aurora-beta-badge[data-appearance="dark"] {
|
|
4628
|
+
--bb-bg: #1C1D30;
|
|
4629
|
+
--bb-fg: #D5D8F0;
|
|
4630
|
+
--bb-edge: 58%;
|
|
4631
|
+
}
|
|
4632
|
+
${AUTO_THEME$1.light} {
|
|
4633
|
+
--bb-bg: #F6F5FB;
|
|
4634
|
+
--bb-fg: #4B4F6E;
|
|
4635
|
+
--bb-edge: 52%;
|
|
4636
|
+
}
|
|
4637
|
+
.aurora-beta-badge--sm {
|
|
4638
|
+
font-size: 10px;
|
|
4639
|
+
height: 16px;
|
|
4640
|
+
padding: 0 6px 0 calc(6px + 0.06em);
|
|
4641
|
+
}
|
|
4642
|
+
.aurora-beta-badge--md {
|
|
4643
|
+
font-size: 11px;
|
|
4644
|
+
height: 18px;
|
|
4645
|
+
padding: 0 7px 0 calc(7px + 0.06em);
|
|
4646
|
+
}
|
|
4647
|
+
`;
|
|
4648
|
+
|
|
4649
|
+
const DEFAULT_TRANSLATIONS$f = {
|
|
4650
|
+
label: "Beta",
|
|
4651
|
+
title: "Funcionalidad en pruebas: puede cambiar",
|
|
4652
|
+
};
|
|
4653
|
+
const BETA_BADGE_STYLE_ID = styleIdFor("aurora-beta-badge-styles", BETA_BADGE_CSS);
|
|
4654
|
+
/**
|
|
4655
|
+
* BetaBadge — sello «Beta» para lo que está en pruebas con clientes reales (Linda Claw).
|
|
4656
|
+
* Píldora pequeña en mayúsculas con el borde iridiscente de la mascota muy atenuado.
|
|
4657
|
+
* Ver README.
|
|
4658
|
+
*/
|
|
4659
|
+
const BetaBadge = forwardRef(function BetaBadge({ size = "sm", title, appearance = "auto", translations, className, ...spanProps }, ref) {
|
|
4660
|
+
useInjectedStyle(BETA_BADGE_STYLE_ID, BETA_BADGE_CSS);
|
|
4661
|
+
const t = { ...DEFAULT_TRANSLATIONS$f, ...translations };
|
|
4662
|
+
const tooltip = title === true ? t.title : title || undefined;
|
|
4663
|
+
return (jsx("span", { ref: ref, className: [
|
|
4664
|
+
"aurora-beta-badge",
|
|
4665
|
+
`aurora-beta-badge--${size}`,
|
|
4666
|
+
className,
|
|
4667
|
+
]
|
|
4668
|
+
.filter(Boolean)
|
|
4669
|
+
.join(" "), "data-appearance": appearance, title: tooltip, ...spanProps, children: t.label }));
|
|
4670
|
+
});
|
|
4671
|
+
|
|
4672
|
+
/**
|
|
4673
|
+
* ColorPicker Types
|
|
4674
|
+
*
|
|
4675
|
+
* Defines the type system for the ColorPicker component used for brand configuration.
|
|
4676
|
+
*/
|
|
4677
|
+
/**
|
|
4678
|
+
* Default predefined colors (Brand-safe palette)
|
|
4679
|
+
*/
|
|
4680
|
+
const DEFAULT_PREDEFINED_COLORS = [
|
|
4681
|
+
// Primary colors
|
|
4682
|
+
{ key: "blue", name: "Azul", value: "#3B82F6" },
|
|
4683
|
+
{ key: "violet", name: "Violeta", value: "#8B5CF6" },
|
|
4684
|
+
{ key: "purple", name: "Púrpura", value: "#A855F7" },
|
|
4685
|
+
// Secondary colors
|
|
4686
|
+
{ key: "green", name: "Verde", value: "#10B981" },
|
|
4687
|
+
{ key: "teal", name: "Turquesa", value: "#14B8A6" },
|
|
4688
|
+
{ key: "cyan", name: "Cian", value: "#06B6D4" },
|
|
4689
|
+
// Accent colors
|
|
4690
|
+
{ key: "pink", name: "Rosa", value: "#EC4899" },
|
|
4691
|
+
{ key: "rose", name: "Rosa claro", value: "#F43F5E" },
|
|
4692
|
+
{ key: "orange", name: "Naranja", value: "#F97316" },
|
|
4693
|
+
// Neutral colors
|
|
4694
|
+
{ key: "red", name: "Rojo", value: "#EF4444" },
|
|
4695
|
+
{ key: "amber", name: "Ámbar", value: "#F59E0B" },
|
|
4696
|
+
{ key: "yellow", name: "Amarillo", value: "#EAB308" },
|
|
4697
|
+
// Gray scale (useful for text/backgrounds)
|
|
4698
|
+
{ key: "gray-dark", name: "Gris oscuro", value: "#374151" },
|
|
4699
|
+
{ key: "gray", name: "Gris", value: "#6B7280" },
|
|
4700
|
+
{ key: "gray-light", name: "Gris claro", value: "#9CA3AF" },
|
|
4701
|
+
];
|
|
4702
|
+
|
|
4703
|
+
/**
|
|
4704
|
+
* Default translations (Spanish)
|
|
4705
|
+
*/
|
|
4706
|
+
const defaultTranslations$e = {
|
|
4707
|
+
label: "Color",
|
|
4708
|
+
placeholder: "Ingresa código hex",
|
|
4709
|
+
customColorLabel: "Color personalizado",
|
|
4710
|
+
predefinedColorsLabel: "Colores predefinidos",
|
|
4711
|
+
recentColorsLabel: "Colores recientes",
|
|
4712
|
+
noRecentColors: "No hay colores recientes",
|
|
4713
|
+
invalidColorFormat: "El código de color no es un formato HEX válido (Ej: #FF5733 o FFA07A)",
|
|
4714
|
+
colorPreview: "Vista previa del color",
|
|
4715
|
+
};
|
|
4716
|
+
/**
|
|
4717
|
+
* Validates hex color format (#RRGGBB or #RGB)
|
|
4718
|
+
*/
|
|
4719
|
+
const isValidHexColor = (color) => {
|
|
4720
|
+
const hexRegex = /^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/;
|
|
4721
|
+
return hexRegex.test(color);
|
|
4722
|
+
};
|
|
4723
|
+
/**
|
|
4724
|
+
* Normalizes hex color to 6-digit format (#RGB -> #RRGGBB)
|
|
4725
|
+
*/
|
|
4726
|
+
const normalizeHexColor = (color) => {
|
|
4727
|
+
if (!color.startsWith("#")) {
|
|
4728
|
+
color = `#${color}`;
|
|
4729
|
+
}
|
|
4730
|
+
// Convert 3-digit to 6-digit
|
|
4731
|
+
if (color.length === 4) {
|
|
4732
|
+
const r = color[1];
|
|
4733
|
+
const g = color[2];
|
|
4734
|
+
const b = color[3];
|
|
4735
|
+
return `#${r}${r}${g}${g}${b}${b}`.toUpperCase();
|
|
4736
|
+
}
|
|
4737
|
+
return color.toUpperCase();
|
|
4738
|
+
};
|
|
4739
|
+
/**
|
|
4740
|
+
* Strips leading "#" characters so the text input shows only the hex digits.
|
|
4741
|
+
* The "#" prefix is rendered as a fixed startContent in the Input.
|
|
4742
|
+
*/
|
|
4743
|
+
const stripHash = (color) => color.replace(/^#+/, "");
|
|
4744
|
+
/**
|
|
4745
|
+
* ColorPicker - Advanced color selection component
|
|
4746
|
+
*
|
|
4747
|
+
* Features:
|
|
4748
|
+
* - Native color picker input
|
|
4749
|
+
* - Text input for hex codes
|
|
4750
|
+
* - Predefined color palette
|
|
4751
|
+
* - Recent colors with localStorage persistence
|
|
4752
|
+
* - Full i18n support
|
|
4753
|
+
* - Dark mode compatible
|
|
4754
|
+
* - Accessible
|
|
4755
|
+
*
|
|
4756
|
+
* @component
|
|
4757
|
+
* @example
|
|
4758
|
+
* ```tsx
|
|
4759
|
+
* <ColorPicker
|
|
4760
|
+
* value={brandColor}
|
|
4761
|
+
* onChange={setBrandColor}
|
|
4762
|
+
* label="Color de marca"
|
|
4763
|
+
* enableRecentColors
|
|
4764
|
+
* />
|
|
4765
|
+
* ```
|
|
4766
|
+
*/
|
|
4767
|
+
const ColorPicker = forwardRef(({ value, onChange, label, required = false, disabled = false, error = false, errorText, predefinedColors = DEFAULT_PREDEFINED_COLORS, enableRecentColors = true, maxRecentColors = 6, recentColorsStorageKey = "aurora-ui-recent-colors", showPredefinedColors = true, translations = {}, className = "", id, name, }, ref) => {
|
|
4768
|
+
const generatedId = useId();
|
|
4769
|
+
const inputId = id || generatedId;
|
|
4770
|
+
const t = { ...defaultTranslations$e, ...translations };
|
|
4771
|
+
// Local state for text input — holds the hex digits WITHOUT the leading "#".
|
|
4772
|
+
// The "#" is rendered as a fixed startContent prefix.
|
|
4773
|
+
const [textValue, setTextValue] = useState(stripHash(value));
|
|
4774
|
+
const [isInvalidFormat, setIsInvalidFormat] = useState(false);
|
|
4775
|
+
// Recent colors state
|
|
4776
|
+
const [recentColors, setRecentColors] = useState([]);
|
|
4777
|
+
// Load recent colors from localStorage on mount
|
|
4778
|
+
useEffect(() => {
|
|
4779
|
+
if (!enableRecentColors)
|
|
4780
|
+
return;
|
|
4781
|
+
try {
|
|
4782
|
+
const stored = localStorage.getItem(recentColorsStorageKey);
|
|
4783
|
+
if (stored) {
|
|
4784
|
+
const colors = JSON.parse(stored);
|
|
4785
|
+
setRecentColors(colors);
|
|
4786
|
+
}
|
|
4787
|
+
}
|
|
4788
|
+
catch (error) {
|
|
4789
|
+
console.error("Error loading recent colors:", error);
|
|
4790
|
+
}
|
|
4791
|
+
}, [enableRecentColors, recentColorsStorageKey]);
|
|
4792
|
+
// Save to recent colors when value changes
|
|
4793
|
+
useEffect(() => {
|
|
4794
|
+
if (!enableRecentColors || !value || !isValidHexColor(value))
|
|
4795
|
+
return;
|
|
4796
|
+
const normalizedValue = normalizeHexColor(value);
|
|
4797
|
+
// Add to recent colors if not already present
|
|
4798
|
+
setRecentColors((prev) => {
|
|
4799
|
+
// Remove if already exists
|
|
4800
|
+
const filtered = prev.filter((c) => c !== normalizedValue);
|
|
4801
|
+
// Add to beginning
|
|
4802
|
+
const updated = [normalizedValue, ...filtered].slice(0, maxRecentColors);
|
|
4803
|
+
// Save to localStorage
|
|
4804
|
+
try {
|
|
4805
|
+
localStorage.setItem(recentColorsStorageKey, JSON.stringify(updated));
|
|
4806
|
+
}
|
|
4807
|
+
catch (error) {
|
|
4808
|
+
console.error("Error saving recent colors:", error);
|
|
4809
|
+
}
|
|
4810
|
+
return updated;
|
|
4811
|
+
});
|
|
4812
|
+
}, [value, enableRecentColors, maxRecentColors, recentColorsStorageKey]);
|
|
4813
|
+
// Sync text value when prop value changes
|
|
4814
|
+
useEffect(() => {
|
|
4815
|
+
setTextValue(stripHash(value));
|
|
4816
|
+
setIsInvalidFormat(false);
|
|
4817
|
+
}, [value]);
|
|
4818
|
+
/**
|
|
4819
|
+
* Handle color picker (native input) change
|
|
4820
|
+
*/
|
|
4821
|
+
const handleColorPickerChange = (e) => {
|
|
4822
|
+
const newColor = normalizeHexColor(e.target.value);
|
|
4823
|
+
setTextValue(stripHash(newColor));
|
|
4824
|
+
setIsInvalidFormat(false);
|
|
4825
|
+
onChange(newColor);
|
|
4826
|
+
};
|
|
4827
|
+
/**
|
|
4828
|
+
* Handle text input change — passive: no validation, normalization, or
|
|
4829
|
+
* onChange propagation while typing. We only update local state and clear
|
|
4830
|
+
* any stale error so the user can correct an invalid value without the
|
|
4831
|
+
* red border lingering. Validation (and stripping of the leading "#")
|
|
4832
|
+
* runs on blur.
|
|
4833
|
+
*
|
|
4834
|
+
* While typing we let the user see whatever they pressed — including a
|
|
4835
|
+
* leading "#" — so the input feels responsive. To prevent a double "#"
|
|
4836
|
+
* on screen, the fixed prefix in `startContent` is hidden while
|
|
4837
|
+
* `textValue` already starts with "#". The cap is `7` (1 "#" + 6 hex)
|
|
4838
|
+
* when the user opted into a leading "#", otherwise `6`.
|
|
4839
|
+
*/
|
|
4840
|
+
const handleTextChange = (newValue) => {
|
|
4841
|
+
const max = newValue.startsWith("#") ? 7 : 6;
|
|
4842
|
+
setTextValue(newValue.slice(0, max));
|
|
4843
|
+
setIsInvalidFormat(false);
|
|
4844
|
+
};
|
|
4845
|
+
/**
|
|
4846
|
+
* Validate the current hex input. Updates `isInvalidFormat` and, when
|
|
4847
|
+
* the input is a valid hex, normalizes the display and propagates via
|
|
4848
|
+
* `onChange`. Returns whether the input is acceptable so a caller can
|
|
4849
|
+
* decide to block submission.
|
|
4850
|
+
*
|
|
4851
|
+
* Empty + not required → valid (no error, no propagation).
|
|
4852
|
+
* Empty + required → invalid (error).
|
|
4853
|
+
* Valid (3 or 6 hex chars, with or without leading "#") → normalize, sync
|
|
4854
|
+
* display to canonical form, propagate, valid.
|
|
4855
|
+
* Otherwise → invalid (error, no propagation).
|
|
4856
|
+
*/
|
|
4857
|
+
const validate = useCallback(() => {
|
|
4858
|
+
// User may have typed a leading "#" — strip it so it doesn't end up
|
|
4859
|
+
// as "##" once we prepend "#" for the regex check.
|
|
4860
|
+
const trimmed = stripHash(textValue.trim());
|
|
4861
|
+
if (trimmed === "") {
|
|
4862
|
+
const valid = !required;
|
|
4863
|
+
setIsInvalidFormat(!valid);
|
|
4864
|
+
// Drop any residual "#" the user typed but never followed up on,
|
|
4865
|
+
// so the field reads as truly empty after blur.
|
|
4866
|
+
setTextValue("");
|
|
4867
|
+
return valid;
|
|
4868
|
+
}
|
|
4869
|
+
const candidate = `#${trimmed}`;
|
|
4870
|
+
if (!isValidHexColor(candidate)) {
|
|
4871
|
+
setIsInvalidFormat(true);
|
|
4872
|
+
return false;
|
|
4873
|
+
}
|
|
4874
|
+
const normalized = normalizeHexColor(candidate);
|
|
4875
|
+
setIsInvalidFormat(false);
|
|
4876
|
+
setTextValue(stripHash(normalized));
|
|
4877
|
+
onChange(normalized);
|
|
4878
|
+
return true;
|
|
4879
|
+
}, [textValue, required, onChange]);
|
|
4880
|
+
useImperativeHandle(ref, () => ({ validate }), [validate]);
|
|
4881
|
+
const handleTextBlur = () => {
|
|
4882
|
+
validate();
|
|
4883
|
+
};
|
|
4884
|
+
/**
|
|
4885
|
+
* Handle predefined color selection
|
|
4886
|
+
*/
|
|
4887
|
+
const handlePredefinedColorClick = (colorValue) => {
|
|
4888
|
+
if (disabled)
|
|
4889
|
+
return;
|
|
4890
|
+
const normalized = normalizeHexColor(colorValue);
|
|
4891
|
+
setTextValue(stripHash(normalized));
|
|
4892
|
+
setIsInvalidFormat(false);
|
|
4893
|
+
onChange(normalized);
|
|
4894
|
+
};
|
|
4895
|
+
/**
|
|
4896
|
+
* Render a color circle button
|
|
4897
|
+
*/
|
|
4898
|
+
const renderColorCircle = (colorValue, colorName, isSelected) => (jsx("button", { type: "button", disabled: disabled, onClick: () => handlePredefinedColorClick(colorValue), className: `
|
|
4899
|
+
w-8 h-8 rounded-full border-2 cursor-pointer transition-all
|
|
4900
|
+
focus:outline-none focus:ring-2 focus:ring-primary-500 focus:ring-offset-1
|
|
4901
|
+
${disabled ? "opacity-50 cursor-not-allowed" : "hover:scale-110"}
|
|
4902
|
+
${isSelected
|
|
4903
|
+
? "border-primary-500 scale-110 ring-2 ring-primary-500 ring-offset-1"
|
|
4904
|
+
: "border-default-700"}
|
|
4905
|
+
`, style: { backgroundColor: colorValue }, title: colorName, "aria-label": colorName }, colorValue));
|
|
4906
|
+
return (jsxs("div", { className: `flex flex-col gap-3 ${className}`, children: [label && (jsxs("label", { htmlFor: inputId, className: "block text-sm font-medium text-foreground", children: [label, required && jsx("span", { className: "text-danger ml-1", children: "*" })] })), jsxs("div", { className: "flex flex-col gap-1", children: [jsxs("div", { className: "flex items-center gap-2", children: [jsx("input", { type: "color", id: inputId, name: name, value: `#${stripHash(textValue)}`, onChange: handleColorPickerChange, disabled: disabled, className: `
|
|
4907
|
+
w-10 h-10 rounded-lg cursor-pointer border-2 flex-shrink-0
|
|
4908
|
+
transition-colors appearance-none bg-transparent p-0.5
|
|
4909
|
+
focus:border-primary-500
|
|
4910
|
+
${disabled ? "opacity-50 cursor-not-allowed" : "hover:border-default-400"}
|
|
4911
|
+
${error
|
|
4912
|
+
? "border-danger dark:border-danger-400"
|
|
4913
|
+
: "border-default-300 dark:border-default-700"}
|
|
4914
|
+
`, "aria-label": t.customColorLabel, title: t.customColorLabel }), jsx(Input, { type: "text", value: textValue, onValueChange: handleTextChange, onBlur: handleTextBlur, placeholder: t.placeholder, disabled: disabled, isDisabled: disabled, isInvalid: error || isInvalidFormat, size: "sm", variant: "bordered",
|
|
4915
|
+
// `min-w-0` (TECH-2459, beweossmbs QA 24-sep): el campo es un ítem de la fila
|
|
4916
|
+
// flex de la muestra de color y tiene que poder encoger en huecos estrechos
|
|
4917
|
+
// (Agenda → Colors: 180px). El `w-full min-w-0` del `<input>` en la fachada
|
|
4918
|
+
// ya lo arregla; esto es el suelo explícito en la raíz del campo.
|
|
4919
|
+
className: "min-w-0", classNames: {
|
|
4920
|
+
input: "uppercase font-mono text-sm",
|
|
4921
|
+
inputWrapper: "min-h-8 h-8 focus-within:!border-primary-500",
|
|
4922
|
+
}, startContent: textValue.startsWith("#") ? null : (jsx("span", { className: "text-default-400 text-sm", children: "#" })) })] }), (error || isInvalidFormat) && (jsx("p", { className: "text-xs text-danger", children: isInvalidFormat ? t.invalidColorFormat : errorText }))] }), showPredefinedColors && predefinedColors.length > 0 && (jsxs("div", { children: [jsx("p", { className: "text-xs font-medium text-default-600 mb-2", children: t.predefinedColorsLabel }), jsx("div", { className: "flex flex-wrap gap-2", children: predefinedColors.map((color) => renderColorCircle(color.value, color.name, normalizeHexColor(value) === normalizeHexColor(color.value))) })] })), enableRecentColors && recentColors.length > 0 && (jsxs("div", { children: [jsx("p", { className: "text-xs font-medium text-default-600 mb-2", children: t.recentColorsLabel }), jsx("div", { className: "flex flex-wrap gap-2", children: recentColors.map((color) => renderColorCircle(color, color, normalizeHexColor(value) === color)) })] }))] }));
|
|
4923
|
+
});
|
|
4924
|
+
ColorPicker.displayName = "ColorPicker";
|
|
4925
|
+
|
|
4926
|
+
const ThemeContext = createContext({
|
|
4927
|
+
mode: "light",
|
|
4928
|
+
color: "blue",
|
|
4929
|
+
customHex: null,
|
|
4930
|
+
// biome-ignore lint/suspicious/noEmptyBlockStatements: <explanation>
|
|
4931
|
+
setMode: () => { },
|
|
4932
|
+
// biome-ignore lint/suspicious/noEmptyBlockStatements: <explanation>
|
|
4933
|
+
setColor: () => { },
|
|
4934
|
+
});
|
|
4935
|
+
const useThemeContext = () => {
|
|
4936
|
+
const context = useContext(ThemeContext);
|
|
4937
|
+
if (!context) {
|
|
4938
|
+
throw new Error("useThemeContext debe ser usado dentro de un ThemeProvider");
|
|
4939
|
+
}
|
|
4940
|
+
return context;
|
|
4941
|
+
};
|
|
4942
|
+
|
|
4768
4943
|
/**
|
|
4769
4944
|
* @file This file contains the theme configurations for the application.
|
|
4770
4945
|
*/
|
|
@@ -6766,6 +6941,535 @@ const LindaIcon = ({ size = 24, className, ...nativeProps }) => {
|
|
|
6766
6941
|
return (jsx("svg", { xmlns: "http://www.w3.org/2000/svg", width: size, height: size, viewBox: "0 0 24 24", className: className, role: "img", "aria-hidden": "true", ...nativeProps, children: jsxs("g", { fill: "currentColor", children: [jsx("path", { d: "M3.84453 3.84453C2.71849 4.97056 2.71849 6.79623 3.84453 7.92226L5.43227 9.51C5.44419 9.49622 5.45669 9.48276 5.46978 9.46967L9.46978 5.46967C9.48284 5.45662 9.49625 5.44415 9.50999 5.43226L7.92226 3.84453C6.79623 2.71849 4.97056 2.71849 3.84453 3.84453Z" }), jsx("path", { d: "M10.5679 6.49012C10.556 6.50386 10.5435 6.51728 10.5304 6.53033L6.53044 10.5303C6.51735 10.5434 6.5039 10.5559 6.49011 10.5678L16.0777 20.1555C17.2038 21.2815 19.0294 21.2815 20.1555 20.1555C21.2815 19.0294 21.2815 17.2038 20.1555 16.0777L10.5679 6.49012Z" }), jsx("path", { d: "M16.1 2.30719C16.261 1.8976 16.8385 1.8976 16.9994 2.30719L17.4298 3.40247C17.479 3.52752 17.5776 3.62651 17.7022 3.67583L18.7934 4.1078C19.2015 4.26934 19.2015 4.849 18.7934 5.01054L17.7022 5.44252C17.5776 5.49184 17.479 5.59082 17.4298 5.71587L16.9995 6.81115C16.8385 7.22074 16.261 7.22074 16.1 6.81116L15.6697 5.71587C15.6205 5.59082 15.5219 5.49184 15.3973 5.44252L14.3061 5.01054C13.898 4.849 13.898 4.26934 14.3061 4.1078L15.3973 3.67583C15.5219 3.62651 15.6205 3.52752 15.6697 3.40247L16.1 2.30719Z" }), jsx("path", { d: "M19.9672 9.12945C20.1281 8.71987 20.7057 8.71987 20.8666 9.12945L21.0235 9.5288C21.0727 9.65385 21.1713 9.75284 21.2959 9.80215L21.6937 9.95965C22.1018 10.1212 22.1018 10.7009 21.6937 10.8624L21.2959 11.0199C21.1713 11.0692 21.0727 11.1682 21.0235 11.2932L20.8666 11.6926C20.7057 12.1022 20.1281 12.1022 19.9672 11.6926L19.8103 11.2932C19.7611 11.1682 19.6625 11.0692 19.5379 11.0199L19.14 10.8624C18.732 10.7009 18.732 10.1212 19.14 9.95965L19.5379 9.80215C19.6625 9.75284 19.7611 9.65385 19.8103 9.5288L19.9672 9.12945Z" }), jsx("path", { d: "M5.1332 15.3072C5.29414 14.8976 5.87167 14.8976 6.03261 15.3072L6.18953 15.7065C6.23867 15.8316 6.33729 15.9306 6.46188 15.9799L6.85975 16.1374C7.26783 16.2989 7.26783 16.8786 6.85975 17.0401L6.46188 17.1976C6.33729 17.2469 6.23867 17.3459 6.18953 17.471L6.03261 17.8703C5.87167 18.2799 5.29414 18.2799 5.1332 17.8703L4.97628 17.471C4.92714 17.3459 4.82852 17.2469 4.70393 17.1976L4.30606 17.0401C3.89798 16.8786 3.89798 16.2989 4.30606 16.1374L4.70393 15.9799C4.82852 15.9306 4.92714 15.8316 4.97628 15.7065L5.1332 15.3072Z" })] }) }));
|
|
6767
6942
|
};
|
|
6768
6943
|
|
|
6944
|
+
const LINDA_MASCOT_STATES = [
|
|
6945
|
+
"idle",
|
|
6946
|
+
"processing",
|
|
6947
|
+
"working",
|
|
6948
|
+
"listening",
|
|
6949
|
+
"talking",
|
|
6950
|
+
"ready",
|
|
6951
|
+
];
|
|
6952
|
+
/** px de cada talla con nombre. Cada una cae en su propio nivel de detalle. */
|
|
6953
|
+
const LINDA_MASCOT_SIZE_PX = {
|
|
6954
|
+
xs: 20,
|
|
6955
|
+
sm: 32,
|
|
6956
|
+
md: 72,
|
|
6957
|
+
lg: 144,
|
|
6958
|
+
};
|
|
6959
|
+
/** Variable CSS que lleva la intensidad de voz (0–1). Parte del contrato público. */
|
|
6960
|
+
const LINDA_MASCOT_LEVEL_VAR = "--linda-mascot-level";
|
|
6961
|
+
/** id de la `<style>` que el componente deja en `<head>`. */
|
|
6962
|
+
const LINDA_MASCOT_STYLE_ID = "aurora-linda-mascot-styles";
|
|
6963
|
+
/** Paleta de la hoja de marca. */
|
|
6964
|
+
const LINDA_MASCOT_PALETTE = {
|
|
6965
|
+
coreLight: "#2A2D4A",
|
|
6966
|
+
coreDark: "#0B0C18",
|
|
6967
|
+
eye: "#EAF2FF",
|
|
6968
|
+
eyeGlow: "#8FB8FF",
|
|
6969
|
+
pink: "#FF8AD8",
|
|
6970
|
+
violet: "#A48BFF",
|
|
6971
|
+
cyan: "#6FE3FF",
|
|
6972
|
+
amber: "#FFD08A",
|
|
6973
|
+
};
|
|
6974
|
+
const resolveMascotPx = (size) => {
|
|
6975
|
+
if (typeof size === "number") {
|
|
6976
|
+
return Number.isFinite(size) && size > 0 ? size : LINDA_MASCOT_SIZE_PX.md;
|
|
6977
|
+
}
|
|
6978
|
+
return LINDA_MASCOT_SIZE_PX[size ?? "md"] ?? LINDA_MASCOT_SIZE_PX.md;
|
|
6979
|
+
};
|
|
6980
|
+
/** ≤20 xs · 21–40 sm · 41–95 md · ≥96 lg. */
|
|
6981
|
+
const resolveMascotTier = (px) => {
|
|
6982
|
+
if (px <= 20) {
|
|
6983
|
+
return "xs";
|
|
6984
|
+
}
|
|
6985
|
+
if (px <= 40) {
|
|
6986
|
+
return "sm";
|
|
6987
|
+
}
|
|
6988
|
+
if (px < 96) {
|
|
6989
|
+
return "md";
|
|
6990
|
+
}
|
|
6991
|
+
return "lg";
|
|
6992
|
+
};
|
|
6993
|
+
const clampLevel = (level) => {
|
|
6994
|
+
if (typeof level !== "number" || Number.isNaN(level)) {
|
|
6995
|
+
return 0;
|
|
6996
|
+
}
|
|
6997
|
+
return Math.min(1, Math.max(0, level));
|
|
6998
|
+
};
|
|
6999
|
+
const LINDA_MASCOT_GEOMETRY = {
|
|
7000
|
+
xs: {
|
|
7001
|
+
coreRx: 40,
|
|
7002
|
+
coreCy: 61,
|
|
7003
|
+
eyeWidth: 17,
|
|
7004
|
+
eyeHeight: 8,
|
|
7005
|
+
eyeStroke: 9,
|
|
7006
|
+
eyeGap: 31,
|
|
7007
|
+
rings: [{ r: 57, squash: 0.3, tilt: -16, strokePx: 1.2, opacity: 0.95 }],
|
|
7008
|
+
halo: false,
|
|
7009
|
+
specular: false,
|
|
7010
|
+
blur: false,
|
|
7011
|
+
ringGlow: "none",
|
|
7012
|
+
},
|
|
7013
|
+
sm: {
|
|
7014
|
+
coreRx: 36,
|
|
7015
|
+
coreCy: 61,
|
|
7016
|
+
eyeWidth: 14,
|
|
7017
|
+
eyeHeight: 6.5,
|
|
7018
|
+
eyeStroke: 6,
|
|
7019
|
+
eyeGap: 28,
|
|
7020
|
+
rings: [
|
|
7021
|
+
{ r: 56, squash: 0.3, tilt: -16, strokePx: 1.1, opacity: 0.9 },
|
|
7022
|
+
{ r: 52, squash: 0.36, tilt: 20, strokePx: 1, opacity: 0.7 },
|
|
7023
|
+
],
|
|
7024
|
+
halo: true,
|
|
7025
|
+
specular: false,
|
|
7026
|
+
blur: false,
|
|
7027
|
+
ringGlow: "none",
|
|
7028
|
+
},
|
|
7029
|
+
md: {
|
|
7030
|
+
coreRx: 33,
|
|
7031
|
+
coreCy: 61,
|
|
7032
|
+
eyeWidth: 12.5,
|
|
7033
|
+
eyeHeight: 5.5,
|
|
7034
|
+
eyeStroke: 4.4,
|
|
7035
|
+
eyeGap: 25,
|
|
7036
|
+
rings: [
|
|
7037
|
+
{ r: 56, squash: 0.3, tilt: -16, strokePx: 1.5, opacity: 0.85 },
|
|
7038
|
+
{ r: 52, squash: 0.38, tilt: 20, strokePx: 1.2, opacity: 0.7 },
|
|
7039
|
+
{ r: 58, squash: 0.2, tilt: -3, strokePx: 1, opacity: 0.55 },
|
|
7040
|
+
],
|
|
7041
|
+
halo: true,
|
|
7042
|
+
specular: true,
|
|
7043
|
+
blur: true,
|
|
7044
|
+
ringGlow: "flat",
|
|
7045
|
+
},
|
|
7046
|
+
lg: {
|
|
7047
|
+
coreRx: 32,
|
|
7048
|
+
coreCy: 61,
|
|
7049
|
+
eyeWidth: 12,
|
|
7050
|
+
eyeHeight: 5.2,
|
|
7051
|
+
eyeStroke: 3.8,
|
|
7052
|
+
eyeGap: 24,
|
|
7053
|
+
rings: [
|
|
7054
|
+
{ r: 56, squash: 0.3, tilt: -16, strokePx: 2, opacity: 0.85 },
|
|
7055
|
+
{ r: 52, squash: 0.38, tilt: 20, strokePx: 1.6, opacity: 0.72 },
|
|
7056
|
+
{ r: 58, squash: 0.2, tilt: -3, strokePx: 1.3, opacity: 0.58 },
|
|
7057
|
+
],
|
|
7058
|
+
halo: true,
|
|
7059
|
+
specular: true,
|
|
7060
|
+
blur: true,
|
|
7061
|
+
ringGlow: "blur",
|
|
7062
|
+
},
|
|
7063
|
+
};
|
|
7064
|
+
|
|
7065
|
+
const LEVEL = `var(${LINDA_MASCOT_LEVEL_VAR})`;
|
|
7066
|
+
const AUTO_THEME = themeScopeSelectors('.aurora-linda-mascot[data-appearance="auto"]');
|
|
7067
|
+
/** Curva de los movimientos que responden a la voz: sale rápido, llega suave. */
|
|
7068
|
+
const EASE_OUT = "cubic-bezier(0.22, 0.7, 0.3, 1)";
|
|
7069
|
+
/**
|
|
7070
|
+
* Reglas de la mascota. Viajan con el componente (se inyectan en `<head>`, ver
|
|
7071
|
+
* `useInjectedStyle`) para que funcione en un microfrontend que no carga `styles.css`.
|
|
7072
|
+
*
|
|
7073
|
+
* Todo se anima con `transform` u `opacity`. Cada grupo animado es un `<g>` SIN atributo
|
|
7074
|
+
* `transform`: en SVG la propiedad CSS `transform` reemplaza al atributo, así que la
|
|
7075
|
+
* geometría (posición, inclinación, aplastamiento) vive en grupos padres y el movimiento
|
|
7076
|
+
* en grupos hijos. Por la misma razón, lo que sigue a la voz (`--linda-mascot-level`,
|
|
7077
|
+
* con `transition`) y lo que late en bucle (con `animation`) van en grupos distintos: una
|
|
7078
|
+
* animación CSS anula la transición de la misma propiedad.
|
|
7079
|
+
*/
|
|
7080
|
+
/** Reglas de movimiento reducido bajo la raíz \`root\` (clase o clase+atributo). */
|
|
7081
|
+
const reducedMotionBody = (root) => `
|
|
7082
|
+
${root} .aurora-lm-spin,
|
|
7083
|
+
${root} .aurora-lm-boost,
|
|
7084
|
+
${root} .aurora-lm-orbit,
|
|
7085
|
+
${root} .aurora-lm-eyes,
|
|
7086
|
+
${root} .aurora-lm-breathe,
|
|
7087
|
+
${root} .aurora-lm-halo,
|
|
7088
|
+
${root} .aurora-lm-sparkle {
|
|
7089
|
+
animation: none !important;
|
|
7090
|
+
}
|
|
7091
|
+
${root} .aurora-lm-level-body,
|
|
7092
|
+
${root} .aurora-lm-level-rings,
|
|
7093
|
+
${root} .aurora-lm-level-halo {
|
|
7094
|
+
transform: none !important;
|
|
7095
|
+
}
|
|
7096
|
+
${root}.aurora-linda-mascot--listening .aurora-lm-level-rings,
|
|
7097
|
+
${root}.aurora-linda-mascot--talking .aurora-lm-level-rings {
|
|
7098
|
+
opacity: calc(0.55 + ${LEVEL} * 0.45);
|
|
7099
|
+
}
|
|
7100
|
+
${root}.aurora-linda-mascot--processing .aurora-lm-halo {
|
|
7101
|
+
animation: aurora-lm-glow 1.6s ease-in-out infinite !important;
|
|
7102
|
+
}
|
|
7103
|
+
${root}.aurora-linda-mascot--working .aurora-lm-halo {
|
|
7104
|
+
opacity: 1 !important;
|
|
7105
|
+
}
|
|
7106
|
+
${root}.aurora-linda-mascot--listening .aurora-lm-level-halo,
|
|
7107
|
+
${root}.aurora-linda-mascot--talking .aurora-lm-level-halo {
|
|
7108
|
+
opacity: calc(0.4 + ${LEVEL} * 0.6) !important;
|
|
7109
|
+
}
|
|
7110
|
+
${root}.aurora-linda-mascot--ready .aurora-lm-sparkle {
|
|
7111
|
+
animation: aurora-lm-sparkle-fade 700ms ease-out 1 both !important;
|
|
7112
|
+
}
|
|
7113
|
+
`;
|
|
7114
|
+
const reducedMotionRules = (root) => `
|
|
7115
|
+
@media (prefers-reduced-motion: reduce) {${reducedMotionBody(root)}}
|
|
7116
|
+
${reducedMotionBody(`${root}[data-motion="reduce"]`)}
|
|
7117
|
+
`;
|
|
7118
|
+
const LINDA_MASCOT_CSS = `
|
|
7119
|
+
.aurora-linda-mascot {
|
|
7120
|
+
${LINDA_MASCOT_LEVEL_VAR}: 0;
|
|
7121
|
+
--lm-halo: 0.5;
|
|
7122
|
+
--lm-shadow: 1;
|
|
7123
|
+
--lm-rim: 0.85;
|
|
7124
|
+
--lm-sparkle: #F2B441;
|
|
7125
|
+
display: inline-block;
|
|
7126
|
+
flex-shrink: 0;
|
|
7127
|
+
overflow: visible;
|
|
7128
|
+
vertical-align: middle;
|
|
7129
|
+
}
|
|
7130
|
+
/* En lg la mascota es escenario (bienvenida, llamada): más halo, como en la hoja de marca.
|
|
7131
|
+
Multiplicador aparte para no competir en especificidad con las reglas de tema. */
|
|
7132
|
+
.aurora-linda-mascot--tier-lg {
|
|
7133
|
+
--lm-halo-boost: 1.4;
|
|
7134
|
+
}
|
|
7135
|
+
/*
|
|
7136
|
+
* Tema. \`auto\` sigue al ancestro claro/oscuro más cercano, incluidos los temas de aurora
|
|
7137
|
+
* (\`blue-dark\`…); ver \`themeScopeSelectors\`.
|
|
7138
|
+
*/
|
|
7139
|
+
${AUTO_THEME.dark},
|
|
7140
|
+
.aurora-linda-mascot[data-appearance="dark"] {
|
|
7141
|
+
--lm-halo: 0.95;
|
|
7142
|
+
--lm-shadow: 0;
|
|
7143
|
+
--lm-rim: 1;
|
|
7144
|
+
--lm-sparkle: #FFF1C9;
|
|
7145
|
+
}
|
|
7146
|
+
${AUTO_THEME.light} {
|
|
7147
|
+
--lm-halo: 0.5;
|
|
7148
|
+
--lm-shadow: 1;
|
|
7149
|
+
--lm-rim: 0.85;
|
|
7150
|
+
--lm-sparkle: #F2B441;
|
|
7151
|
+
}
|
|
7152
|
+
|
|
7153
|
+
/* Cuerpo entero: respiración en bucle + escala por voz (grupos distintos). */
|
|
7154
|
+
.aurora-lm-breathe,
|
|
7155
|
+
.aurora-lm-level-body,
|
|
7156
|
+
.aurora-lm-level-rings {
|
|
7157
|
+
transform-box: view-box;
|
|
7158
|
+
transform-origin: 60px 61px;
|
|
7159
|
+
}
|
|
7160
|
+
.aurora-lm-breathe {
|
|
7161
|
+
animation: aurora-lm-breathe 2s ease-in-out infinite alternate;
|
|
7162
|
+
}
|
|
7163
|
+
.aurora-lm-level-body,
|
|
7164
|
+
.aurora-lm-level-rings,
|
|
7165
|
+
.aurora-lm-level-halo {
|
|
7166
|
+
transition: transform 140ms ${EASE_OUT}, opacity 240ms ${EASE_OUT};
|
|
7167
|
+
}
|
|
7168
|
+
|
|
7169
|
+
/* Halo y sombra de contacto. */
|
|
7170
|
+
.aurora-lm-halo,
|
|
7171
|
+
.aurora-lm-level-halo {
|
|
7172
|
+
transform-box: fill-box;
|
|
7173
|
+
transform-origin: center;
|
|
7174
|
+
}
|
|
7175
|
+
.aurora-lm-halo {
|
|
7176
|
+
opacity: calc(var(--lm-halo) * var(--lm-halo-boost, 1));
|
|
7177
|
+
transition: opacity 400ms ease;
|
|
7178
|
+
}
|
|
7179
|
+
.aurora-lm-shadow {
|
|
7180
|
+
opacity: var(--lm-shadow);
|
|
7181
|
+
}
|
|
7182
|
+
.aurora-lm-rim {
|
|
7183
|
+
opacity: var(--lm-rim);
|
|
7184
|
+
}
|
|
7185
|
+
|
|
7186
|
+
/*
|
|
7187
|
+
* Anillos. El círculo gira dentro de su grupo aplastado: la elipse no cambia de forma y
|
|
7188
|
+
* lo que recorre el trazo es el color, como un anillo que gira en profundidad.
|
|
7189
|
+
* \`--lm-spin-duration\` y \`--lm-spin-direction\` llegan por anillo desde el marcado.
|
|
7190
|
+
*/
|
|
7191
|
+
.aurora-lm-spin {
|
|
7192
|
+
transform-box: view-box;
|
|
7193
|
+
transform-origin: 0 0;
|
|
7194
|
+
animation: aurora-lm-spin var(--lm-spin-duration, 24s) linear infinite
|
|
7195
|
+
var(--lm-spin-direction, normal);
|
|
7196
|
+
}
|
|
7197
|
+
/* Segundo giro encadenado: parado salvo en working, donde dobla la velocidad sin saltos
|
|
7198
|
+
(pausar conserva el ángulo; cambiar la duración lo haría saltar). */
|
|
7199
|
+
.aurora-lm-boost {
|
|
7200
|
+
animation-play-state: paused;
|
|
7201
|
+
}
|
|
7202
|
+
/* Cometa del anillo principal: orbita siempre, se ve solo al pensar o trabajar. */
|
|
7203
|
+
.aurora-lm-orbit {
|
|
7204
|
+
animation-play-state: paused;
|
|
7205
|
+
}
|
|
7206
|
+
.aurora-lm-comet,
|
|
7207
|
+
.aurora-lm-glint {
|
|
7208
|
+
opacity: 0;
|
|
7209
|
+
transition: opacity 320ms ease;
|
|
7210
|
+
}
|
|
7211
|
+
|
|
7212
|
+
/* Ojos: solo parpadean (sin expresiones faciales). Dos parpadeos por ciclo, a 4,8 y 6,2 s. */
|
|
7213
|
+
.aurora-lm-eyes {
|
|
7214
|
+
transform-box: fill-box;
|
|
7215
|
+
transform-origin: center;
|
|
7216
|
+
animation: aurora-lm-blink 11s ease-in-out infinite;
|
|
7217
|
+
}
|
|
7218
|
+
|
|
7219
|
+
/* Chispas de fin de turno. */
|
|
7220
|
+
/* Dorado en claro (el blanco desaparece sobre fondo claro), casi blanco en oscuro. */
|
|
7221
|
+
.aurora-lm-sparkle {
|
|
7222
|
+
transform-box: fill-box;
|
|
7223
|
+
transform-origin: center;
|
|
7224
|
+
opacity: 0;
|
|
7225
|
+
fill: var(--lm-sparkle);
|
|
7226
|
+
}
|
|
7227
|
+
|
|
7228
|
+
/* ── Estados ─────────────────────────────────────────────────────────────── */
|
|
7229
|
+
|
|
7230
|
+
/* idle no añade nada: es el movimiento base de arriba (giro, respiración, parpadeo). */
|
|
7231
|
+
|
|
7232
|
+
.aurora-linda-mascot--processing .aurora-lm-orbit {
|
|
7233
|
+
animation-play-state: running;
|
|
7234
|
+
}
|
|
7235
|
+
.aurora-linda-mascot--processing .aurora-lm-comet {
|
|
7236
|
+
opacity: 1;
|
|
7237
|
+
}
|
|
7238
|
+
.aurora-linda-mascot--processing .aurora-lm-halo {
|
|
7239
|
+
animation: aurora-lm-pulse 1.6s ease-in-out infinite;
|
|
7240
|
+
}
|
|
7241
|
+
|
|
7242
|
+
.aurora-linda-mascot--working .aurora-lm-boost,
|
|
7243
|
+
.aurora-linda-mascot--working .aurora-lm-orbit {
|
|
7244
|
+
animation-play-state: running;
|
|
7245
|
+
}
|
|
7246
|
+
.aurora-linda-mascot--working .aurora-lm-glint {
|
|
7247
|
+
opacity: 1;
|
|
7248
|
+
}
|
|
7249
|
+
|
|
7250
|
+
.aurora-linda-mascot--listening .aurora-lm-level-rings {
|
|
7251
|
+
transform: scale(calc(0.86 + ${LEVEL} * 0.16));
|
|
7252
|
+
}
|
|
7253
|
+
.aurora-linda-mascot--listening .aurora-lm-level-halo {
|
|
7254
|
+
transform: scale(calc(0.92 + ${LEVEL} * 0.2));
|
|
7255
|
+
}
|
|
7256
|
+
|
|
7257
|
+
.aurora-linda-mascot--talking .aurora-lm-level-body {
|
|
7258
|
+
transform: scale(calc(1 + ${LEVEL} * 0.07));
|
|
7259
|
+
}
|
|
7260
|
+
.aurora-linda-mascot--talking .aurora-lm-level-halo {
|
|
7261
|
+
transform: scale(calc(1 + ${LEVEL} * 0.32));
|
|
7262
|
+
opacity: calc(0.75 + ${LEVEL} * 0.25);
|
|
7263
|
+
}
|
|
7264
|
+
|
|
7265
|
+
.aurora-linda-mascot--ready .aurora-lm-sparkle {
|
|
7266
|
+
animation: aurora-lm-sparkle 700ms cubic-bezier(0.2, 0.8, 0.3, 1) 1 both;
|
|
7267
|
+
animation-delay: var(--lm-sparkle-delay, 0ms);
|
|
7268
|
+
}
|
|
7269
|
+
|
|
7270
|
+
/* ── Keyframes ───────────────────────────────────────────────────────────── */
|
|
7271
|
+
|
|
7272
|
+
@keyframes aurora-lm-spin {
|
|
7273
|
+
to {
|
|
7274
|
+
transform: rotate(360deg);
|
|
7275
|
+
}
|
|
7276
|
+
}
|
|
7277
|
+
@keyframes aurora-lm-breathe {
|
|
7278
|
+
from {
|
|
7279
|
+
transform: scale(1);
|
|
7280
|
+
}
|
|
7281
|
+
to {
|
|
7282
|
+
transform: scale(1.03);
|
|
7283
|
+
}
|
|
7284
|
+
}
|
|
7285
|
+
@keyframes aurora-lm-blink {
|
|
7286
|
+
0%, 42%, 45%, 97.5%, 100% {
|
|
7287
|
+
transform: scaleY(1);
|
|
7288
|
+
}
|
|
7289
|
+
43.5%, 98.8% {
|
|
7290
|
+
transform: scaleY(0.12);
|
|
7291
|
+
}
|
|
7292
|
+
}
|
|
7293
|
+
@keyframes aurora-lm-pulse {
|
|
7294
|
+
0%, 100% {
|
|
7295
|
+
transform: scale(1);
|
|
7296
|
+
opacity: calc(var(--lm-halo) * var(--lm-halo-boost, 1));
|
|
7297
|
+
}
|
|
7298
|
+
50% {
|
|
7299
|
+
transform: scale(1.12);
|
|
7300
|
+
opacity: 1;
|
|
7301
|
+
}
|
|
7302
|
+
}
|
|
7303
|
+
@keyframes aurora-lm-glow {
|
|
7304
|
+
0%, 100% {
|
|
7305
|
+
opacity: calc(var(--lm-halo) * var(--lm-halo-boost, 1));
|
|
7306
|
+
}
|
|
7307
|
+
50% {
|
|
7308
|
+
opacity: 1;
|
|
7309
|
+
}
|
|
7310
|
+
}
|
|
7311
|
+
@keyframes aurora-lm-sparkle {
|
|
7312
|
+
0% {
|
|
7313
|
+
opacity: 0;
|
|
7314
|
+
transform: scale(0.2) rotate(0deg);
|
|
7315
|
+
}
|
|
7316
|
+
35% {
|
|
7317
|
+
opacity: 1;
|
|
7318
|
+
transform: scale(1) rotate(45deg);
|
|
7319
|
+
}
|
|
7320
|
+
100% {
|
|
7321
|
+
opacity: 0;
|
|
7322
|
+
transform: scale(0.5) rotate(90deg);
|
|
7323
|
+
}
|
|
7324
|
+
}
|
|
7325
|
+
@keyframes aurora-lm-sparkle-fade {
|
|
7326
|
+
0%, 100% {
|
|
7327
|
+
opacity: 0;
|
|
7328
|
+
}
|
|
7329
|
+
35% {
|
|
7330
|
+
opacity: 1;
|
|
7331
|
+
}
|
|
7332
|
+
}
|
|
7333
|
+
|
|
7334
|
+
/*
|
|
7335
|
+
* Movimiento reducido: nada gira, parpadea, respira ni crece. El estado se sigue leyendo
|
|
7336
|
+
* sin movimiento: al pensar o trabajar el cometa o el destello se quedan quietos sobre el
|
|
7337
|
+
* anillo (así se distingue también en \`xs\`, que no tiene halo) y el halo late en opacidad
|
|
7338
|
+
* o se enciende; con la voz sube la opacidad del halo y de los anillos; las chispas
|
|
7339
|
+
* aparecen y se desvanecen sin moverse.
|
|
7340
|
+
*
|
|
7341
|
+
* Las mismas reglas se aplican con la preferencia del sistema y con \`motion="reduce"\`
|
|
7342
|
+
* (\`data-motion\`), que el consumidor usa para listas largas o para forzarlo. \`!important\`
|
|
7343
|
+
* porque las reglas por estado de arriba tienen más especificidad y \`@media\` no la sube.
|
|
7344
|
+
*/
|
|
7345
|
+
${reducedMotionRules(".aurora-linda-mascot")}
|
|
7346
|
+
`;
|
|
7347
|
+
|
|
7348
|
+
const CX = 60;
|
|
7349
|
+
const STYLE_ID = styleIdFor(LINDA_MASCOT_STYLE_ID, LINDA_MASCOT_CSS);
|
|
7350
|
+
/** Velocidad de giro por anillo (s por vuelta) y sentido. */
|
|
7351
|
+
const RING_MOTION = [
|
|
7352
|
+
{ duration: "24s", direction: "normal" },
|
|
7353
|
+
{ duration: "30s", direction: "reverse" },
|
|
7354
|
+
{ duration: "36s", direction: "normal" },
|
|
7355
|
+
];
|
|
7356
|
+
/** Chispas de `ready`: posición en el viewBox, tamaño y retardo. */
|
|
7357
|
+
const SPARKLES = [
|
|
7358
|
+
{ x: 90, y: 17, s: 9, delay: "0ms" },
|
|
7359
|
+
{ x: 104, y: 35, s: 5.5, delay: "90ms" },
|
|
7360
|
+
{ x: 73, y: 7, s: 4.5, delay: "170ms" },
|
|
7361
|
+
];
|
|
7362
|
+
const sparklePath = (s) => {
|
|
7363
|
+
const k = s * 0.18;
|
|
7364
|
+
return `M0 ${-s}C${k} ${-k} ${k} ${-k} ${s} 0C${k} ${k} ${k} ${k} 0 ${s}C${-k} ${k} ${-k} ${k} ${-s} 0C${-k} ${-k} ${-k} ${-k} 0 ${-s}Z`;
|
|
7365
|
+
};
|
|
7366
|
+
/**
|
|
7367
|
+
* Perímetro en px de pantalla de la elipse de un anillo. Con `non-scaling-stroke` los
|
|
7368
|
+
* guiones se miden en px de pantalla (no en unidades del viewBox ni en `pathLength`), así
|
|
7369
|
+
* que el cometa se dimensiona como fracción de este perímetro para medir lo mismo a
|
|
7370
|
+
* cualquier talla.
|
|
7371
|
+
*/
|
|
7372
|
+
const ringPerimeterPx = (ring, px) => {
|
|
7373
|
+
const a = (ring.r * px) / 120;
|
|
7374
|
+
const b = a * ring.squash;
|
|
7375
|
+
const h = ((a - b) / (a + b)) ** 2;
|
|
7376
|
+
return Math.PI * (a + b) * (1 + (3 * h) / (10 + Math.sqrt(4 - 3 * h)));
|
|
7377
|
+
};
|
|
7378
|
+
/**
|
|
7379
|
+
* Cometa con estela sobre un anillo: cola de `steps` tramos consecutivos con el
|
|
7380
|
+
* iridiscente del anillo, cada uno más opaco y más grueso que el anterior, y una cabeza
|
|
7381
|
+
* redonda y brillante al final. Todo es `stroke-dasharray` sobre el mismo círculo que el
|
|
7382
|
+
* anillo, así que gira con `transform` y se recorta delante/detrás igual que él. El giro
|
|
7383
|
+
* es horario: la cabeza va al final del trazo, por delante de su cola.
|
|
7384
|
+
*/
|
|
7385
|
+
const Comet = ({ className, r, perimeter, tail, strokePx, headPx, steps, ringPaint, headPaint, }) => {
|
|
7386
|
+
const tailPx = perimeter * tail;
|
|
7387
|
+
const seg = tailPx / steps;
|
|
7388
|
+
return (jsxs("g", { className: className, "data-part": "comet", children: [Array.from({ length: steps }, (_, i) => {
|
|
7389
|
+
const t = (i + 1) / steps;
|
|
7390
|
+
return (jsx("circle", { r: r, fill: "none", stroke: ringPaint, strokeWidth: strokePx * (0.8 + 1.6 * t), strokeOpacity: t ** 1.5, strokeDasharray: `${(seg + 0.4).toFixed(2)} ${(perimeter - seg - 0.4).toFixed(2)}`, strokeDashoffset: (-seg * i).toFixed(2), vectorEffect: "non-scaling-stroke" }, i));
|
|
7391
|
+
}), jsx("circle", { r: r, fill: "none", stroke: headPaint, strokeWidth: headPx * 1.5, strokeOpacity: 0.3, strokeLinecap: "round", strokeDasharray: `0.01 ${(perimeter - 0.01).toFixed(2)}`, strokeDashoffset: (-tailPx).toFixed(2), vectorEffect: "non-scaling-stroke" }), jsx("circle", { r: r, fill: "none", stroke: "#FFFFFF", strokeWidth: headPx, strokeLinecap: "round", strokeDasharray: `0.01 ${(perimeter - 0.01).toFixed(2)}`, strokeDashoffset: (-tailPx).toFixed(2), vectorEffect: "non-scaling-stroke" })] }));
|
|
7392
|
+
};
|
|
7393
|
+
/** Arco «∩» de un ojo centrado en (x, y): ancho w, alto h. */
|
|
7394
|
+
const eyePath = (x, y, w, h) => {
|
|
7395
|
+
const half = w / 2;
|
|
7396
|
+
const base = y + h / 2;
|
|
7397
|
+
const control = y - h * 0.8333;
|
|
7398
|
+
return `M${x - half} ${base}C${x - half} ${control} ${x + half} ${control} ${x + half} ${base}`;
|
|
7399
|
+
};
|
|
7400
|
+
/**
|
|
7401
|
+
* `useId` devuelve `:r0:` (React 18), `«r0»` o `_r_0_` (19): fuera lo que no vale en un
|
|
7402
|
+
* id referenciado desde `url(#…)`.
|
|
7403
|
+
*/
|
|
7404
|
+
const safeId = (raw) => `lm${raw.replace(/[^a-zA-Z0-9_-]/g, "")}`;
|
|
7405
|
+
/**
|
|
7406
|
+
* LindaMascot — la cara de Linda: un orbe de tinta con ojos luminosos y anillos
|
|
7407
|
+
* iridiscentes, en SVG + CSS (sin imágenes, fuentes ni WebGL).
|
|
7408
|
+
*
|
|
7409
|
+
* Se anima según lo que Linda está haciendo (`state`) y, en la llamada de voz, según el
|
|
7410
|
+
* volumen (`level`), sin preguntar nada al modelo. Pinta más o menos detalle según la
|
|
7411
|
+
* talla para leerse igual a 16 px que a 200 px. Ver README.
|
|
7412
|
+
*/
|
|
7413
|
+
const LindaMascot = forwardRef(function LindaMascot({ state = "idle", size = "md", level, label, appearance = "auto", motion = "auto", className, style, ...svgProps }, ref) {
|
|
7414
|
+
useInjectedStyle(STYLE_ID, LINDA_MASCOT_CSS);
|
|
7415
|
+
const uid = safeId(useId());
|
|
7416
|
+
const px = resolveMascotPx(size);
|
|
7417
|
+
const tier = resolveMascotTier(px);
|
|
7418
|
+
const g = LINDA_MASCOT_GEOMETRY[tier];
|
|
7419
|
+
const coreRy = g.coreRx * 0.92;
|
|
7420
|
+
const eyeY = g.coreCy - coreRy + coreRy * 2 * 0.45;
|
|
7421
|
+
const eyes = [CX - g.eyeGap / 2, CX + g.eyeGap / 2];
|
|
7422
|
+
// Recorte delante/detrás: en lg los velos desenfocados salen más allá del anillo.
|
|
7423
|
+
const clipHalf = g.ringGlow === "blur" ? 120 : 80;
|
|
7424
|
+
const id = {
|
|
7425
|
+
core: `${uid}-core`,
|
|
7426
|
+
coreClip: `${uid}-core-clip`,
|
|
7427
|
+
rim: `${uid}-rim`,
|
|
7428
|
+
ring: `${uid}-ring`,
|
|
7429
|
+
halo: `${uid}-halo`,
|
|
7430
|
+
shadow: `${uid}-shadow`,
|
|
7431
|
+
specular: `${uid}-specular`,
|
|
7432
|
+
back: `${uid}-back`,
|
|
7433
|
+
front: `${uid}-front`,
|
|
7434
|
+
soft: `${uid}-soft`,
|
|
7435
|
+
eyeGlow: `${uid}-eye-glow`,
|
|
7436
|
+
aura: `${uid}-aura`,
|
|
7437
|
+
veil: `${uid}-veil`,
|
|
7438
|
+
tail: `${uid}-tail`,
|
|
7439
|
+
};
|
|
7440
|
+
const a11y = label
|
|
7441
|
+
? { role: "img", "aria-label": label }
|
|
7442
|
+
: { "aria-hidden": true };
|
|
7443
|
+
const renderRing = (ring, index, half) => {
|
|
7444
|
+
const motion = RING_MOTION[index];
|
|
7445
|
+
const spinStyle = {
|
|
7446
|
+
"--lm-spin-duration": motion.duration,
|
|
7447
|
+
"--lm-spin-direction": motion.direction,
|
|
7448
|
+
};
|
|
7449
|
+
const isMain = index === 0;
|
|
7450
|
+
const perimeter = ringPerimeterPx(ring, px);
|
|
7451
|
+
return (jsx("g", { "data-ring": index + 1, "data-ring-half": half, transform: `translate(${CX} ${g.coreCy}) rotate(${ring.tilt})`, opacity: half === "back" ? ring.opacity * 0.62 : ring.opacity, children: jsx("g", { clipPath: `url(#${half === "back" ? id.back : id.front})`, children: jsxs("g", { transform: `scale(1 ${ring.squash})`, children: [jsx("g", { className: "aurora-lm-spin", style: spinStyle, children: jsxs("g", { className: "aurora-lm-spin aurora-lm-boost", style: spinStyle, children: [g.ringGlow === "blur" && (jsx("circle", { "data-part": "veil", r: ring.r, fill: "none", stroke: `url(#${id.ring})`, strokeWidth: ring.strokePx * 16, strokeOpacity: 0.3, vectorEffect: "non-scaling-stroke", filter: `url(#${id.veil})` })), g.ringGlow !== "none" && (jsx("circle", { r: ring.r, fill: "none", stroke: `url(#${id.ring})`, strokeWidth: ring.strokePx * (g.ringGlow === "blur" ? 6.5 : 5), strokeOpacity: g.ringGlow === "blur" ? 0.34 : 0.14, vectorEffect: "non-scaling-stroke", filter: g.ringGlow === "blur" ? `url(#${id.soft})` : undefined })), g.ringGlow !== "none" && (jsx("circle", { r: ring.r + 2.2, fill: "none", stroke: `url(#${id.ring})`, strokeWidth: ring.strokePx * 0.55, strokeOpacity: 0.55, vectorEffect: "non-scaling-stroke" })), jsx("circle", { r: ring.r, fill: "none", stroke: `url(#${id.ring})`, strokeWidth: ring.strokePx, vectorEffect: "non-scaling-stroke" })] }) }), isMain && (jsxs("g", { className: "aurora-lm-spin aurora-lm-orbit", style: {
|
|
7452
|
+
"--lm-spin-duration": "1.6s",
|
|
7453
|
+
}, children: [jsx(Comet, { className: "aurora-lm-comet", r: ring.r, perimeter: perimeter, tail: 0.3, strokePx: ring.strokePx, headPx: Math.max(2, ring.strokePx * 2.4), steps: tier === "xs" ? 6 : 12, ringPaint: `url(#${id.tail})`, headPaint: LINDA_MASCOT_PALETTE.eyeGlow }), jsx(Comet, { className: "aurora-lm-glint", r: ring.r, perimeter: perimeter, tail: 0.14, strokePx: ring.strokePx * 1.2, headPx: Math.max(2.4, ring.strokePx * 3), steps: tier === "xs" ? 4 : 8, ringPaint: `url(#${id.tail})`, headPaint: LINDA_MASCOT_PALETTE.eyeGlow })] }))] }) }) }, `${half}-${index}`));
|
|
7454
|
+
};
|
|
7455
|
+
const ringHalves = (half) => g.rings.map((ring, index) => renderRing(ring, index, half));
|
|
7456
|
+
return (
|
|
7457
|
+
// biome-ignore lint/a11y/noSvgWithoutTitle: el nombre llega por aria-label (con `label`) o la mascota es decorativa (aria-hidden); biome no ve el spread de `a11y`.
|
|
7458
|
+
jsxs("svg", { ref: ref, xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 120 120", width: px, height: px, focusable: "false", className: [
|
|
7459
|
+
"aurora-linda-mascot",
|
|
7460
|
+
`aurora-linda-mascot--${state}`,
|
|
7461
|
+
`aurora-linda-mascot--tier-${tier}`,
|
|
7462
|
+
className,
|
|
7463
|
+
]
|
|
7464
|
+
.filter(Boolean)
|
|
7465
|
+
.join(" "), "data-state": state, "data-tier": tier, "data-appearance": appearance, "data-motion": motion, style: {
|
|
7466
|
+
...style,
|
|
7467
|
+
[LINDA_MASCOT_LEVEL_VAR]: clampLevel(level),
|
|
7468
|
+
}, ...a11y, ...svgProps, children: [jsxs("defs", { children: [jsxs("radialGradient", { id: id.core, cx: "0.4", cy: "0.34", r: "0.72", children: [jsx("stop", { offset: "0", stopColor: LINDA_MASCOT_PALETTE.coreLight }), jsx("stop", { offset: "0.6", stopColor: "#15172B" }), jsx("stop", { offset: "1", stopColor: LINDA_MASCOT_PALETTE.coreDark })] }), jsxs("linearGradient", { id: id.ring, x1: "0", y1: "0", x2: "1", y2: "1", children: [jsx("stop", { offset: "0", stopColor: LINDA_MASCOT_PALETTE.pink }), jsx("stop", { offset: "0.36", stopColor: LINDA_MASCOT_PALETTE.violet }), jsx("stop", { offset: "0.68", stopColor: LINDA_MASCOT_PALETTE.cyan }), jsx("stop", { offset: "1", stopColor: LINDA_MASCOT_PALETTE.amber })] }), jsxs("linearGradient", { id: id.tail, x1: "0", y1: "0", x2: "1", y2: "1", children: [jsx("stop", { offset: "0", stopColor: "#FFC4EC" }), jsx("stop", { offset: "0.36", stopColor: "#D4C8FF" }), jsx("stop", { offset: "0.68", stopColor: "#B8F1FF" }), jsx("stop", { offset: "1", stopColor: "#FFE6BF" })] }), jsxs("linearGradient", { id: id.rim, x1: "0.2", y1: "0", x2: "0.5", y2: "1", children: [jsx("stop", { offset: "0", stopColor: LINDA_MASCOT_PALETTE.violet }), jsx("stop", { offset: "0.42", stopColor: LINDA_MASCOT_PALETTE.cyan }), jsx("stop", { offset: "0.74", stopColor: LINDA_MASCOT_PALETTE.pink }), jsx("stop", { offset: "1", stopColor: LINDA_MASCOT_PALETTE.amber })] }), jsx("clipPath", { id: id.coreClip, children: jsx("ellipse", { cx: CX, cy: g.coreCy, rx: g.coreRx, ry: coreRy }) }), jsx("clipPath", { id: id.back, children: jsx("rect", { x: -clipHalf, y: -clipHalf, width: clipHalf * 2, height: clipHalf }) }), jsx("clipPath", { id: id.front, children: jsx("rect", { x: -clipHalf, y: 0, width: clipHalf * 2, height: clipHalf }) }), g.halo && (jsxs("radialGradient", { id: id.halo, children: [jsx("stop", { offset: "0", stopColor: LINDA_MASCOT_PALETTE.violet, stopOpacity: "0.7" }), jsx("stop", { offset: tier === "lg" ? "0.48" : "0.42", stopColor: LINDA_MASCOT_PALETTE.pink, stopOpacity: tier === "lg" ? "0.36" : "0.28" }), jsx("stop", { offset: tier === "lg" ? "0.78" : "0.7", stopColor: LINDA_MASCOT_PALETTE.cyan, stopOpacity: tier === "lg" ? "0.14" : "0.08" }), jsx("stop", { offset: "1", stopColor: LINDA_MASCOT_PALETTE.cyan, stopOpacity: "0" })] })), g.halo && (jsxs("radialGradient", { id: id.shadow, children: [jsx("stop", { offset: "0", stopColor: LINDA_MASCOT_PALETTE.coreDark, stopOpacity: "0.28" }), jsx("stop", { offset: "1", stopColor: LINDA_MASCOT_PALETTE.coreDark, stopOpacity: "0" })] })), g.specular && (jsxs("radialGradient", { id: id.specular, children: [jsx("stop", { offset: "0", stopColor: "#FFFFFF", stopOpacity: "0.2" }), jsx("stop", { offset: "1", stopColor: "#FFFFFF", stopOpacity: "0" })] })), g.blur && (jsx("filter", { id: id.eyeGlow, filterUnits: "userSpaceOnUse", x: "-20", y: "-20", width: "160", height: "160", children: jsx("feGaussianBlur", { stdDeviation: "2.2" }) })), g.blur && (jsx("filter", { id: id.aura, filterUnits: "userSpaceOnUse", x: "-20", y: "-20", width: "160", height: "160", children: jsx("feGaussianBlur", { stdDeviation: tier === "lg" ? 5.5 : 4 }) })), g.ringGlow === "blur" && (jsx("filter", { id: id.soft, filterUnits: "userSpaceOnUse", x: "-90", y: "-90", width: "180", height: "180", children: jsx("feGaussianBlur", { stdDeviation: "2.6" }) })), g.ringGlow === "blur" && (jsx("filter", { id: id.veil, filterUnits: "userSpaceOnUse", x: "-100", y: "-100", width: "200", height: "200", children: jsx("feGaussianBlur", { stdDeviation: "5" }) }))] }), jsx("g", { className: "aurora-lm-level-body", children: jsxs("g", { className: "aurora-lm-breathe", children: [g.halo && (jsx("g", { className: "aurora-lm-level-halo", children: jsx("circle", { className: "aurora-lm-halo", "data-part": "halo", cx: CX, cy: g.coreCy, r: tier === "lg" ? 61 : 58, fill: `url(#${id.halo})` }) })), g.halo && (jsx("ellipse", { className: "aurora-lm-shadow", "data-part": "shadow", cx: CX, cy: g.coreCy + coreRy + 9, rx: g.coreRx * 0.8, ry: 4.5, fill: `url(#${id.shadow})` })), jsx("g", { className: "aurora-lm-rings aurora-lm-level-rings", children: ringHalves("back") }), tier !== "xs" && (jsx("ellipse", { className: "aurora-lm-rim", "data-part": "aura", cx: CX, cy: g.coreCy, rx: g.coreRx + 1, ry: coreRy + 1, fill: "none", stroke: `url(#${id.rim})`, strokeWidth: tier === "lg" ? 9 : g.blur ? 7 : 3, strokeOpacity: tier === "lg" ? 0.85 : g.blur ? 0.75 : 0.45, filter: g.blur ? `url(#${id.aura})` : undefined })), jsxs("g", { "data-part": "core", children: [jsx("ellipse", { cx: CX, cy: g.coreCy, rx: g.coreRx, ry: coreRy, fill: `url(#${id.core})` }), jsx("g", { clipPath: `url(#${id.coreClip})`, children: jsx("ellipse", { className: "aurora-lm-rim", cx: CX, cy: g.coreCy, rx: g.coreRx, ry: coreRy, fill: "none", stroke: `url(#${id.rim})`, strokeWidth: tier === "xs" ? 5 : g.blur ? 7 : 5, filter: g.blur ? `url(#${id.eyeGlow})` : undefined }) }), g.specular && (jsx("ellipse", { "data-part": "specular", cx: CX - g.coreRx * 0.36, cy: g.coreCy - coreRy * 0.5, rx: g.coreRx * 0.38, ry: g.coreRx * 0.2, transform: `rotate(-28 ${CX - g.coreRx * 0.36} ${g.coreCy - coreRy * 0.5})`, fill: `url(#${id.specular})` }))] }), jsxs("g", { className: "aurora-lm-eyes", "data-part": "eyes", children: [eyes.map((x) => (jsx("path", { d: eyePath(x, eyeY, g.eyeWidth, g.eyeHeight), fill: "none", stroke: LINDA_MASCOT_PALETTE.eyeGlow, strokeWidth: g.eyeStroke * (g.blur ? 2.2 : 1.7), strokeLinecap: "round", strokeOpacity: g.blur ? 0.85 : 0.35, filter: g.blur ? `url(#${id.eyeGlow})` : undefined }, `glow-${x}`))), eyes.map((x) => (jsx("path", { "data-part": "eye", d: eyePath(x, eyeY, g.eyeWidth, g.eyeHeight), fill: "none", stroke: LINDA_MASCOT_PALETTE.eye, strokeWidth: g.eyeStroke, strokeLinecap: "round" }, `eye-${x}`)))] }), jsx("g", { className: "aurora-lm-rings aurora-lm-level-rings", children: ringHalves("front") }), state === "ready" && tier !== "xs" && (jsx("g", { "data-part": "sparkles", children: SPARKLES.map((s) => (jsx("g", { transform: `translate(${s.x} ${s.y})`, children: jsx("path", { className: "aurora-lm-sparkle", d: sparklePath(s.s), style: {
|
|
7469
|
+
"--lm-sparkle-delay": s.delay,
|
|
7470
|
+
} }) }, `${s.x}-${s.y}`))) }))] }) })] }));
|
|
7471
|
+
});
|
|
7472
|
+
|
|
6769
7473
|
const defaultTranslations$8 = {
|
|
6770
7474
|
previewAlt: "Vista previa de imagen",
|
|
6771
7475
|
removeButtonAriaLabel: "Eliminar imagen",
|
|
@@ -11987,31 +12691,20 @@ const VARIANT_BY_COLOR = {
|
|
|
11987
12691
|
danger: "danger",
|
|
11988
12692
|
default: "default",
|
|
11989
12693
|
};
|
|
11990
|
-
// TECH-2459 ·
|
|
11991
|
-
//
|
|
11992
|
-
//
|
|
11993
|
-
//
|
|
11994
|
-
//
|
|
11995
|
-
//
|
|
11996
|
-
|
|
11997
|
-
|
|
11998
|
-
|
|
11999
|
-
|
|
12000
|
-
|
|
12001
|
-
|
|
12002
|
-
|
|
12003
|
-
|
|
12004
|
-
// Color de la `x`, también tomado del slot `closeButton` de v2 (`text-success-500`,
|
|
12005
|
-
// `data-[hover]:bg-success-200`, …). `bg-transparent` neutraliza el `bg-default` de
|
|
12006
|
-
// `.close-button--default` de v3, que sobre un fondo teñido cantaba como una píldora gris.
|
|
12007
|
-
const CLOSE_BY_COLOR = {
|
|
12008
|
-
primary: "text-primary-500 hover:bg-primary-200",
|
|
12009
|
-
secondary: "text-secondary-500 hover:bg-secondary-200",
|
|
12010
|
-
success: "text-success-500 hover:bg-success-200",
|
|
12011
|
-
warning: "text-warning-500 hover:bg-warning-200",
|
|
12012
|
-
danger: "text-danger-500 hover:bg-danger-200",
|
|
12013
|
-
default: "text-default-400 hover:bg-default-200",
|
|
12014
|
-
};
|
|
12694
|
+
// TECH-2459 · Aspecto de v3 — DECISIÓN DEL USUARIO del 1-oct-2026, excepción aceptada a v2. v2
|
|
12695
|
+
// pintaba el toast con `<Alert variant="flat" color={...}>`, que daba FONDO TEÑIDO por color, y
|
|
12696
|
+
// P-25 (F5) lo había repuesto con las clases literales de v2 (`bg-success-50`…). Ya no: el toast
|
|
12697
|
+
// se ve como los ejemplos oficiales de v3 (Default/Accent/Success/Warning/Danger). El fondo es el
|
|
12698
|
+
// de `.toast` (`bg-surface`: blanco en claro, la superficie oscura del pack en oscuro) en los seis
|
|
12699
|
+
// colores, y el color del estado va SOLO en el título y en el icono. La descripción es la de v3
|
|
12700
|
+
// (`text-muted`), sin tocar.
|
|
12701
|
+
// La `x`, neutra en los seis colores: `text-muted`, el mismo gris de `.close-button--default` de
|
|
12702
|
+
// v3, que llega solo al no pisarlo. `bg-transparent` quita el `bg-default` de esa clase: en Aurora
|
|
12703
|
+
// `--color-default` es el gris MEDIO de v2 (`#d4d4d8`), no el casi imperceptible que v3 espera, y
|
|
12704
|
+
// sobre el fondo blanco cantaba como una píldora gris permanente. El hover sí lleva el gris suave
|
|
12705
|
+
// de v3 (`bg-default-hover`, que deriva del `--default` PROPIO de v3, no del de Aurora); hay que
|
|
12706
|
+
// declararlo como utilidad porque el `bg-transparent` de arriba también gana al hover de v3.
|
|
12707
|
+
const CLOSE_CLASS = "shrink-0 self-center bg-transparent hover:bg-default-hover data-[hovered=true]:bg-default-hover";
|
|
12015
12708
|
// TECH-2459 · Indicador de estado — REPUESTO el 8-sep-2026 por decisión del usuario, a partir del
|
|
12016
12709
|
// finding `app-loginos-toast-v3-visual.md` (smoke de loginos contra QA, beta.114): con el MISMO
|
|
12017
12710
|
// toast (`color="danger"`, login con credenciales inválidas) v2 pintaba un glifo de estado a la
|
|
@@ -12038,22 +12731,25 @@ const ICON_BY_VARIANT = {
|
|
|
12038
12731
|
success: SuccessIcon,
|
|
12039
12732
|
warning: WarningIcon,
|
|
12040
12733
|
};
|
|
12041
|
-
// Color del glifo:
|
|
12042
|
-
//
|
|
12043
|
-
//
|
|
12044
|
-
//
|
|
12045
|
-
//
|
|
12046
|
-
//
|
|
12047
|
-
//
|
|
12048
|
-
//
|
|
12049
|
-
//
|
|
12734
|
+
// Color del glifo: el MISMO que el del título en cada estado (decisión del 1-oct, ver arriba). Los
|
|
12735
|
+
// literales de v2 que había aquí (`text-success-700`…) estaban pensados para el fondo teñido y ya
|
|
12736
|
+
// no aplican. Lo que pinta v3 por su cuenta (`toast.css`, la última regla de cada selector):
|
|
12737
|
+
// - success/warning/danger: `.toast--<x> .toast__indicator` → `text-<x>-soft-foreground`, el mismo
|
|
12738
|
+
// token que su título. Se deja heredar: cadena vacía.
|
|
12739
|
+
// - accent (= `primary`): v3 tiñe el título (`text-accent-soft-foreground`) pero NO el indicador,
|
|
12740
|
+
// que se queda en la base `text-overlay-foreground` (neutro: casi negro en claro, casi blanco en
|
|
12741
|
+
// oscuro). Se le da el token del título para que icono y título digan el mismo estado.
|
|
12742
|
+
// - default: v3 no tiñe ni título ni indicador; los dos quedan en `text-overlay-foreground`, que
|
|
12743
|
+
// es el foreground neutro que toca. Cadena vacía.
|
|
12744
|
+
// - secondary: v3 no tiene esa variante de marca (se parte de "default"), así que se recolorea con
|
|
12745
|
+
// el token propio de Aurora, el mismo que el título (`--color-secondary-600`).
|
|
12050
12746
|
const ICON_BY_COLOR = {
|
|
12051
|
-
primary: "text-
|
|
12747
|
+
primary: "text-accent-soft-foreground",
|
|
12052
12748
|
secondary: "text-secondary-600",
|
|
12053
|
-
success: "
|
|
12054
|
-
warning: "
|
|
12055
|
-
danger: "
|
|
12056
|
-
default: "
|
|
12749
|
+
success: "",
|
|
12750
|
+
warning: "",
|
|
12751
|
+
danger: "",
|
|
12752
|
+
default: "",
|
|
12057
12753
|
};
|
|
12058
12754
|
// Geometría del hueco del icono en v2 (slot `iconWrapper`: `flex-none relative w-9 h-9
|
|
12059
12755
|
// rounded-full grid place-items-center`, con el glifo a `w-6`). El toast de v2 pasaba
|
|
@@ -12078,7 +12774,7 @@ function GlobalToast({ translations = {} } = {}) {
|
|
|
12078
12774
|
const slots = toastVariants({ placement: "top end", variant });
|
|
12079
12775
|
const isSecondary = color === "secondary";
|
|
12080
12776
|
const StatusIcon = ICON_BY_VARIANT[variant];
|
|
12081
|
-
return (jsx("div", { className: "fixed top-4 right-4 z-[2000] w-full max-w-sm", children: jsxs("div", { className: cn(slots.toast(), "gap-x-0 rounded-[12px]"
|
|
12777
|
+
return (jsx("div", { className: "fixed top-4 right-4 z-[2000] w-full max-w-sm", children: jsxs("div", { className: cn(slots.toast(), "gap-x-0 rounded-[12px]"), "data-frontmost": "true", style: { position: "relative" }, children: [jsx("div", { className: cn(slots.indicator(), INDICATOR_BASE, ICON_BY_COLOR[color]), children: jsx(StatusIcon, {}) }), jsxs("div", { className: cn(slots.content(), "ms-2 box-border min-h-10 justify-center"), children: [jsx("div", { className: slots.title(), style: isSecondary ? { color: "var(--color-secondary-600)" } : undefined, children: toast.title }), toast.description && (jsx("div", { className: slots.description(), children: toast.description }))] }), jsx(CloseButton, { "aria-label": t.closeButtonAriaLabel, className: CLOSE_CLASS, onPress: hideToast })] }) }));
|
|
12082
12778
|
}
|
|
12083
12779
|
|
|
12084
12780
|
/**
|
|
@@ -18883,4 +19579,4 @@ const NavigationLoadingProvider = ({ children, }) => {
|
|
|
18883
19579
|
return (jsxs(NavigationLoadingContext.Provider, { value: value, children: [children, jsx(NavigationLoadingOverlay, { isVisible: isVisible })] }));
|
|
18884
19580
|
};
|
|
18885
19581
|
|
|
18886
|
-
export { ALL_THEMES, AURORA_THEME_FAMILIES, AURORA_THEME_REGISTRY, AccordionList, AddHolidayForm, Alert, AnalyticsCard, AreaLineChart, AuraAutocomplete, AuraTable, AuraToastProvider, AuraAutocomplete as Autocomplete, AutocompleteItem, BEWEOS_THEME_MODE_COOKIE_NAME, Badge, BreadcrumbItem, BreadcrumbsComponent, Button, CONTAINER_ATTRIBUTE, Card, CardBody, Checkbox, Chip, ColorPicker, ColorSelector, ContentCarousel, Currency, DEFAULT_DONUT_COLORS, DEFAULT_PREDEFINED_COLORS, DEFAULT_RANKED_BAR_COLORS, DISMISS_CONTROL_ATTRIBUTE, DatePicker, DateRangePicker, DateSelector, Divider, DonutChart, Drawer, DrawerBody, DrawerContent, DrawerFilters, DrawerFooter, DrawerHeader, Dropdown, DropdownSection, EmailPreview, EnumMenuNavListItem, GlobalToast, H1, H2, H3, H4, HeaderComponent, HeroUIProvider, HolidayType, IconComponent, Image$1 as Image, ImagePreview, Input, InputPassword, Kanban, KanbanCard, KanbanColumn, LINDA_TONE_PROPERTIES, LindaIcon, MenuClaw, MenuComponent, Modal, ModalBody, ModalContent, ModalFooter, ModalHeader, MultiStepWizard, NavigationLoadingContext, NavigationLoadingOverlay, NavigationLoadingProvider, P, Pagination, Phone, Progress, PromotionalBanner, REGISTERED_THEME_COLORS, RangeFilter, RankedBarList, RowSteps, DEFAULT_TRANSLATIONS$2 as SEGMENTATION_DEFAULT_TRANSLATIONS, ScheduleRow, SearchInput, SegmentationBuilder, Select, SelectItem, SelectSection, SimpleLineChart, SocialMediaBar, SocialMediaCarousel, SocialMediaPreview, StatTile, StepIndicator, Switch, Switch as SwitchComponent, THEME_COLOR_HEX_MAP, Tab, Table, Tabs, TagsFilter, Textarea, ThemeContext, ThemePicker, ThemeProvider, TimeInput, TimeInputComponent, ToastContext, Tooltip, TwoColumnLayoutAgent, UploadFile, VerticalSteps, ViolinPlot, WhatsAppPreview, Wizard, WizardNavigation, WizardSidebar, applyCustomPrimaryColor, applyLindaTones, cn, computeBars, computeMax, computeSlices, computeTotal, containerProps, createCurrencyFormatter, uniqueCountries as defaultCountries, defaultCurrencyOptions, defaultTranslations$5 as defaultTranslations, deriveLindaTones, dismissControlProps, forceLightOnlyThemeBeforeReact, formatAuroraThemeTooltip, formatDonutPercent, generateThemeColorScale, getContrastForeground, getRegisteredThemeColorBases, getSelectedKeyFromPath, hexToHsl, hexToThemeColor, hslToCssValue, isAuroraFullThemeId, isDismissControl, isDonutEmpty, isExactThemeColor, isGroupState, isHexColor, isRankedBarListEmpty, isSegmentationGroup, parseColorToHsl, removeCustomPrimaryColor, removeLindaTones, shouldClosePopoverOnInteractOutside, sizeMap, themeColors, useAuraToast, useCloseOnAncestorScroll, useDisclosure, useMediaQuery, useNavigationLoading, useThemeContext };
|
|
19582
|
+
export { ALL_THEMES, AURORA_THEME_FAMILIES, AURORA_THEME_REGISTRY, AccordionList, AddHolidayForm, Alert, AnalyticsCard, AreaLineChart, AuraAutocomplete, AuraTable, AuraToastProvider, AuraAutocomplete as Autocomplete, AutocompleteItem, BEWEOS_THEME_MODE_COOKIE_NAME, Badge, BetaBadge, BreadcrumbItem, BreadcrumbsComponent, Button, CONTAINER_ATTRIBUTE, Card, CardBody, Checkbox, Chip, ColorPicker, ColorSelector, ContentCarousel, Currency, DEFAULT_DONUT_COLORS, DEFAULT_PREDEFINED_COLORS, DEFAULT_RANKED_BAR_COLORS, DISMISS_CONTROL_ATTRIBUTE, DatePicker, DateRangePicker, DateSelector, Divider, DonutChart, Drawer, DrawerBody, DrawerContent, DrawerFilters, DrawerFooter, DrawerHeader, Dropdown, DropdownSection, EmailPreview, EnumMenuNavListItem, GlobalToast, H1, H2, H3, H4, HeaderComponent, HeroUIProvider, HolidayType, IconComponent, Image$1 as Image, ImagePreview, Input, InputPassword, Kanban, KanbanCard, KanbanColumn, LINDA_MASCOT_LEVEL_VAR, LINDA_MASCOT_STATES, LINDA_TONE_PROPERTIES, LindaIcon, LindaMascot, MenuClaw, MenuComponent, Modal, ModalBody, ModalContent, ModalFooter, ModalHeader, MultiStepWizard, NavigationLoadingContext, NavigationLoadingOverlay, NavigationLoadingProvider, P, Pagination, Phone, Progress, PromotionalBanner, REGISTERED_THEME_COLORS, RangeFilter, RankedBarList, RowSteps, DEFAULT_TRANSLATIONS$2 as SEGMENTATION_DEFAULT_TRANSLATIONS, ScheduleRow, SearchInput, SegmentationBuilder, Select, SelectItem, SelectSection, SimpleLineChart, SocialMediaBar, SocialMediaCarousel, SocialMediaPreview, StatTile, StepIndicator, Switch, Switch as SwitchComponent, THEME_COLOR_HEX_MAP, Tab, Table, Tabs, TagsFilter, Textarea, ThemeContext, ThemePicker, ThemeProvider, TimeInput, TimeInputComponent, ToastContext, Tooltip, TwoColumnLayoutAgent, UploadFile, VerticalSteps, ViolinPlot, WhatsAppPreview, Wizard, WizardNavigation, WizardSidebar, applyCustomPrimaryColor, applyLindaTones, cn, computeBars, computeMax, computeSlices, computeTotal, containerProps, createCurrencyFormatter, uniqueCountries as defaultCountries, defaultCurrencyOptions, defaultTranslations$5 as defaultTranslations, deriveLindaTones, dismissControlProps, forceLightOnlyThemeBeforeReact, formatAuroraThemeTooltip, formatDonutPercent, generateThemeColorScale, getContrastForeground, getRegisteredThemeColorBases, getSelectedKeyFromPath, hexToHsl, hexToThemeColor, hslToCssValue, isAuroraFullThemeId, isDismissControl, isDonutEmpty, isExactThemeColor, isGroupState, isHexColor, isRankedBarListEmpty, isSegmentationGroup, parseColorToHsl, removeCustomPrimaryColor, removeLindaTones, shouldClosePopoverOnInteractOutside, sizeMap, themeColors, useAuraToast, useCloseOnAncestorScroll, useDisclosure, useMediaQuery, useNavigationLoading, useThemeContext };
|