@axzydev/axzy_ui_system 1.7.4 → 1.8.1
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 +224 -110
- package/dist/index.cjs.map +1 -1
- package/dist/index.css +1 -1
- package/dist/index.d.cts +22 -5
- package/dist/index.d.ts +22 -5
- package/dist/index.js +220 -107
- package/dist/index.js.map +1 -1
- package/dist/layered.css +1 -1
- package/package.json +1 -1
- package/src/components/atoms/button/button.tsx +7 -0
- package/src/components/atoms/input/input.tsx +5 -0
- package/src/components/atoms/segmented-control/segmented-control.tsx +22 -5
- package/src/components/molecules/card/card.props.ts +12 -0
- package/src/components/molecules/card/card.tsx +52 -22
- package/src/components/molecules/chip-input/chip-input.tsx +2 -0
- package/src/components/molecules/multi-select/multi-select.tsx +2 -0
- package/src/components/molecules/tabs/tabs.tsx +22 -8
- package/src/components/organisms/data-table/dataTable.tsx +29 -3
- package/src/components/organisms/page-header/page-header.tsx +22 -6
- package/src/components/organisms/sidebar/sidebar.tsx +8 -5
- package/src/components/organisms/topbar/topbar.props.ts +2 -0
- package/src/components/organisms/topbar/topbar.tsx +56 -34
- package/src/components/templates/layout/layout.tsx +3 -2
- package/src/components/theme-provider/themeProvider.props.ts +1 -1
- package/src/components/theme-provider/themeProvider.tsx +54 -1
- package/src/index.ts +4 -4
- package/src/json-renderer/catalog/axzyCatalog.ts +5 -0
- package/src/sandbox/axzyConfig.test.ts +4 -0
- package/src/sandbox/axzyConfig.ts +1 -1
- package/src/sandbox/i18n.ts +6 -0
- package/src/showcases/FeedbackShowcases.tsx +2 -1
- package/src/showcases/StructureShowcases.tsx +17 -2
- package/src/theme/theme-context.ts +10 -2
- package/src/theme/theme.ts +1 -1
- package/src/theme/theme.types.ts +2 -1
|
@@ -3,7 +3,7 @@ import useClickOutside from "@/hooks/useClickOutside";
|
|
|
3
3
|
import { useRef, useState } from "react";
|
|
4
4
|
import { ITTopBarProps } from "./topbar.props";
|
|
5
5
|
import ITText from "@/components/atoms/text/text";
|
|
6
|
-
import {
|
|
6
|
+
import { useITAppearance } from "@/theme/theme-context";
|
|
7
7
|
import { useITTranslation } from "@/i18n";
|
|
8
8
|
|
|
9
9
|
/**
|
|
@@ -73,7 +73,10 @@ export default function ITTopBar({
|
|
|
73
73
|
showDesktopMenuButton = false,
|
|
74
74
|
}: ITTopBarProps) {
|
|
75
75
|
// Flat appearance: plain bar (no shadow/blur), initials avatar, name before it.
|
|
76
|
-
|
|
76
|
+
// Bento: same plain structure, blended with the canvas, pill nav and round icon buttons.
|
|
77
|
+
const appearance = useITAppearance();
|
|
78
|
+
const bento = appearance === "bento";
|
|
79
|
+
const flat = appearance === "flat" || bento;
|
|
77
80
|
const { t } = useITTranslation();
|
|
78
81
|
const initials = (userMenu?.userName ?? "")
|
|
79
82
|
.trim()
|
|
@@ -81,6 +84,8 @@ export default function ITTopBar({
|
|
|
81
84
|
.slice(0, 2)
|
|
82
85
|
.map((word) => word.charAt(0).toUpperCase())
|
|
83
86
|
.join("");
|
|
87
|
+
const userRestBg = bento ? "var(--it-bento-control-bg, #f1f5f9)" : "transparent";
|
|
88
|
+
const userHoverBg = bento ? "var(--it-bento-control-bg-hover, #e2e8f0)" : "var(--it-topbar-user-hover, #f1f5f9)";
|
|
84
89
|
const [isUserMenuOpen, setIsUserMenuOpen] = useState(false);
|
|
85
90
|
const userMenuRef = useRef<HTMLDivElement>(null);
|
|
86
91
|
|
|
@@ -90,8 +95,10 @@ export default function ITTopBar({
|
|
|
90
95
|
<header
|
|
91
96
|
className={`sticky top-0 z-40 transition-all duration-300 ${flat ? "" : "backdrop-blur-md"}`}
|
|
92
97
|
style={{
|
|
93
|
-
backgroundColor:
|
|
94
|
-
|
|
98
|
+
backgroundColor: bento
|
|
99
|
+
? "var(--it-layout-bg, #f4f4f6)"
|
|
100
|
+
: "var(--it-topbar-bg, rgba(255, 255, 255, 0.9))",
|
|
101
|
+
borderBottom: bento ? "none" : "1px solid var(--it-topbar-border, rgba(15, 23, 42, 0.06))",
|
|
95
102
|
boxShadow: flat ? "none" : "var(--shadow-xs)",
|
|
96
103
|
}}
|
|
97
104
|
>
|
|
@@ -105,12 +112,13 @@ export default function ITTopBar({
|
|
|
105
112
|
<button
|
|
106
113
|
type="button"
|
|
107
114
|
aria-label={t("common.menu")}
|
|
108
|
-
className={`${showDesktopMenuButton ? "" : "lg:hidden"} p-2.5 -ml-2.5 rounded-xl transition-colors duration-200`}
|
|
115
|
+
className={`${showDesktopMenuButton ? "" : "lg:hidden"} ${bento ? "w-10 h-10 flex items-center justify-center rounded-full" : "p-2.5 -ml-2.5 rounded-xl"} transition-colors duration-200`}
|
|
109
116
|
style={{
|
|
110
|
-
color: "var(--it-topbar-icon, #64748b)",
|
|
117
|
+
color: bento ? "var(--it-bento-control-text, #475569)" : "var(--it-topbar-icon, #64748b)",
|
|
118
|
+
backgroundColor: bento ? "var(--it-bento-control-bg, #f1f5f9)" : undefined,
|
|
111
119
|
}}
|
|
112
|
-
onMouseEnter={(e) => e.currentTarget.style.backgroundColor = "var(--it-topbar-user-hover, #f1f5f9)"}
|
|
113
|
-
onMouseLeave={(e) => e.currentTarget.style.backgroundColor = "transparent"}
|
|
120
|
+
onMouseEnter={(e) => e.currentTarget.style.backgroundColor = bento ? "var(--it-bento-control-bg-hover, #e2e8f0)" : "var(--it-topbar-user-hover, #f1f5f9)"}
|
|
121
|
+
onMouseLeave={(e) => e.currentTarget.style.backgroundColor = bento ? "var(--it-bento-control-bg, #f1f5f9)" : "transparent"}
|
|
114
122
|
onClick={onToggleMobileMenu}
|
|
115
123
|
>
|
|
116
124
|
<FaBars className="w-[1.125rem] h-[1.125rem]" />
|
|
@@ -137,28 +145,42 @@ export default function ITTopBar({
|
|
|
137
145
|
|
|
138
146
|
{/* Top Navigation Items (Desktop) */}
|
|
139
147
|
{navItems && navItems.length > 0 && (
|
|
140
|
-
<nav
|
|
141
|
-
{
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
{
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
148
|
+
<nav
|
|
149
|
+
className={bento ? "hidden md:flex ml-4 gap-2" : "hidden md:flex ml-8 space-x-1 border-l pl-8"}
|
|
150
|
+
style={bento ? undefined : { borderColor: "var(--it-topbar-border, rgba(15, 23, 42, 0.06))" }}
|
|
151
|
+
>
|
|
152
|
+
{navItems.map((item) => {
|
|
153
|
+
// Resting look per appearance; the active item is the ink pill in bento.
|
|
154
|
+
const restBg = bento
|
|
155
|
+
? item.isActive ? "var(--it-bento-ink-bg, #0f172a)" : "var(--it-bento-control-bg, #f1f5f9)"
|
|
156
|
+
: item.isActive ? "var(--it-topbar-user-hover, #f1f5f9)" : "transparent";
|
|
157
|
+
const restColor = bento
|
|
158
|
+
? item.isActive ? "var(--it-bento-ink-text, #ffffff)" : "var(--it-bento-control-text, #475569)"
|
|
159
|
+
: "var(--it-topbar-text, #475569)";
|
|
160
|
+
return (
|
|
161
|
+
<button
|
|
162
|
+
key={item.id}
|
|
163
|
+
onClick={() => onNavItemClick?.(item.id)}
|
|
164
|
+
aria-current={item.isActive ? "page" : undefined}
|
|
165
|
+
className={`px-4 py-2 ${bento ? "rounded-full" : "rounded-lg"} font-medium text-[0.9rem] transition-all duration-200 ease-[cubic-bezier(0.2,0,0,1)]`}
|
|
166
|
+
style={{ color: restColor, backgroundColor: restBg }}
|
|
167
|
+
onMouseEnter={(e) => {
|
|
168
|
+
if (bento && item.isActive) return;
|
|
169
|
+
e.currentTarget.style.color = bento ? "var(--it-bento-control-text, #0f172a)" : "var(--it-topbar-text, #0f172a)";
|
|
170
|
+
e.currentTarget.style.backgroundColor = bento ? "var(--it-bento-control-bg-hover, #e2e8f0)" : "var(--it-topbar-user-hover, #f1f5f9)";
|
|
171
|
+
}}
|
|
172
|
+
onMouseLeave={(e) => {
|
|
173
|
+
e.currentTarget.style.color = restColor;
|
|
174
|
+
e.currentTarget.style.backgroundColor = restBg;
|
|
175
|
+
}}
|
|
176
|
+
>
|
|
177
|
+
<div className="flex items-center gap-2">
|
|
178
|
+
{item.icon && <span className="opacity-70">{item.icon}</span>}
|
|
179
|
+
<ITText as="span">{item.label}</ITText>
|
|
180
|
+
</div>
|
|
181
|
+
</button>
|
|
182
|
+
);
|
|
183
|
+
})}
|
|
162
184
|
</nav>
|
|
163
185
|
)}
|
|
164
186
|
</div>
|
|
@@ -184,15 +206,15 @@ export default function ITTopBar({
|
|
|
184
206
|
<div className="relative">
|
|
185
207
|
<button
|
|
186
208
|
type="button"
|
|
187
|
-
className={`flex items-center gap-3 rounded-full py-1.5 transition-all duration-200 ease-[cubic-bezier(0.2,0,0,1)] border border-transparent hover:border-secondary-200 ${flat ? "flex-row-reverse pl-4 pr-2" : "pl-2 pr-4"}`}
|
|
209
|
+
className={`flex items-center gap-3 rounded-full py-1.5 transition-all duration-200 ease-[cubic-bezier(0.2,0,0,1)] border border-transparent ${bento ? "" : "hover:border-secondary-200"} ${flat ? "flex-row-reverse pl-4 pr-2" : "pl-2 pr-4"}`}
|
|
188
210
|
style={{
|
|
189
|
-
backgroundColor: isUserMenuOpen ?
|
|
211
|
+
backgroundColor: isUserMenuOpen ? userHoverBg : userRestBg,
|
|
190
212
|
}}
|
|
191
213
|
onMouseEnter={(e) => {
|
|
192
|
-
if (!isUserMenuOpen) e.currentTarget.style.backgroundColor =
|
|
214
|
+
if (!isUserMenuOpen) e.currentTarget.style.backgroundColor = userHoverBg;
|
|
193
215
|
}}
|
|
194
216
|
onMouseLeave={(e) => {
|
|
195
|
-
if (!isUserMenuOpen) e.currentTarget.style.backgroundColor =
|
|
217
|
+
if (!isUserMenuOpen) e.currentTarget.style.backgroundColor = userRestBg;
|
|
196
218
|
}}
|
|
197
219
|
onClick={() => setIsUserMenuOpen(!isUserMenuOpen)}
|
|
198
220
|
>
|
|
@@ -4,7 +4,7 @@ import ITTopBar from "@/components/organisms/topbar/topbar";
|
|
|
4
4
|
import ITSidebar from "@/components/organisms/sidebar/sidebar";
|
|
5
5
|
import { ITLayoutProps } from "./layout.props";
|
|
6
6
|
import { theme } from "@/theme/theme";
|
|
7
|
-
import {
|
|
7
|
+
import { useITAppearance } from "@/theme/theme-context";
|
|
8
8
|
|
|
9
9
|
/**
|
|
10
10
|
* Main application shell with sidebar, topbar, and content area.
|
|
@@ -49,7 +49,8 @@ export default function ITLayout({
|
|
|
49
49
|
}: ITLayoutProps) {
|
|
50
50
|
const [internalCollapsed, setInternalCollapsed] = useState(true);
|
|
51
51
|
const [mobileSidebarOpen, setMobileSidebarOpen] = useState(false);
|
|
52
|
-
|
|
52
|
+
// Bento shares the flat sidebar structure (narrow panel, no drop shadow).
|
|
53
|
+
const flat = useITAppearance() !== "soft";
|
|
53
54
|
|
|
54
55
|
const isControlled = sidebar.isCollapsed !== undefined;
|
|
55
56
|
const desktopCollapsed = isControlled ? sidebar.isCollapsed : internalCollapsed;
|
|
@@ -58,6 +58,6 @@ export interface ITThemeProviderProps {
|
|
|
58
58
|
radius?: number;
|
|
59
59
|
/** Global shadow strength level. `0` ⇒ no shadows, `1` ⇒ subtle, `2` ⇒ default current look, `3` ⇒ pronounced. Shadows are injected as `--shadow-*`/`.shadow-*` overrides so cards, dropdowns and dialogs follow the level. @default 2 (keeps current default when omitted) */
|
|
60
60
|
shadow?: number;
|
|
61
|
-
/** Visual language of the whole system. `"soft"` keeps the current look (floating cards, tinted shadows, glass sidebar/top bar). `"flat"` switches the app shell and surfaces to flat ones: hairline borders instead of shadows on `ITCard`, a plain `ITSidebar` (no glow, no accent bar, narrower), a plain `ITTopBar` (no shadow, initials avatar) and a compact `ITPageHeader` without the icon tile. It also sets `data-it-appearance` on `<html>` so app CSS can follow it. Combine it with `shadow` and `radius` to finish the look. @default "soft" */
|
|
61
|
+
/** Visual language of the whole system. `"soft"` keeps the current look (floating cards, tinted shadows, glass sidebar/top bar). `"flat"` switches the app shell and surfaces to flat ones: hairline borders instead of shadows on `ITCard`, a plain `ITSidebar` (no glow, no accent bar, narrower), a plain `ITTopBar` (no shadow, initials avatar) and a compact `ITPageHeader` without the icon tile. It also sets `data-it-appearance` on `<html>` so app CSS can follow it. `"bento"` gives a modern pastel dashboard: light-gray canvas, borderless white cards with a diffuse shadow and 24px radius, pill buttons and inputs, an ink-pill active item in `ITTopBar` and a neutral-pill one in `ITSidebar`, a large `ITPageHeader` title and pastel tiles through `ITCard` `tone`; an explicit `radius`/`shadow` prop wins over the bento defaults. Combine it with `shadow` and `radius` to finish the look. @default "soft" */
|
|
62
62
|
appearance?: ITThemeAppearance;
|
|
63
63
|
}
|
|
@@ -83,7 +83,7 @@ const PRESET_I18N_KEY: Record<string, string> = {
|
|
|
83
83
|
// Contexto y hooks (useITTheme / useITThemeSafe) viven en @/theme/theme-context.
|
|
84
84
|
// Re-exportados desde aquí para mantener compatibilidad de la API pública.
|
|
85
85
|
// eslint-disable-next-line react-refresh/only-export-components
|
|
86
|
-
export { useITTheme, useITThemeSafe, useITFlatAppearance } from "@/theme/theme-context";
|
|
86
|
+
export { useITTheme, useITThemeSafe, useITFlatAppearance, useITAppearance } from "@/theme/theme-context";
|
|
87
87
|
|
|
88
88
|
const getNestedValue = (obj: any, path: string) => {
|
|
89
89
|
return path.split(".").reduce((acc, part) => acc && acc[part], obj);
|
|
@@ -343,6 +343,59 @@ export default function ITThemeProvider({
|
|
|
343
343
|
};
|
|
344
344
|
}, [radius, shadow]);
|
|
345
345
|
|
|
346
|
+
// Tokens del lenguaje "bento": solo se inyectan con appearance="bento".
|
|
347
|
+
// Los componentes leen estos vars (con fallback al aspecto base), así que un
|
|
348
|
+
// `radius`/`shadow` explícito gana: en ese caso no se definen los vars de
|
|
349
|
+
// radio/sombra y los componentes caen a la escala global.
|
|
350
|
+
useLayoutEffect(() => {
|
|
351
|
+
const STYLE_ID = "it-theme-bento-tokens";
|
|
352
|
+
let tag = document.getElementById(STYLE_ID) as HTMLStyleElement | null;
|
|
353
|
+
if (appearance !== "bento") {
|
|
354
|
+
tag?.remove();
|
|
355
|
+
return;
|
|
356
|
+
}
|
|
357
|
+
if (!tag) {
|
|
358
|
+
tag = document.createElement("style");
|
|
359
|
+
tag.id = STYLE_ID;
|
|
360
|
+
document.head.appendChild(tag);
|
|
361
|
+
}
|
|
362
|
+
const hasRadius = typeof radius === "number" && isFinite(radius);
|
|
363
|
+
const hasShadow = shadowLevel(shadow) !== null;
|
|
364
|
+
const shapeLight = hasRadius
|
|
365
|
+
? ""
|
|
366
|
+
: "--it-bento-card-radius:24px;--it-bento-pill-radius:9999px;--it-bento-field-radius:1rem;--it-input-radius:var(--it-bento-pill-radius);";
|
|
367
|
+
const shadowLight = hasShadow
|
|
368
|
+
? ""
|
|
369
|
+
: "--it-bento-card-shadow:0 1px 2px rgba(16,24,40,.04),0 12px 32px -16px rgba(16,24,40,.10);--it-bento-card-shadow-hover:0 1px 2px rgba(16,24,40,.06),0 18px 40px -16px rgba(16,24,40,.18);";
|
|
370
|
+
const shadowDark = hasShadow
|
|
371
|
+
? ""
|
|
372
|
+
: "--it-bento-card-shadow:0 1px 2px rgba(0,0,0,.4),0 12px 32px -16px rgba(0,0,0,.55);--it-bento-card-shadow-hover:0 1px 2px rgba(0,0,0,.5),0 18px 40px -16px rgba(0,0,0,.7);";
|
|
373
|
+
// El item activo del sidebar sigue los tokens bento (píldora neutra, texto tinta).
|
|
374
|
+
const navTokens =
|
|
375
|
+
"--it-sidebar-active-bg:var(--it-bento-nav-active-bg);--it-sidebar-active-color:var(--it-bento-nav-active-text);--it-sidebar-active-icon:var(--it-bento-nav-active-text);";
|
|
376
|
+
const light =
|
|
377
|
+
"--it-layout-bg:#f4f4f6;--it-card-bg:#ffffff;--it-bento-card-padding:1.5rem;" +
|
|
378
|
+
"--it-input-bg:#f1f5f9;--it-input-border:transparent;" +
|
|
379
|
+
"--it-input-focus-ring:0 0 0 2px color-mix(in srgb,var(--color-primary) 50%,transparent);" +
|
|
380
|
+
"--it-bento-control-bg:#f1f5f9;--it-bento-control-bg-hover:#e2e8f0;--it-bento-control-text:#475569;" +
|
|
381
|
+
"--it-bento-ink-bg:#0f172a;--it-bento-ink-text:#ffffff;" +
|
|
382
|
+
"--it-bento-nav-active-bg:#f1f5f9;--it-bento-nav-active-text:#0f172a;" + navTokens;
|
|
383
|
+
const dark =
|
|
384
|
+
"--it-layout-bg:#020617;--it-card-bg:#0f172a;" +
|
|
385
|
+
"--it-input-bg:#1e293b;--it-input-border:transparent;" +
|
|
386
|
+
"--it-bento-control-bg:#1e293b;--it-bento-control-bg-hover:#334155;--it-bento-control-text:#cbd5e1;" +
|
|
387
|
+
"--it-bento-ink-bg:#ffffff;--it-bento-ink-text:#0f172a;" +
|
|
388
|
+
"--it-bento-nav-active-bg:#1e293b;--it-bento-nav-active-text:#ffffff;" + navTokens;
|
|
389
|
+
const A = '[data-it-appearance="bento"]';
|
|
390
|
+
tag.textContent =
|
|
391
|
+
`:root${A}{${light}${shapeLight}${shadowLight}}` +
|
|
392
|
+
`:root${A}.dark,:root${A} .dark,:root${A} [data-theme="dark"]{${dark}${shadowDark}}` +
|
|
393
|
+
`:root${A} [data-theme="light"]{${light}${shadowLight}}`;
|
|
394
|
+
return () => {
|
|
395
|
+
tag?.remove();
|
|
396
|
+
};
|
|
397
|
+
}, [appearance, radius, shadow]);
|
|
398
|
+
|
|
346
399
|
// Inyectar variables CSS en el :root al cambiar la paleta o tema resuelto
|
|
347
400
|
useEffect(() => {
|
|
348
401
|
const injectStyles = (obj: any, prefix = "") => {
|
package/src/index.ts
CHANGED
|
@@ -171,7 +171,7 @@ import type { ITBadgetProps } from "./components/atoms/badget/badget.props";
|
|
|
171
171
|
import type { ITBreadcrumbsProps, ITBreadcrumbItem } from "./components/molecules/breadcrumbs/breadcrumbs.props";
|
|
172
172
|
import type { ITButtonProps } from "./components/atoms/button/button.props";
|
|
173
173
|
import type { ITCalendarProps } from "./components/molecules/calendar/calendar.props";
|
|
174
|
-
import type { ITCardProps } from "./components/molecules/card/card.props";
|
|
174
|
+
import type { ITCardProps, ITCardTone } from "./components/molecules/card/card.props";
|
|
175
175
|
import type { ITCheckboxProps } from "./components/atoms/checkbox/checkbox.props";
|
|
176
176
|
import type { ITChipProps } from "./components/atoms/chip/chip.props";
|
|
177
177
|
import type { ITChipInputProps } from "./components/molecules/chip-input/chip-input.props";
|
|
@@ -232,7 +232,7 @@ import ITLoader from "./components/atoms/loader/loader";
|
|
|
232
232
|
import type { LoaderProps as ITLoaderProps } from "./components/atoms/loader/loader.props";
|
|
233
233
|
import ITStepper from "./components/molecules/stepper/stepper";
|
|
234
234
|
import type { ITStepperProps } from "./components/molecules/stepper/stepper.props";
|
|
235
|
-
import ITThemeProvider, { useITTheme, useITThemeSafe, useITFlatAppearance } from "./components/theme-provider/themeProvider";
|
|
235
|
+
import ITThemeProvider, { useITTheme, useITThemeSafe, useITFlatAppearance, useITAppearance } from "./components/theme-provider/themeProvider";
|
|
236
236
|
import type { ITThemeProviderProps, ITThemePalette, ITThemeAppearance } from "./components/theme-provider/themeProvider.props";
|
|
237
237
|
import ITTimePicker from "./components/molecules/time-picker/timePicker";
|
|
238
238
|
import type { ITTimePickerProps } from "./components/molecules/time-picker/timePicker.props";
|
|
@@ -262,7 +262,7 @@ export {
|
|
|
262
262
|
ITSearchTable,
|
|
263
263
|
ITSegmentedControl, ITSidebar, ITSkeleton, ITSlideToggle, ITSlider, ITStack, ITStatCard, ITStepper, ITLineBarChart,
|
|
264
264
|
ITTable, ITTabs, ITText, ITTextarea, ITTripleFilter, ITThemeProvider,
|
|
265
|
-
useITTheme, useITThemeSafe, useITFlatAppearance, ITTimePicker, ITToast, ITWysiwyg,
|
|
265
|
+
useITTheme, useITThemeSafe, useITFlatAppearance, useITAppearance, ITTimePicker, ITToast, ITWysiwyg,
|
|
266
266
|
UploadStatus, FileTypeEnum,
|
|
267
267
|
getContrastTextColor, isLightColor, resolveCssColor,
|
|
268
268
|
ITI18nProvider, useITTranslation, esMessages, enMessages,
|
|
@@ -288,7 +288,7 @@ export {
|
|
|
288
288
|
ITAlertProps, ITAvatarProps, ITBadgetProps,
|
|
289
289
|
ITBreadcrumbItem, ITBreadcrumbsProps, ITButtonProps,
|
|
290
290
|
ITCalendarProps,
|
|
291
|
-
ITCardProps, ITCheckboxProps, ITChipProps, ITChipInputProps, ITConfirmDialogProps,
|
|
291
|
+
ITCardProps, ITCardTone, ITCheckboxProps, ITChipProps, ITChipInputProps, ITConfirmDialogProps,
|
|
292
292
|
ITDropdownMenuItem, ITDropdownMenuProps, ITFieldProps, ITMaskedInputProps, ITMultiSelectOption, ITMultiSelectProps,
|
|
293
293
|
ITDataTableFetchParams, ITDataTableProps, ITDataTableResponse, ITDatePickerProps,
|
|
294
294
|
ITDialogProps, ITDividerProps, ITDrawerProps,
|
|
@@ -91,6 +91,11 @@ export const AXZY_CATALOG_ENTRIES: CatalogEntry[] = [
|
|
|
91
91
|
summary: "Tarjeta con título, imagen y contenido.",
|
|
92
92
|
props: {
|
|
93
93
|
title: { kind: "string" },
|
|
94
|
+
tone: {
|
|
95
|
+
kind: "enum",
|
|
96
|
+
values: ["primary", "secondary", "ternary", "success", "info", "warning", "danger"],
|
|
97
|
+
doc: "Tinte pastel de la tarjeta (token --color-<tone>-soft).",
|
|
98
|
+
},
|
|
94
99
|
image: { kind: "string", doc: "URL de la imagen de cabecera." },
|
|
95
100
|
alt: { kind: "string" },
|
|
96
101
|
onClick: { kind: "action" },
|
|
@@ -16,6 +16,10 @@ describe("axzy.json", () => {
|
|
|
16
16
|
expect(config.showFab).toBe(true);
|
|
17
17
|
});
|
|
18
18
|
|
|
19
|
+
it("acepta la apariencia bento", () => {
|
|
20
|
+
expect(mergeAxzyConfig({ appearance: "bento" }).appearance).toBe("bento");
|
|
21
|
+
});
|
|
22
|
+
|
|
19
23
|
it("recorta fuera de rango en vez de romper", () => {
|
|
20
24
|
const config = mergeAxzyConfig({ density: 9, radius: 99, shadow: -3 });
|
|
21
25
|
expect(config.density).toBe(1.5);
|
|
@@ -71,7 +71,7 @@ export function mergeAxzyConfig(raw: unknown): AxzyConfig {
|
|
|
71
71
|
density: clamp(entrada.density, 0.5, 1.5, DEFAULT_AXZY_CONFIG.density),
|
|
72
72
|
radius: clamp(entrada.radius, 0, 24, DEFAULT_AXZY_CONFIG.radius, 0),
|
|
73
73
|
shadow: clamp(entrada.shadow, 0, 3, DEFAULT_AXZY_CONFIG.shadow, 0),
|
|
74
|
-
appearance: entrada.appearance === "flat" || entrada.appearance === "soft" ? entrada.appearance : DEFAULT_AXZY_CONFIG.appearance,
|
|
74
|
+
appearance: entrada.appearance === "flat" || entrada.appearance === "soft" || entrada.appearance === "bento" ? entrada.appearance : DEFAULT_AXZY_CONFIG.appearance,
|
|
75
75
|
showFab: typeof entrada.showFab === "boolean" ? entrada.showFab : DEFAULT_AXZY_CONFIG.showFab,
|
|
76
76
|
};
|
|
77
77
|
}
|
package/src/sandbox/i18n.ts
CHANGED
|
@@ -698,6 +698,9 @@ export const sandboxMessages: ITMessagesByLocale = {
|
|
|
698
698
|
"Layouts de card listos para producción construidos con ITCard",
|
|
699
699
|
"showcase.structure.patternCourseProgress": "Progreso del curso",
|
|
700
700
|
"showcase.structure.patternPricing": "Plan de precios destacado",
|
|
701
|
+
"showcase.structure.cardToneTitle": "Tarjetas pastel (tone)",
|
|
702
|
+
"showcase.structure.cardToneSubtitle": "La prop tone tiñe el fondo con el token --color-<tone>-soft; funciona en soft, flat y bento.",
|
|
703
|
+
"showcase.structure.cardToneBody": "Fondo tintado con el token suave del color.",
|
|
701
704
|
"showcase.structure.patternActivity": "Item de feed de actividad",
|
|
702
705
|
"showcase.structure.patternTeamMember": "Perfil de miembro del equipo",
|
|
703
706
|
"showcase.structure.textDesc":
|
|
@@ -1770,6 +1773,9 @@ export const sandboxMessages: ITMessagesByLocale = {
|
|
|
1770
1773
|
"Production-ready card layouts built with ITCard",
|
|
1771
1774
|
"showcase.structure.patternCourseProgress": "Course progress",
|
|
1772
1775
|
"showcase.structure.patternPricing": "Featured pricing plan",
|
|
1776
|
+
"showcase.structure.cardToneTitle": "Pastel cards (tone)",
|
|
1777
|
+
"showcase.structure.cardToneSubtitle": "The tone prop tints the background with the --color-<tone>-soft token; it works in soft, flat and bento.",
|
|
1778
|
+
"showcase.structure.cardToneBody": "Background tinted with the soft color token.",
|
|
1773
1779
|
"showcase.structure.patternActivity": "Activity feed item",
|
|
1774
1780
|
"showcase.structure.patternTeamMember": "Team member profile",
|
|
1775
1781
|
"showcase.structure.textDesc":
|
|
@@ -362,6 +362,7 @@ export const ThemeProviderShowcase = () => {
|
|
|
362
362
|
'<ITThemeProvider><App /></ITThemeProvider>',
|
|
363
363
|
'<ITThemeProvider theme={{ primary: "#3b82f6", layout: { sidebarBg: "#0f172a" } }} showFab={false}><App /></ITThemeProvider>',
|
|
364
364
|
'<ITThemeProvider density={0.9} radius={8} shadow={1} appearance="flat"><App /></ITThemeProvider>',
|
|
365
|
+
'<ITThemeProvider appearance="bento"><App /></ITThemeProvider>',
|
|
365
366
|
],
|
|
366
367
|
props: [
|
|
367
368
|
{ name: "theme", type: "Partial<ITThemePalette>", description: "Overrides de paleta fusionados (deep merge) con el tema por defecto: `primary`, `secondary`, `ternary`, `danger`, `success`, `info`, `alert`, `warning`, `layout` y `table`." },
|
|
@@ -370,7 +371,7 @@ export const ThemeProviderShowcase = () => {
|
|
|
370
371
|
{ name: "density", type: "number", default: "1", description: "Factor de densidad global: escala el `font-size` raíz para compactar todo lo basado en `rem`. Se limita a `0.5`–`1.5`." },
|
|
371
372
|
{ name: "radius", type: "number", default: "—", description: "Radio global en px aplicado a toda la escala `--radius-*`; `0` deja las esquinas totalmente cuadradas." },
|
|
372
373
|
{ name: "shadow", type: "number", default: "—", description: "Fuerza global de sombras: `0` ninguna, `1` sutiles, `2` look actual, `3` marcadas." },
|
|
373
|
-
{ name: "appearance", type: '"soft" | "flat"', default: '"soft"', description: "Lenguaje visual del sistema; `\"flat\"` usa superficies planas con bordes de 1 px y
|
|
374
|
+
{ name: "appearance", type: '"soft" | "flat" | "bento"', default: '"soft"', description: "Lenguaje visual del sistema; `\"flat\"` usa superficies planas con bordes de 1 px; `\"bento\"` usa lienzo gris claro, tarjetas blancas sin borde con sombra difusa y radio de 24 px, controles en píldora y navegación con píldora tinta. Fija `data-it-appearance` en `<html>`; un `radius`/`shadow` explícito gana sobre los valores bento." },
|
|
374
375
|
],
|
|
375
376
|
notes: [
|
|
376
377
|
"`useITTheme()` lanza error si se usa fuera del provider; `useITThemeSafe()` devuelve `undefined` en ese caso.",
|
|
@@ -372,6 +372,19 @@ export const CardShowcase = () => {
|
|
|
372
372
|
}
|
|
373
373
|
gallery={
|
|
374
374
|
<ITStack spacing={8}>
|
|
375
|
+
<GallerySection
|
|
376
|
+
title={t("showcase.structure.cardToneTitle")}
|
|
377
|
+
subtitle={t("showcase.structure.cardToneSubtitle")}
|
|
378
|
+
>
|
|
379
|
+
<div className="grid grid-cols-2 lg:grid-cols-4 gap-4">
|
|
380
|
+
{(["primary", "secondary", "ternary", "success", "info", "warning", "danger"] as const).map((tone) => (
|
|
381
|
+
<ITCard key={tone} tone={tone} title={tone}>
|
|
382
|
+
{t("showcase.structure.cardToneBody")}
|
|
383
|
+
</ITCard>
|
|
384
|
+
))}
|
|
385
|
+
</div>
|
|
386
|
+
</GallerySection>
|
|
387
|
+
|
|
375
388
|
<GallerySection
|
|
376
389
|
title={t("showcase.structure.galleryRealCases")}
|
|
377
390
|
subtitle={t("showcase.structure.galleryCardsSubtitle")}
|
|
@@ -537,13 +550,14 @@ export const CardShowcase = () => {
|
|
|
537
550
|
}
|
|
538
551
|
doc={{
|
|
539
552
|
summary: "Contenedor de tarjeta con imagen, título, cuerpo y pie de acciones opcionales; con onClick se vuelve interactivo.",
|
|
540
|
-
description: "Pieza base de composición para agrupar contenido relacionado. Borde, radio y sombra se adaptan al aspecto del tema (flat /
|
|
553
|
+
description: "Pieza base de composición para agrupar contenido relacionado. Borde, radio y sombra se adaptan al aspecto del tema (soft / flat / bento).",
|
|
541
554
|
examples: [
|
|
542
555
|
'<ITCard title="Ingresos del mes" actions={<ITButton label="Ver detalle" size="sm" />}>$48.230</ITCard>',
|
|
543
556
|
'<ITCard title="Curso" image="/courses/ts.jpg" imageClassName="h-32" onClick={() => open(course.id)}>…</ITCard>',
|
|
544
557
|
'<ITCard className="max-w-sm"><ITText muted>Tarjeta estática, sin cabecera ni acciones.</ITText></ITCard>',
|
|
545
558
|
],
|
|
546
559
|
props: [
|
|
560
|
+
{ name: "tone", type: '"primary" | "secondary" | "ternary" | "success" | "info" | "warning" | "danger"', default: "—", description: "Tarjeta pastel: tiñe el fondo con el token --color-<tone>-soft (sustituye al blanco). En soft/flat usa --color-<tone>-soft-border como borde; en bento sigue sin borde." },
|
|
547
561
|
{ name: "onClick", type: "() => void", default: "—", description: "Al recibirlo la tarjeta se vuelve clicable: cursor pointer, transición de sombra y elevación en hover. Sin él es estática." },
|
|
548
562
|
{ name: "title", type: "string", default: "—", description: "Título de la cabecera; se renderiza como h3 mediante ITText." },
|
|
549
563
|
{ name: "image", type: "string", default: "—", description: "URL de la imagen superior, a ancho completo con h-48 object-cover." },
|
|
@@ -559,7 +573,8 @@ export const CardShowcase = () => {
|
|
|
559
573
|
notes: [
|
|
560
574
|
"El aspecto del tema decide borde y sombra: en flat el borde es un hairline y solo hay sombra en hover si la tarjeta es clicable; en soft flota con --shadow-sm y sube a --shadow-lg con translateY(-2px).",
|
|
561
575
|
"Variables CSS: --it-card-bg, --it-card-border, --it-card-flat-border y --it-card-radius.",
|
|
562
|
-
"
|
|
576
|
+
"En bento la tarjeta no tiene borde, usa --it-bento-card-shadow (más marcada en hover si es clicable, sin translateY), radio de 24 px y padding de 1.5rem; un radius/shadow explícito en el provider gana.",
|
|
577
|
+
"El padding es fijo (cuerpo 1.25rem, 1.5rem en bento; pie 1rem): no hay prop de tamaño.",
|
|
563
578
|
],
|
|
564
579
|
}}
|
|
565
580
|
/>
|
|
@@ -38,10 +38,18 @@ export const useITThemeSafe = (): ITThemeContextType | undefined => {
|
|
|
38
38
|
return useContext(ITThemeContext);
|
|
39
39
|
};
|
|
40
40
|
|
|
41
|
+
/**
|
|
42
|
+
* Lenguaje visual activo (`"soft"` | `"flat"` | `"bento"`).
|
|
43
|
+
* Fuera de un ITThemeProvider devuelve `"soft"` (aspecto por defecto).
|
|
44
|
+
*/
|
|
45
|
+
export const useITAppearance = (): ITThemeAppearance => {
|
|
46
|
+
return useContext(ITThemeContext)?.appearance ?? "soft";
|
|
47
|
+
};
|
|
48
|
+
|
|
41
49
|
/**
|
|
42
50
|
* `true` cuando el provider pide el lenguaje visual plano (`appearance="flat"`).
|
|
43
51
|
* Fuera de un ITThemeProvider devuelve `false` (aspecto por defecto).
|
|
44
52
|
*/
|
|
45
53
|
export const useITFlatAppearance = (): boolean => {
|
|
46
|
-
return
|
|
47
|
-
};
|
|
54
|
+
return useITAppearance() === "flat";
|
|
55
|
+
};
|
package/src/theme/theme.ts
CHANGED
|
@@ -322,7 +322,7 @@ export const components = {
|
|
|
322
322
|
input: {
|
|
323
323
|
backgroundColor: 'var(--it-input-bg, #ffffff)',
|
|
324
324
|
borderColor: `var(--it-input-border, ${semanticColors.gray[300]})`,
|
|
325
|
-
borderRadius: 'var(--radius-md)',
|
|
325
|
+
borderRadius: 'var(--it-input-radius, var(--radius-md))',
|
|
326
326
|
focus: {
|
|
327
327
|
borderColor: `var(--it-input-focus-border, ${semanticColors.primary[500]})`,
|
|
328
328
|
ring: `var(--it-input-focus-ring, 0 0 0 3px ${semanticColors.primary[100]})`,
|
package/src/theme/theme.types.ts
CHANGED
|
@@ -16,8 +16,9 @@ export type ColorScale = {
|
|
|
16
16
|
* Visual language of the whole system.
|
|
17
17
|
* - `"soft"`: soft depth — floating cards, tinted shadows, glass sidebar and top bar (default).
|
|
18
18
|
* - `"flat"`: flat surfaces — hairline borders instead of shadows, plain sidebar and top bar, compact page headers.
|
|
19
|
+
* - `"bento"`: modern pastel "bento" dashboards — light-gray canvas, borderless white cards with a diffuse shadow and 24px radius, pill controls (buttons, inputs), ink-pill navigation and pastel tiles via `ITCard` `tone`.
|
|
19
20
|
*/
|
|
20
|
-
export type ITThemeAppearance = "soft" | "flat";
|
|
21
|
+
export type ITThemeAppearance = "soft" | "flat" | "bento";
|
|
21
22
|
|
|
22
23
|
export interface ITThemePalette {
|
|
23
24
|
/** Main brand accent color (hex). Drives primary buttons, active nav items, focus rings, links, and the `--color-primary` CSS variable. @default "#06b6d4" */
|