@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
package/package.json
CHANGED
|
@@ -9,6 +9,7 @@ import { useState } from "react";
|
|
|
9
9
|
import { theme } from "@/theme/theme";
|
|
10
10
|
import { ITButtonProps } from "./button.props";
|
|
11
11
|
import ITText from "@/components/atoms/text/text";
|
|
12
|
+
import { useITAppearance } from "@/theme/theme-context";
|
|
12
13
|
|
|
13
14
|
/**
|
|
14
15
|
* Highly configurable action button supporting 8 visual variants, theme colors, 3 sizes, icons, and hover/focus states.
|
|
@@ -37,6 +38,8 @@ export default function ITButton({
|
|
|
37
38
|
}: ITButtonProps) {
|
|
38
39
|
const [isHovered, setIsHovered] = useState(false);
|
|
39
40
|
const [isFocused, setIsFocused] = useState(false);
|
|
41
|
+
// Bento: pill buttons. The radius token is only defined when the provider has no explicit `radius`.
|
|
42
|
+
const bento = useITAppearance() === "bento";
|
|
40
43
|
|
|
41
44
|
/** La variante `icon-only` siempre es un cuadrado con padding simétrico. */
|
|
42
45
|
const isIconOnly = variant === "icon-only";
|
|
@@ -72,6 +75,10 @@ export default function ITButton({
|
|
|
72
75
|
transition: themeConfig.transition || 'all 0.2s',
|
|
73
76
|
};
|
|
74
77
|
|
|
78
|
+
if (bento) {
|
|
79
|
+
style.borderRadius = `var(--it-bento-pill-radius, ${themeConfig.borderRadius})`;
|
|
80
|
+
}
|
|
81
|
+
|
|
75
82
|
if (variant === "rounded") {
|
|
76
83
|
style.borderRadius = "9999px";
|
|
77
84
|
}
|
|
@@ -91,6 +91,11 @@ export default function ITInput({
|
|
|
91
91
|
const getStyle = () => {
|
|
92
92
|
const style = inputFieldStyle(inputTheme, size);
|
|
93
93
|
|
|
94
|
+
// Textarea is multi-line: in bento it keeps a soft radius instead of the single-line pill.
|
|
95
|
+
if (isTextArea) {
|
|
96
|
+
style.borderRadius = "var(--it-bento-field-radius, var(--radius-md))";
|
|
97
|
+
}
|
|
98
|
+
|
|
94
99
|
if (disabled) {
|
|
95
100
|
style.backgroundColor = inputTheme.disabled?.backgroundColor || style.backgroundColor;
|
|
96
101
|
style.borderColor = inputTheme.disabled?.borderColor || style.borderColor;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import clsx from "clsx";
|
|
2
2
|
import { ITSegmentedControlProps } from "./segmented-control.props";
|
|
3
3
|
import ITText from "@/components/atoms/text/text";
|
|
4
|
+
import { useITAppearance } from "@/theme/theme-context";
|
|
4
5
|
|
|
5
6
|
const sizeMap = {
|
|
6
7
|
sm: { button: "px-2.5 py-1.5 text-[11px]", container: "p-0.5" },
|
|
@@ -31,16 +32,22 @@ export default function ITSegmentedControl({
|
|
|
31
32
|
disabled = false,
|
|
32
33
|
}: ITSegmentedControlProps) {
|
|
33
34
|
const { button, container } = sizeMap[size];
|
|
35
|
+
// Bento: borderless pill track with an ink pill for the selected option.
|
|
36
|
+
const bento = useITAppearance() === "bento";
|
|
34
37
|
|
|
35
38
|
return (
|
|
36
39
|
<div
|
|
37
40
|
role="group"
|
|
38
41
|
className={clsx(
|
|
39
|
-
"inline-flex
|
|
42
|
+
"inline-flex",
|
|
43
|
+
bento
|
|
44
|
+
? "rounded-full"
|
|
45
|
+
: "rounded-xl bg-slate-100 dark:bg-slate-800 border border-slate-200 dark:border-slate-700",
|
|
40
46
|
container,
|
|
41
47
|
disabled && "opacity-50 cursor-not-allowed",
|
|
42
48
|
className
|
|
43
49
|
)}
|
|
50
|
+
style={bento ? { backgroundColor: "var(--it-bento-control-bg, #f1f5f9)" } : undefined}
|
|
44
51
|
>
|
|
45
52
|
{options.map((opt) => {
|
|
46
53
|
const isActive = opt.value === value;
|
|
@@ -53,12 +60,22 @@ export default function ITSegmentedControl({
|
|
|
53
60
|
disabled={disabled}
|
|
54
61
|
className={clsx(
|
|
55
62
|
button,
|
|
56
|
-
"
|
|
57
|
-
|
|
58
|
-
? "
|
|
59
|
-
:
|
|
63
|
+
"font-semibold transition-all flex items-center gap-1.5",
|
|
64
|
+
bento
|
|
65
|
+
? "rounded-full border border-transparent"
|
|
66
|
+
: isActive
|
|
67
|
+
? "rounded-lg bg-white dark:bg-slate-700 text-slate-800 dark:text-white shadow-sm border border-slate-200 dark:border-slate-600"
|
|
68
|
+
: "rounded-lg text-slate-500 dark:text-slate-400 hover:text-slate-700 dark:hover:text-slate-300 border border-transparent",
|
|
60
69
|
disabled && "pointer-events-none"
|
|
61
70
|
)}
|
|
71
|
+
style={
|
|
72
|
+
bento
|
|
73
|
+
? {
|
|
74
|
+
backgroundColor: isActive ? "var(--it-bento-ink-bg, #0f172a)" : "transparent",
|
|
75
|
+
color: isActive ? "var(--it-bento-ink-text, #ffffff)" : "var(--it-bento-control-text, #475569)",
|
|
76
|
+
}
|
|
77
|
+
: undefined
|
|
78
|
+
}
|
|
62
79
|
>
|
|
63
80
|
{opt.icon && <span>{opt.icon}</span>}
|
|
64
81
|
<ITText as="span">{opt.label}</ITText>
|
|
@@ -1,4 +1,16 @@
|
|
|
1
|
+
/** Pastel tint applied to a card through the `--color-<tone>-soft` token. */
|
|
2
|
+
export type ITCardTone =
|
|
3
|
+
| "primary"
|
|
4
|
+
| "secondary"
|
|
5
|
+
| "ternary"
|
|
6
|
+
| "success"
|
|
7
|
+
| "info"
|
|
8
|
+
| "warning"
|
|
9
|
+
| "danger";
|
|
10
|
+
|
|
1
11
|
export interface ITCardProps {
|
|
12
|
+
/** Pastel tile: tints the card background with the `--color-<tone>-soft` token of the palette, overriding the default white surface. In `soft`/`flat` appearances it also uses `--color-<tone>-soft-border` for the border; in `bento` the card stays borderless. Works in every appearance and in dark mode. Omit it for the regular surface. */
|
|
13
|
+
tone?: ITCardTone;
|
|
2
14
|
/** Click handler. When provided, the card becomes interactive with hover shadow effect. */
|
|
3
15
|
onClick?: () => void;
|
|
4
16
|
/** Card title displayed in the header area. */
|
|
@@ -2,11 +2,12 @@ import clsx from "clsx";
|
|
|
2
2
|
import { ITCardProps } from "./card.props";
|
|
3
3
|
import { useState } from "react";
|
|
4
4
|
import ITText from "@/components/atoms/text/text";
|
|
5
|
-
import {
|
|
5
|
+
import { useITAppearance } from "@/theme/theme-context";
|
|
6
6
|
import { useITTranslation } from "@/i18n";
|
|
7
7
|
|
|
8
8
|
/**
|
|
9
|
-
* Versatile card container with optional image, title, body content, and action footer. Supports interactive hover states
|
|
9
|
+
* Versatile card container with optional image, title, body content, and action footer. Supports interactive hover states
|
|
10
|
+
* and pastel `tone` tiles. Follows the provider `appearance` (`soft`, `flat`, `bento`).
|
|
10
11
|
*
|
|
11
12
|
* @example
|
|
12
13
|
* <ITCard title="Welcome" image="/hero.jpg" actions={<ITButton label="Learn more" />}>
|
|
@@ -14,12 +15,16 @@ import { useITTranslation } from "@/i18n";
|
|
|
14
15
|
* </ITCard>
|
|
15
16
|
*
|
|
16
17
|
* @example
|
|
18
|
+
* <ITCard tone="success" title="Revenue">+12.4%</ITCard>
|
|
19
|
+
*
|
|
20
|
+
* @example
|
|
17
21
|
* <ITCard onClick={() => {}} className="max-w-sm">
|
|
18
22
|
* Clickable card with hover shadow.
|
|
19
23
|
* </ITCard>
|
|
20
24
|
*/
|
|
21
25
|
export default function ITCard({
|
|
22
26
|
title,
|
|
27
|
+
tone,
|
|
23
28
|
headerActions,
|
|
24
29
|
image,
|
|
25
30
|
alt,
|
|
@@ -34,41 +39,66 @@ export default function ITCard({
|
|
|
34
39
|
}: ITCardProps) {
|
|
35
40
|
const { t } = useITTranslation();
|
|
36
41
|
const [isHovered, setIsHovered] = useState(false);
|
|
37
|
-
const
|
|
42
|
+
const appearance = useITAppearance();
|
|
43
|
+
const bento = appearance === "bento";
|
|
44
|
+
const flat = appearance === "flat";
|
|
38
45
|
const resolvedAlt = alt ?? t("card.imageAlt");
|
|
39
46
|
|
|
40
47
|
const containerStyle: React.CSSProperties = {
|
|
41
48
|
backgroundColor: "var(--it-card-bg, #ffffff)",
|
|
42
|
-
borderColor:
|
|
43
|
-
? "
|
|
44
|
-
:
|
|
45
|
-
|
|
49
|
+
borderColor: bento
|
|
50
|
+
? "transparent"
|
|
51
|
+
: flat
|
|
52
|
+
? "var(--it-card-flat-border, var(--color-secondary-200))"
|
|
53
|
+
: "var(--it-card-border, rgba(15, 23, 42, 0.06))",
|
|
54
|
+
borderWidth: bento ? "0px" : "1px",
|
|
46
55
|
borderStyle: "solid",
|
|
47
|
-
borderRadius:
|
|
48
|
-
? "var(--it-card-radius, var(--radius-
|
|
49
|
-
:
|
|
56
|
+
borderRadius: bento
|
|
57
|
+
? "var(--it-card-radius, var(--it-bento-card-radius, var(--radius-2xl)))"
|
|
58
|
+
: flat
|
|
59
|
+
? "var(--it-card-radius, var(--radius-lg))"
|
|
60
|
+
: "var(--it-card-radius, var(--radius-2xl))",
|
|
50
61
|
// Soft depth: every card floats a little at rest; interactive cards
|
|
51
62
|
// gain visual weight on hover instead of appearing flat until clicked.
|
|
52
63
|
// Flat appearance: the hairline border carries the surface, so a card
|
|
53
64
|
// only gets a shadow as hover feedback when it is clickable.
|
|
54
|
-
|
|
65
|
+
// Bento: borderless, a diffuse shadow at rest and a stronger one on hover.
|
|
66
|
+
boxShadow: bento
|
|
55
67
|
? onClick && isHovered
|
|
56
|
-
? "var(--shadow-
|
|
57
|
-
: "
|
|
58
|
-
:
|
|
59
|
-
? isHovered
|
|
60
|
-
? "var(--shadow-
|
|
61
|
-
: "
|
|
62
|
-
:
|
|
68
|
+
? "var(--it-bento-card-shadow-hover, var(--shadow-lg))"
|
|
69
|
+
: "var(--it-bento-card-shadow, var(--shadow-sm))"
|
|
70
|
+
: flat
|
|
71
|
+
? onClick && isHovered
|
|
72
|
+
? "var(--shadow-sm)"
|
|
73
|
+
: "none"
|
|
74
|
+
: onClick
|
|
75
|
+
? isHovered
|
|
76
|
+
? "var(--shadow-lg)"
|
|
77
|
+
: "var(--shadow-sm)"
|
|
78
|
+
: "var(--shadow-sm)",
|
|
63
79
|
transition: onClick ? "box-shadow 0.2s ease-in-out, transform 0.2s ease-in-out" : "none",
|
|
64
|
-
transform: onClick && isHovered && !flat ? "translateY(-2px)" : "translateY(0)",
|
|
80
|
+
transform: onClick && isHovered && !flat && !bento ? "translateY(-2px)" : "translateY(0)",
|
|
65
81
|
cursor: onClick ? "pointer" : "default",
|
|
66
82
|
};
|
|
67
83
|
|
|
84
|
+
// Pastel tile: the soft token is translucent, so it is layered over the
|
|
85
|
+
// regular surface (opaque tile, correct in light and dark).
|
|
86
|
+
if (tone) {
|
|
87
|
+
const soft = `var(--color-${tone}-soft)`;
|
|
88
|
+
containerStyle.backgroundImage = `linear-gradient(${soft}, ${soft})`;
|
|
89
|
+
if (!bento) containerStyle.borderColor = `var(--color-${tone}-soft-border)`;
|
|
90
|
+
}
|
|
91
|
+
|
|
68
92
|
const bodyStyle: React.CSSProperties = {
|
|
69
|
-
padding: "1.25rem",
|
|
93
|
+
padding: bento ? "var(--it-bento-card-padding, 1.5rem)" : "1.25rem",
|
|
70
94
|
};
|
|
71
95
|
|
|
96
|
+
const titleTypography = bento
|
|
97
|
+
? "text-lg font-bold"
|
|
98
|
+
: flat
|
|
99
|
+
? "text-base font-semibold"
|
|
100
|
+
: "text-xl font-semibold";
|
|
101
|
+
|
|
72
102
|
return (
|
|
73
103
|
<div
|
|
74
104
|
onClick={onClick}
|
|
@@ -103,7 +133,7 @@ export default function ITCard({
|
|
|
103
133
|
{title && (
|
|
104
134
|
<ITText
|
|
105
135
|
as="h3"
|
|
106
|
-
className={clsx(
|
|
136
|
+
className={clsx(titleTypography, titleClassName)}
|
|
107
137
|
>
|
|
108
138
|
{title}
|
|
109
139
|
</ITText>
|
|
@@ -113,7 +143,7 @@ export default function ITCard({
|
|
|
113
143
|
) : title ? (
|
|
114
144
|
<ITText
|
|
115
145
|
as="h3"
|
|
116
|
-
className={clsx(flat ? "
|
|
146
|
+
className={clsx(titleTypography, flat ? "mb-3" : bento ? "mb-4" : "mb-2", titleClassName)}
|
|
117
147
|
>
|
|
118
148
|
{title}
|
|
119
149
|
</ITText>
|
|
@@ -70,6 +70,8 @@ export default function ITChipInput({
|
|
|
70
70
|
style.alignItems = "center";
|
|
71
71
|
style.gap = "0.375rem";
|
|
72
72
|
style.minHeight = "2.5rem";
|
|
73
|
+
// Multi-line control: bento rounds it with a softer radius than the single-line pill.
|
|
74
|
+
style.borderRadius = "var(--it-bento-field-radius, var(--radius-md))";
|
|
73
75
|
|
|
74
76
|
if (disabled) {
|
|
75
77
|
style.backgroundColor = inputTheme.disabled?.backgroundColor || style.backgroundColor;
|
|
@@ -127,6 +127,8 @@ export default function ITMultiSelect({
|
|
|
127
127
|
style.alignItems = "center";
|
|
128
128
|
style.gap = "0.375rem";
|
|
129
129
|
style.minHeight = "2.5rem";
|
|
130
|
+
// Multi-line control: bento rounds it with a softer radius than the single-line pill.
|
|
131
|
+
style.borderRadius = "var(--it-bento-field-radius, var(--radius-md))";
|
|
130
132
|
|
|
131
133
|
if (disabled) {
|
|
132
134
|
style.backgroundColor = inputTheme.disabled?.backgroundColor || style.backgroundColor;
|
|
@@ -2,10 +2,12 @@ import React, { useState } from 'react';
|
|
|
2
2
|
import { ITTabsProps } from './tabs.props';
|
|
3
3
|
import { clsx } from 'clsx';
|
|
4
4
|
import ITText from "@/components/atoms/text/text";
|
|
5
|
+
import { useITAppearance } from "@/theme/theme-context";
|
|
5
6
|
|
|
6
7
|
/**
|
|
7
8
|
* Tabbed navigation component with "line" and "pill" visual variants.
|
|
8
9
|
* Manages its own active state internally via a defaultActiveId.
|
|
10
|
+
* With `appearance="bento"` the "pill" variant becomes a rounded track with an ink active pill.
|
|
9
11
|
*
|
|
10
12
|
* @example
|
|
11
13
|
* <ITTabs
|
|
@@ -25,6 +27,7 @@ const ITTabs: React.FC<ITTabsProps> = ({
|
|
|
25
27
|
className = '',
|
|
26
28
|
containerClassName = ''
|
|
27
29
|
}) => {
|
|
30
|
+
const bento = useITAppearance() === "bento" && variant === 'pill';
|
|
28
31
|
const [activeId, setActiveId] = useState(defaultActiveId || items[0]?.id);
|
|
29
32
|
|
|
30
33
|
const handleTabClick = (id: string, disabled?: boolean) => {
|
|
@@ -40,9 +43,10 @@ const ITTabs: React.FC<ITTabsProps> = ({
|
|
|
40
43
|
{/* HEADER */}
|
|
41
44
|
<div className={clsx(
|
|
42
45
|
"flex border-secondary-200 mb-4",
|
|
43
|
-
variant === 'line' ? "border-b" : "gap-2 p-1 bg-secondary-100 rounded-lg w-fit",
|
|
46
|
+
variant === 'line' ? "border-b" : bento ? "gap-2 p-1 rounded-full w-fit" : "gap-2 p-1 bg-secondary-100 rounded-lg w-fit",
|
|
44
47
|
className
|
|
45
|
-
)}
|
|
48
|
+
)}
|
|
49
|
+
style={bento ? { backgroundColor: "var(--it-bento-control-bg, #f1f5f9)", borderColor: "transparent" } : undefined}>
|
|
46
50
|
{items.map((item) => {
|
|
47
51
|
const isActive = item.id === activeId;
|
|
48
52
|
|
|
@@ -61,14 +65,24 @@ const ITTabs: React.FC<ITTabsProps> = ({
|
|
|
61
65
|
: "border-transparent text-secondary-500 hover:text-secondary-700 hover:border-secondary-300",
|
|
62
66
|
],
|
|
63
67
|
// PILL VARIANT
|
|
64
|
-
variant === 'pill' &&
|
|
65
|
-
"rounded-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
68
|
+
variant === 'pill' && (bento
|
|
69
|
+
? "rounded-full"
|
|
70
|
+
: [
|
|
71
|
+
"rounded-md",
|
|
72
|
+
isActive
|
|
73
|
+
? "bg-white text-primary-600 shadow-sm"
|
|
74
|
+
: "text-secondary-500 hover:text-secondary-700",
|
|
75
|
+
]),
|
|
70
76
|
item.disabled && "opacity-50 cursor-not-allowed"
|
|
71
77
|
)}
|
|
78
|
+
style={
|
|
79
|
+
bento
|
|
80
|
+
? {
|
|
81
|
+
backgroundColor: isActive ? "var(--it-bento-ink-bg, #0f172a)" : "transparent",
|
|
82
|
+
color: isActive ? "var(--it-bento-ink-text, #ffffff)" : "var(--it-bento-control-text, #475569)",
|
|
83
|
+
}
|
|
84
|
+
: undefined
|
|
85
|
+
}
|
|
72
86
|
>
|
|
73
87
|
{item.icon && <span className="w-4 h-4">{item.icon}</span>}
|
|
74
88
|
<ITText as="span">{item.label}</ITText>
|
|
@@ -147,6 +147,29 @@ export default function ITDataTable<T extends Record<string, unknown>>({
|
|
|
147
147
|
const isTableVirtual = virtualized && effectiveView === "table";
|
|
148
148
|
const isStickyHeader = stickyHeader && isTableVirtual;
|
|
149
149
|
const resolvedRowHeight = rowHeight ?? getRowHeight(size, density);
|
|
150
|
+
|
|
151
|
+
// La virtualización espacios filas de `resolvedRowHeight`; si el contenedor
|
|
152
|
+
// corta a una altura arbitraria, la última fila sale partida. Medimos el
|
|
153
|
+
// encabezado y recortamos a un múltiplo exacto de fila.
|
|
154
|
+
const headRef = useRef<HTMLTableSectionElement | null>(null);
|
|
155
|
+
const [headHeight, setHeadHeight] = useState(0);
|
|
156
|
+
useEffect(() => {
|
|
157
|
+
if (!isTableVirtual) return;
|
|
158
|
+
const el = headRef.current;
|
|
159
|
+
if (!el) return;
|
|
160
|
+
const measure = () => setHeadHeight(el.getBoundingClientRect().height);
|
|
161
|
+
measure();
|
|
162
|
+
if (typeof ResizeObserver === "undefined") return;
|
|
163
|
+
const ro = new ResizeObserver(measure);
|
|
164
|
+
ro.observe(el);
|
|
165
|
+
return () => ro.disconnect();
|
|
166
|
+
}, [isTableVirtual, resolvedRowHeight]);
|
|
167
|
+
|
|
168
|
+
const snappedMaxHeight =
|
|
169
|
+
isTableVirtual && headHeight > 0
|
|
170
|
+
? headHeight +
|
|
171
|
+
Math.max(1, Math.floor((virtualizedMaxHeight - headHeight) / resolvedRowHeight)) * resolvedRowHeight
|
|
172
|
+
: virtualizedMaxHeight;
|
|
150
173
|
const columnCount = columns.length;
|
|
151
174
|
const pinnedMeta = React.useMemo(() => getPinnedColumnMeta(columns), [columns]);
|
|
152
175
|
const scrollOuterClass = isTableVirtual
|
|
@@ -507,7 +530,7 @@ export default function ITDataTable<T extends Record<string, unknown>>({
|
|
|
507
530
|
))}
|
|
508
531
|
</colgroup>
|
|
509
532
|
)}
|
|
510
|
-
<thead>
|
|
533
|
+
<thead ref={headRef}>
|
|
511
534
|
<tr
|
|
512
535
|
className={clsx(tableHeaderRow, "dark:text-slate-200", headerSlot.className)}
|
|
513
536
|
style={headerSlot.style}
|
|
@@ -590,7 +613,10 @@ export default function ITDataTable<T extends Record<string, unknown>>({
|
|
|
590
613
|
onRowClick && !isLoading && tableRowClickable,
|
|
591
614
|
rowSlot.className
|
|
592
615
|
)}
|
|
593
|
-
style={
|
|
616
|
+
style={{
|
|
617
|
+
...(isTableVirtual ? { height: resolvedRowHeight } : null),
|
|
618
|
+
...rowSlot.style,
|
|
619
|
+
}}
|
|
594
620
|
tabIndex={onRowClick && !isLoading ? 0 : undefined}
|
|
595
621
|
onClick={onRowClick && !isLoading ? handleRowClick(row) : undefined}
|
|
596
622
|
onKeyDown={onRowClick && !isLoading ? handleRowKeyDown(row) : undefined}
|
|
@@ -740,7 +766,7 @@ export default function ITDataTable<T extends Record<string, unknown>>({
|
|
|
740
766
|
<div
|
|
741
767
|
ref={scrollRef}
|
|
742
768
|
className="overflow-x-auto overflow-y-auto"
|
|
743
|
-
style={{ maxHeight:
|
|
769
|
+
style={{ maxHeight: snappedMaxHeight }}
|
|
744
770
|
>
|
|
745
771
|
{tableEl}
|
|
746
772
|
</div>
|
|
@@ -2,7 +2,7 @@ import { FaChevronLeft } from "react-icons/fa";
|
|
|
2
2
|
import { ITPageHeaderProps } from "./page-header.props";
|
|
3
3
|
import ITBreadcrumbs from "@/components/molecules/breadcrumbs/breadcrumbs";
|
|
4
4
|
import ITText from "@/components/atoms/text/text";
|
|
5
|
-
import {
|
|
5
|
+
import { useITAppearance } from "@/theme/theme-context";
|
|
6
6
|
import { useITTranslation } from "@/i18n";
|
|
7
7
|
|
|
8
8
|
/**
|
|
@@ -33,7 +33,10 @@ export default function ITPageHeader({
|
|
|
33
33
|
const { t } = useITTranslation();
|
|
34
34
|
const showTopRow = breadcrumbs?.length || backAction;
|
|
35
35
|
// Flat appearance: compact title, no decorative icon tile.
|
|
36
|
-
|
|
36
|
+
// Bento: no icon tile either, but a larger tight title and a borderless round back button.
|
|
37
|
+
const appearance = useITAppearance();
|
|
38
|
+
const bento = appearance === "bento";
|
|
39
|
+
const flat = appearance === "flat";
|
|
37
40
|
|
|
38
41
|
return (
|
|
39
42
|
<div className={`${className} space-y-3`}>
|
|
@@ -44,7 +47,16 @@ export default function ITPageHeader({
|
|
|
44
47
|
{backAction && (
|
|
45
48
|
<button
|
|
46
49
|
onClick={backAction}
|
|
47
|
-
className=
|
|
50
|
+
className={
|
|
51
|
+
bento
|
|
52
|
+
? "flex items-center justify-center w-10 h-10 rounded-full hover:opacity-80 transition-opacity duration-200 flex-shrink-0"
|
|
53
|
+
: "flex items-center justify-center w-9 h-9 rounded-full border border-slate-200 dark:border-slate-700 text-slate-400 hover:text-slate-700 dark:hover:text-slate-200 hover:border-slate-300 dark:hover:border-slate-600 hover:bg-slate-50 dark:hover:bg-slate-800/60 transition-all duration-200 flex-shrink-0"
|
|
54
|
+
}
|
|
55
|
+
style={
|
|
56
|
+
bento
|
|
57
|
+
? { backgroundColor: "var(--it-bento-control-bg, #f1f5f9)", color: "var(--it-bento-control-text, #475569)" }
|
|
58
|
+
: undefined
|
|
59
|
+
}
|
|
48
60
|
aria-label={t("pageHeader.back")}
|
|
49
61
|
>
|
|
50
62
|
<FaChevronLeft size={14} />
|
|
@@ -65,7 +77,7 @@ export default function ITPageHeader({
|
|
|
65
77
|
{/* Fila principal: icono + título + descripción + (acciones si no hay top row) */}
|
|
66
78
|
<div className="flex items-start justify-between gap-6">
|
|
67
79
|
<div className="flex items-start gap-4 min-w-0">
|
|
68
|
-
{icon && !flat && (
|
|
80
|
+
{icon && !flat && !bento && (
|
|
69
81
|
<div
|
|
70
82
|
className="flex items-center justify-center w-12 h-12 rounded-full border border-slate-200 dark:border-slate-700 bg-slate-50/50 dark:bg-slate-800/50 text-slate-600 dark:text-slate-300 flex-shrink-0 transition-all duration-200 hover:border-slate-300 dark:hover:border-slate-500 hover:shadow-sm"
|
|
71
83
|
style={iconColor ? { color: iconColor } : undefined}
|
|
@@ -77,7 +89,9 @@ export default function ITPageHeader({
|
|
|
77
89
|
<ITText
|
|
78
90
|
as="h1"
|
|
79
91
|
className={
|
|
80
|
-
|
|
92
|
+
bento
|
|
93
|
+
? "text-3xl font-bold text-slate-900 dark:text-white tracking-tighter leading-tight"
|
|
94
|
+
: flat
|
|
81
95
|
? "text-[1.625rem] font-bold text-slate-900 dark:text-white tracking-tight leading-tight"
|
|
82
96
|
: "text-3xl font-bold text-slate-800 dark:text-white tracking-tight leading-tight"
|
|
83
97
|
}
|
|
@@ -88,7 +102,9 @@ export default function ITPageHeader({
|
|
|
88
102
|
<ITText
|
|
89
103
|
as="p"
|
|
90
104
|
className={
|
|
91
|
-
|
|
105
|
+
bento
|
|
106
|
+
? "text-sm font-normal text-slate-500 dark:text-slate-400 mt-1.5 leading-relaxed"
|
|
107
|
+
: flat
|
|
92
108
|
? "text-sm font-normal text-slate-500 dark:text-slate-400 mt-0.5 leading-relaxed"
|
|
93
109
|
: "text-sm font-light text-slate-500 dark:text-slate-400 mt-1 leading-relaxed"
|
|
94
110
|
}
|
|
@@ -10,7 +10,7 @@ import {
|
|
|
10
10
|
} from "./sidebar.props";
|
|
11
11
|
import ITBadget from "@/components/atoms/badget/badget";
|
|
12
12
|
import ITText from "@/components/atoms/text/text";
|
|
13
|
-
import {
|
|
13
|
+
import { useITAppearance } from "@/theme/theme-context";
|
|
14
14
|
import { useITTranslation } from "@/i18n";
|
|
15
15
|
|
|
16
16
|
/** Narrows a submenu entry to a titled group. */
|
|
@@ -95,7 +95,10 @@ export default function ITSidebar({
|
|
|
95
95
|
expandOnHover = true,
|
|
96
96
|
}: ITSidebarProps) {
|
|
97
97
|
// Flat appearance: plain surface, no glow/accent bar, narrower panel.
|
|
98
|
-
|
|
98
|
+
// Bento shares that structure; its tokens make the active item a neutral pill with bold ink text.
|
|
99
|
+
const appearance = useITAppearance();
|
|
100
|
+
const bento = appearance === "bento";
|
|
101
|
+
const flat = appearance === "flat" || bento;
|
|
99
102
|
const { t } = useITTranslation();
|
|
100
103
|
const [expandedItems, setExpandedItems] = useState<Set<string>>(new Set());
|
|
101
104
|
const [isHovering, setIsHovering] = useState(false);
|
|
@@ -202,7 +205,7 @@ export default function ITSidebar({
|
|
|
202
205
|
color: subitem.isActive ? "var(--it-sidebar-active-color, var(--color-secondary-900))" : "var(--it-sidebar-label-color, var(--color-secondary-600))",
|
|
203
206
|
backgroundColor: subitem.isActive ? "var(--it-sidebar-active-bg, var(--color-secondary-50))" : 'transparent',
|
|
204
207
|
fontSize: flat ? '0.84rem' : '0.78rem',
|
|
205
|
-
fontWeight: subitem.isActive ? 600 : 500,
|
|
208
|
+
fontWeight: subitem.isActive ? (bento ? 700 : 600) : 500,
|
|
206
209
|
letterSpacing: '0.01em',
|
|
207
210
|
marginLeft: subitemConnector === '|' ? '-1px' : '0',
|
|
208
211
|
}}
|
|
@@ -365,7 +368,7 @@ export default function ITSidebar({
|
|
|
365
368
|
style={{
|
|
366
369
|
zIndex: 50,
|
|
367
370
|
backgroundColor: "var(--it-sidebar-bg, rgba(255, 255, 255, 0.90))",
|
|
368
|
-
borderRight: "1px solid var(--it-sidebar-border, var(--color-secondary-200))",
|
|
371
|
+
borderRight: bento ? "none" : "1px solid var(--it-sidebar-border, var(--color-secondary-200))",
|
|
369
372
|
...(flat ? {} : { WebkitBackdropFilter: 'blur(12px)', backdropFilter: 'blur(12px)' }),
|
|
370
373
|
}}
|
|
371
374
|
>
|
|
@@ -480,7 +483,7 @@ export default function ITSidebar({
|
|
|
480
483
|
style={{
|
|
481
484
|
color: active ? "var(--it-sidebar-active-color, #ffffff)" : "var(--it-sidebar-label-color, var(--color-secondary-300))",
|
|
482
485
|
fontSize: flat ? '0.875rem' : '0.8rem',
|
|
483
|
-
fontWeight: active ? '600' : '500'
|
|
486
|
+
fontWeight: active ? (bento ? '700' : '600') : '500'
|
|
484
487
|
}}
|
|
485
488
|
>
|
|
486
489
|
{item.label}
|
|
@@ -7,6 +7,8 @@ export interface ITTopBarNavItem {
|
|
|
7
7
|
label: string;
|
|
8
8
|
/** Optional icon element rendered beside the label. */
|
|
9
9
|
icon?: any;
|
|
10
|
+
/** Marks the item as the current one. Sets `aria-current="page"`; with `appearance="bento"` it renders as the ink pill (dark fill, white text), in the other appearances as a subtle highlight. @default false */
|
|
11
|
+
isActive?: boolean;
|
|
10
12
|
/** Click handler for this navigation item (legacy, prefer onNavItemClick). */
|
|
11
13
|
action: () => void;
|
|
12
14
|
}
|