@gtivr4/a1-design-system-react 0.8.0 → 0.10.0
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/package.json +1 -1
- package/src/color-scheme.css +9 -0
- package/src/components/bottom-drawer/BottomDrawer.d.ts +34 -0
- package/src/components/bottom-drawer/BottomDrawer.jsx +55 -0
- package/src/components/bottom-drawer/bottom-drawer.css +138 -0
- package/src/components/card/card.css +1 -1
- package/src/components/checkbox-group/checkbox-group.css +1 -1
- package/src/components/choice-group/choice-group.css +2 -2
- package/src/components/data-table/data-table.css +11 -0
- package/src/components/field/field.css +1 -1
- package/src/components/fieldset/fieldset.css +1 -1
- package/src/components/inline-editable/inline-editable.css +1 -1
- package/src/components/radio-group/radio-group.css +1 -1
- package/src/components/switch/switch.css +1 -1
- package/src/components/top-header/TopHeader.jsx +11 -10
- package/src/components/top-header/top-header.css +10 -4
- package/src/index.js +1 -0
- package/src/themes.css +2 -0
- package/src/tokens.css +6 -0
package/package.json
CHANGED
package/src/color-scheme.css
CHANGED
|
@@ -43,6 +43,8 @@ body {
|
|
|
43
43
|
.a1-inverse {
|
|
44
44
|
color-scheme: dark;
|
|
45
45
|
--semantic-color-surface-page: var(--base-color-neutral-900);
|
|
46
|
+
--semantic-color-surface-card: var(--base-color-neutral-800);
|
|
47
|
+
--semantic-color-surface-field: var(--base-color-neutral-700);
|
|
46
48
|
--semantic-color-surface-panel: var(--base-color-neutral-800);
|
|
47
49
|
--semantic-color-surface-raised: var(--base-color-neutral-700);
|
|
48
50
|
--semantic-color-text-default: var(--base-color-neutral-50);
|
|
@@ -122,6 +124,7 @@ body {
|
|
|
122
124
|
:root {
|
|
123
125
|
color-scheme: dark;
|
|
124
126
|
--semantic-color-surface-page: var(--base-color-neutral-900);
|
|
127
|
+
--semantic-color-surface-card: var(--base-color-neutral-800);
|
|
125
128
|
--semantic-color-surface-panel: var(--base-color-neutral-800);
|
|
126
129
|
--semantic-color-surface-raised: var(--base-color-neutral-700);
|
|
127
130
|
--semantic-color-text-default: var(--base-color-neutral-50);
|
|
@@ -196,6 +199,7 @@ body {
|
|
|
196
199
|
.a1-inverse {
|
|
197
200
|
color-scheme: light;
|
|
198
201
|
--semantic-color-surface-page: var(--base-color-neutral-0);
|
|
202
|
+
--semantic-color-surface-card: var(--base-color-neutral-0);
|
|
199
203
|
--semantic-color-surface-panel: var(--base-color-neutral-50);
|
|
200
204
|
--semantic-color-surface-raised: var(--base-color-neutral-100);
|
|
201
205
|
--semantic-color-text-default: var(--base-color-neutral-900);
|
|
@@ -273,6 +277,8 @@ body {
|
|
|
273
277
|
html.a1-theme-dark {
|
|
274
278
|
color-scheme: dark;
|
|
275
279
|
--semantic-color-surface-page: var(--base-color-neutral-900);
|
|
280
|
+
--semantic-color-surface-card: var(--base-color-neutral-800);
|
|
281
|
+
--semantic-color-surface-field: var(--base-color-neutral-700);
|
|
276
282
|
--semantic-color-surface-panel: var(--base-color-neutral-800);
|
|
277
283
|
--semantic-color-surface-raised: var(--base-color-neutral-700);
|
|
278
284
|
--semantic-color-text-default: var(--base-color-neutral-50);
|
|
@@ -348,6 +354,8 @@ html.a1-theme-dark .a1-inverse,
|
|
|
348
354
|
html.a1-theme-dark .a1-theme-light {
|
|
349
355
|
color-scheme: light;
|
|
350
356
|
--semantic-color-surface-page: var(--base-color-neutral-0);
|
|
357
|
+
--semantic-color-surface-card: var(--base-color-neutral-0);
|
|
358
|
+
--semantic-color-surface-field: var(--base-color-neutral-0);
|
|
351
359
|
--semantic-color-surface-panel: var(--base-color-neutral-50);
|
|
352
360
|
--semantic-color-surface-raised: var(--base-color-neutral-100);
|
|
353
361
|
--semantic-color-text-default: var(--base-color-neutral-900);
|
|
@@ -512,6 +520,7 @@ html.a1-theme-light {
|
|
|
512
520
|
html.a1-theme-light .a1-inverse {
|
|
513
521
|
color-scheme: dark;
|
|
514
522
|
--semantic-color-surface-page: var(--base-color-neutral-900);
|
|
523
|
+
--semantic-color-surface-field: var(--base-color-neutral-700);
|
|
515
524
|
--semantic-color-surface-panel: var(--base-color-neutral-800);
|
|
516
525
|
--semantic-color-surface-raised: var(--base-color-neutral-700);
|
|
517
526
|
--semantic-color-text-default: var(--base-color-neutral-50);
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { ReactNode } from "react";
|
|
2
|
+
|
|
3
|
+
export interface BottomDrawerItem {
|
|
4
|
+
/** Unique identifier for the item. */
|
|
5
|
+
id: string;
|
|
6
|
+
/** Display label shown below the icon. */
|
|
7
|
+
label: string;
|
|
8
|
+
/** Material Symbols icon name. */
|
|
9
|
+
icon: string;
|
|
10
|
+
/** Navigate to this URL — renders an <a> element. */
|
|
11
|
+
href?: string;
|
|
12
|
+
/** Click handler — used when no href is provided; renders a <button>. */
|
|
13
|
+
onClick?: () => void;
|
|
14
|
+
/** Marks this item as the currently active destination. */
|
|
15
|
+
active?: boolean;
|
|
16
|
+
/** Numeric badge count shown on the icon (capped at 99+). */
|
|
17
|
+
badge?: number;
|
|
18
|
+
/** Disables the item. */
|
|
19
|
+
disabled?: boolean;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export interface BottomDrawerProps {
|
|
23
|
+
/**
|
|
24
|
+
* Up to 5 navigation items. Additional items beyond 5 are silently ignored.
|
|
25
|
+
*/
|
|
26
|
+
items: BottomDrawerItem[];
|
|
27
|
+
/**
|
|
28
|
+
* Accessible name for the nav element.
|
|
29
|
+
* @default "Primary navigation"
|
|
30
|
+
*/
|
|
31
|
+
"aria-label"?: string;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export declare function BottomDrawer(props: BottomDrawerProps): ReactNode;
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { Icon } from "../icon/Icon.jsx";
|
|
2
|
+
import "./bottom-drawer.css";
|
|
3
|
+
|
|
4
|
+
export function BottomDrawer({ items = [], "aria-label": ariaLabel = "Primary navigation", className = "" }) {
|
|
5
|
+
const visibleItems = items.slice(0, 5);
|
|
6
|
+
|
|
7
|
+
return (
|
|
8
|
+
<nav className={["a1-bottom-drawer", className].filter(Boolean).join(" ")} aria-label={ariaLabel}>
|
|
9
|
+
<ul className="a1-bottom-drawer__list" role="list">
|
|
10
|
+
{visibleItems.map((item) => {
|
|
11
|
+
const Tag = item.href ? "a" : "button";
|
|
12
|
+
const tagProps = item.href
|
|
13
|
+
? { href: item.href }
|
|
14
|
+
: { type: "button", onClick: item.onClick };
|
|
15
|
+
|
|
16
|
+
const linkClass = [
|
|
17
|
+
"a1-bottom-drawer__link",
|
|
18
|
+
item.active && "a1-bottom-drawer__link--active",
|
|
19
|
+
item.disabled && "a1-bottom-drawer__link--disabled",
|
|
20
|
+
]
|
|
21
|
+
.filter(Boolean)
|
|
22
|
+
.join(" ");
|
|
23
|
+
|
|
24
|
+
return (
|
|
25
|
+
<li key={item.id} className="a1-bottom-drawer__item">
|
|
26
|
+
<Tag
|
|
27
|
+
className={linkClass}
|
|
28
|
+
aria-current={item.active ? "page" : undefined}
|
|
29
|
+
aria-disabled={item.disabled ? "true" : undefined}
|
|
30
|
+
{...tagProps}
|
|
31
|
+
>
|
|
32
|
+
<span className="a1-bottom-drawer__link-icon-wrap">
|
|
33
|
+
<Icon
|
|
34
|
+
name={item.icon}
|
|
35
|
+
className="a1-bottom-drawer__link-icon"
|
|
36
|
+
aria-hidden="true"
|
|
37
|
+
/>
|
|
38
|
+
{item.badge > 0 && (
|
|
39
|
+
<span
|
|
40
|
+
className="a1-bottom-drawer__badge"
|
|
41
|
+
aria-label={`${item.badge} unread`}
|
|
42
|
+
>
|
|
43
|
+
{item.badge > 99 ? "99+" : item.badge}
|
|
44
|
+
</span>
|
|
45
|
+
)}
|
|
46
|
+
</span>
|
|
47
|
+
<span className="a1-bottom-drawer__link-label">{item.label}</span>
|
|
48
|
+
</Tag>
|
|
49
|
+
</li>
|
|
50
|
+
);
|
|
51
|
+
})}
|
|
52
|
+
</ul>
|
|
53
|
+
</nav>
|
|
54
|
+
);
|
|
55
|
+
}
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
/* ═══════════════════════════════════════════════════════════════════════════
|
|
2
|
+
BottomDrawer
|
|
3
|
+
Fixed bottom navigation bar — up to 5 items, icon stacked above label.
|
|
4
|
+
Mirrors the TopHeader icon-above item style via shared --a1-nav-stacked-*
|
|
5
|
+
custom properties.
|
|
6
|
+
═══════════════════════════════════════════════════════════════════════════ */
|
|
7
|
+
|
|
8
|
+
/* ── Shared stacked-nav-item variables ───────────────────────────────────── */
|
|
9
|
+
/* These same names are referenced in top-header.css icon-above mode so both
|
|
10
|
+
components stay in sync when tokens change. */
|
|
11
|
+
|
|
12
|
+
:root {
|
|
13
|
+
--a1-nav-stacked-icon-size: var(--semantic-font-size-heading-sm);
|
|
14
|
+
--a1-nav-stacked-label-size: var(--semantic-font-size-body-xs);
|
|
15
|
+
--a1-nav-stacked-gap: var(--base-spacing-2);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/* ── Shell ────────────────────────────────────────────────────────────────── */
|
|
19
|
+
|
|
20
|
+
.a1-bottom-drawer {
|
|
21
|
+
position: fixed;
|
|
22
|
+
inset-block-end: 0;
|
|
23
|
+
inset-inline: 0;
|
|
24
|
+
z-index: var(--component-bottom-drawer-z-index);
|
|
25
|
+
display: flex;
|
|
26
|
+
align-items: stretch;
|
|
27
|
+
min-block-size: var(--component-bottom-drawer-height);
|
|
28
|
+
padding-block-end: env(safe-area-inset-bottom, 0px);
|
|
29
|
+
background: var(--semantic-color-surface-page);
|
|
30
|
+
border-block-start: var(--component-bottom-drawer-border-width) solid var(--semantic-color-border-subtle);
|
|
31
|
+
box-shadow: 0 -4px 12px color-mix(in srgb, var(--semantic-color-text-default) 8%, transparent);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/* ── Item list ────────────────────────────────────────────────────────────── */
|
|
35
|
+
|
|
36
|
+
.a1-bottom-drawer__list {
|
|
37
|
+
display: flex;
|
|
38
|
+
align-items: stretch;
|
|
39
|
+
width: 100%;
|
|
40
|
+
list-style: none;
|
|
41
|
+
margin: 0;
|
|
42
|
+
padding: 0;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
.a1-bottom-drawer__item {
|
|
46
|
+
flex: 1;
|
|
47
|
+
display: flex;
|
|
48
|
+
align-items: stretch;
|
|
49
|
+
min-inline-size: 0;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/* ── Nav link — icon above label ─────────────────────────────────────────── */
|
|
53
|
+
|
|
54
|
+
.a1-bottom-drawer__link {
|
|
55
|
+
display: inline-flex;
|
|
56
|
+
flex-direction: column;
|
|
57
|
+
align-items: center;
|
|
58
|
+
justify-content: center;
|
|
59
|
+
gap: var(--a1-nav-stacked-gap);
|
|
60
|
+
width: 100%;
|
|
61
|
+
padding-block: var(--base-spacing-8);
|
|
62
|
+
padding-inline: var(--base-spacing-4);
|
|
63
|
+
color: var(--semantic-color-text-muted);
|
|
64
|
+
font-family: var(--component-paragraph-font-family);
|
|
65
|
+
font-size: var(--a1-nav-stacked-label-size);
|
|
66
|
+
font-weight: 500;
|
|
67
|
+
line-height: 1;
|
|
68
|
+
text-decoration: none;
|
|
69
|
+
background: transparent;
|
|
70
|
+
border: none;
|
|
71
|
+
cursor: pointer;
|
|
72
|
+
transition:
|
|
73
|
+
color var(--semantic-motion-duration-fast) var(--semantic-motion-easing-standard),
|
|
74
|
+
background var(--semantic-motion-duration-fast) var(--semantic-motion-easing-standard);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
.a1-bottom-drawer__link:hover:not(.a1-bottom-drawer__link--disabled) {
|
|
78
|
+
color: var(--semantic-color-text-default);
|
|
79
|
+
background: var(--semantic-color-surface-raised);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
.a1-bottom-drawer__link:focus-visible {
|
|
83
|
+
outline: 2px solid var(--semantic-color-action-background);
|
|
84
|
+
outline-offset: -2px;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
.a1-bottom-drawer__link--active {
|
|
88
|
+
color: var(--semantic-color-action-background);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
.a1-bottom-drawer__link--disabled {
|
|
92
|
+
opacity: var(--component-button-disabled-opacity);
|
|
93
|
+
cursor: not-allowed;
|
|
94
|
+
pointer-events: none;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/* ── Icon ─────────────────────────────────────────────────────────────────── */
|
|
98
|
+
|
|
99
|
+
.a1-bottom-drawer__link-icon-wrap {
|
|
100
|
+
position: relative;
|
|
101
|
+
display: inline-flex;
|
|
102
|
+
flex-shrink: 0;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
.a1-bottom-drawer__link-icon {
|
|
106
|
+
font-size: var(--a1-nav-stacked-icon-size);
|
|
107
|
+
flex-shrink: 0;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/* ── Badge ────────────────────────────────────────────────────────────────── */
|
|
111
|
+
|
|
112
|
+
.a1-bottom-drawer__badge {
|
|
113
|
+
position: absolute;
|
|
114
|
+
top: -3px;
|
|
115
|
+
right: -6px;
|
|
116
|
+
min-inline-size: 16px;
|
|
117
|
+
block-size: 16px;
|
|
118
|
+
padding-inline: var(--base-spacing-4);
|
|
119
|
+
background: var(--semantic-color-status-error-background);
|
|
120
|
+
color: var(--semantic-color-status-error-foreground);
|
|
121
|
+
border-radius: 8px;
|
|
122
|
+
font-family: var(--component-paragraph-font-family);
|
|
123
|
+
font-size: var(--component-tab-count-font-size, 10px);
|
|
124
|
+
font-weight: 700;
|
|
125
|
+
line-height: 16px;
|
|
126
|
+
text-align: center;
|
|
127
|
+
white-space: nowrap;
|
|
128
|
+
pointer-events: none;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/* ── Label ────────────────────────────────────────────────────────────────── */
|
|
132
|
+
|
|
133
|
+
.a1-bottom-drawer__link-label {
|
|
134
|
+
overflow: hidden;
|
|
135
|
+
text-overflow: ellipsis;
|
|
136
|
+
white-space: nowrap;
|
|
137
|
+
max-inline-size: 100%;
|
|
138
|
+
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
.a1-card {
|
|
2
2
|
container: a1-card / inline-size;
|
|
3
3
|
box-sizing: border-box;
|
|
4
|
-
background: var(--semantic-color-surface-
|
|
4
|
+
background: var(--semantic-color-surface-card);
|
|
5
5
|
border: var(--component-card-border-width) solid var(--semantic-color-border-subtle);
|
|
6
6
|
border-radius: var(--component-card-border-radius);
|
|
7
7
|
padding: var(--component-card-padding);
|
|
@@ -177,7 +177,7 @@
|
|
|
177
177
|
margin-top: var(--a1-cb-input-nudge);
|
|
178
178
|
border: var(--component-field-border-width) solid var(--semantic-color-border-strong);
|
|
179
179
|
border-radius: var(--a1-cb-radius);
|
|
180
|
-
background-color: var(--semantic-color-surface-
|
|
180
|
+
background-color: var(--semantic-color-surface-field);
|
|
181
181
|
cursor: pointer;
|
|
182
182
|
transition:
|
|
183
183
|
border-color var(--semantic-motion-duration-fast),
|
|
@@ -161,7 +161,7 @@
|
|
|
161
161
|
padding-inline-start: calc(var(--a1-cg-padding) + var(--a1-cg-indicator-size) + var(--base-spacing-8));
|
|
162
162
|
border: var(--component-choice-group-border-width) solid var(--semantic-color-border-subtle);
|
|
163
163
|
border-radius: var(--component-choice-group-border-radius);
|
|
164
|
-
background-color: var(--semantic-color-surface-
|
|
164
|
+
background-color: var(--semantic-color-surface-field);
|
|
165
165
|
cursor: pointer;
|
|
166
166
|
transition:
|
|
167
167
|
border-color var(--semantic-motion-duration-fast) var(--semantic-motion-easing-standard),
|
|
@@ -271,7 +271,7 @@
|
|
|
271
271
|
width: var(--a1-cg-indicator-size);
|
|
272
272
|
height: var(--a1-cg-indicator-size);
|
|
273
273
|
border: 1.5px solid var(--semantic-color-border-default);
|
|
274
|
-
background-color: var(--semantic-color-surface-
|
|
274
|
+
background-color: var(--semantic-color-surface-field);
|
|
275
275
|
background-repeat: no-repeat;
|
|
276
276
|
background-position: center;
|
|
277
277
|
transition:
|
|
@@ -433,4 +433,15 @@
|
|
|
433
433
|
flex-direction: column;
|
|
434
434
|
align-items: flex-start;
|
|
435
435
|
}
|
|
436
|
+
|
|
437
|
+
/* Notice rows span the full card width — suppress the column-label ::before
|
|
438
|
+
and reset td to block so the content fills edge-to-edge. */
|
|
439
|
+
.a1-data-table__notice-row td {
|
|
440
|
+
display: block;
|
|
441
|
+
padding: 0;
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
.a1-data-table__notice-row td::before {
|
|
445
|
+
display: none;
|
|
446
|
+
}
|
|
436
447
|
}
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
--a1-field-accent-compensation: var(--component-field-accent-compensation);
|
|
17
17
|
|
|
18
18
|
/* interaction — hover/active/readonly values come from :root in color-scheme.css */
|
|
19
|
-
--a1-field-background: var(--semantic-color-surface-
|
|
19
|
+
--a1-field-background: var(--semantic-color-surface-field);
|
|
20
20
|
--a1-field-border-color: var(--semantic-color-border-strong);
|
|
21
21
|
--a1-field-focus-ring-color: var(--component-field-focus-ring-color);
|
|
22
22
|
--a1-field-focus-ring-width: var(--component-field-focus-ring-width);
|
|
@@ -51,7 +51,7 @@
|
|
|
51
51
|
/* ─── Surface variant ────────────────────────────────────────────────────── */
|
|
52
52
|
|
|
53
53
|
.a1-fieldset--surface {
|
|
54
|
-
background: var(--semantic-color-surface-
|
|
54
|
+
background: var(--semantic-color-surface-card);
|
|
55
55
|
border: var(--component-card-border-width) solid var(--semantic-color-border-subtle);
|
|
56
56
|
border-radius: var(--component-card-border-radius);
|
|
57
57
|
box-shadow: var(--component-card-shadow);
|
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
display: block;
|
|
30
30
|
width: 100%;
|
|
31
31
|
min-height: 2em;
|
|
32
|
-
background: var(--semantic-color-surface-
|
|
32
|
+
background: var(--semantic-color-surface-field);
|
|
33
33
|
border: 1px solid var(--semantic-color-border-strong);
|
|
34
34
|
border-radius: var(--base-border-radius-sm);
|
|
35
35
|
padding: var(--base-spacing-4) var(--base-spacing-8);
|
|
@@ -174,7 +174,7 @@
|
|
|
174
174
|
margin-top: var(--a1-rb-input-nudge);
|
|
175
175
|
border: var(--component-field-border-width) solid var(--semantic-color-border-strong);
|
|
176
176
|
border-radius: 50%;
|
|
177
|
-
background-color: var(--semantic-color-surface-
|
|
177
|
+
background-color: var(--semantic-color-surface-field);
|
|
178
178
|
cursor: pointer;
|
|
179
179
|
transition:
|
|
180
180
|
border-color var(--semantic-motion-duration-fast),
|
|
@@ -150,7 +150,7 @@
|
|
|
150
150
|
height: var(--a1-sw-track-height);
|
|
151
151
|
border-radius: var(--a1-sw-track-height);
|
|
152
152
|
border: var(--component-field-border-width) solid var(--semantic-color-border-strong);
|
|
153
|
-
background-color: var(--semantic-color-surface-
|
|
153
|
+
background-color: var(--semantic-color-surface-field);
|
|
154
154
|
cursor: pointer;
|
|
155
155
|
transition:
|
|
156
156
|
background-color var(--semantic-motion-duration-normal) var(--semantic-motion-easing-standard),
|
|
@@ -17,9 +17,9 @@ const BP_QUERIES = {
|
|
|
17
17
|
};
|
|
18
18
|
|
|
19
19
|
// Resolve a scalar or responsive { xs?, sm?, md?, lg?, xl? } navIconPosition
|
|
20
|
-
// value to
|
|
21
|
-
function
|
|
22
|
-
if (!prop || typeof prop === "string") return prop
|
|
20
|
+
// value to the mode active at the current viewport: "start" | "above" | "hidden".
|
|
21
|
+
function resolveNavMode(prop) {
|
|
22
|
+
if (!prop || typeof prop === "string") return prop ?? "start";
|
|
23
23
|
// Cascade xs → sm → md → lg → xl, carrying forward the last explicit value.
|
|
24
24
|
let resolved = prop.xs ?? "start";
|
|
25
25
|
for (const [bp, query] of Object.entries(BP_QUERIES)) {
|
|
@@ -27,7 +27,7 @@ function resolveIconAbove(prop) {
|
|
|
27
27
|
resolved = prop[bp] ?? resolved;
|
|
28
28
|
}
|
|
29
29
|
}
|
|
30
|
-
return resolved
|
|
30
|
+
return resolved; // "start" | "above" | "hidden"
|
|
31
31
|
}
|
|
32
32
|
|
|
33
33
|
// Split a flat items array into sections separated by { divider: true } markers.
|
|
@@ -589,7 +589,7 @@ export function TopHeader({
|
|
|
589
589
|
navIconPosition = "start",
|
|
590
590
|
className = "",
|
|
591
591
|
}) {
|
|
592
|
-
const [
|
|
592
|
+
const [navMode, setNavMode] = useState(() => resolveNavMode(navIconPosition));
|
|
593
593
|
const [openSubmenu, setOpenSubmenu] = useState(null);
|
|
594
594
|
const [openAction, setOpenAction] = useState(null);
|
|
595
595
|
const [mobileNavOpen, setMobileNavOpen] = useState(false);
|
|
@@ -617,11 +617,11 @@ export function TopHeader({
|
|
|
617
617
|
return () => desktopQuery.removeListener(closeAtDesktop);
|
|
618
618
|
}, [mobileNavOpen]);
|
|
619
619
|
|
|
620
|
-
// Re-resolve
|
|
620
|
+
// Re-resolve navMode whenever navIconPosition or the viewport changes.
|
|
621
621
|
useEffect(() => {
|
|
622
622
|
if (typeof window === "undefined" || !window.matchMedia) return undefined;
|
|
623
623
|
|
|
624
|
-
const update = () =>
|
|
624
|
+
const update = () => setNavMode(resolveNavMode(navIconPosition));
|
|
625
625
|
const listeners = Object.values(BP_QUERIES).map((q) => {
|
|
626
626
|
const mq = window.matchMedia(q);
|
|
627
627
|
mq.addEventListener("change", update);
|
|
@@ -637,7 +637,8 @@ export function TopHeader({
|
|
|
637
637
|
<header
|
|
638
638
|
className={[
|
|
639
639
|
"a1-top-header",
|
|
640
|
-
|
|
640
|
+
navMode === "above" && "a1-top-header--nav-icon-above",
|
|
641
|
+
navMode === "hidden" && "a1-top-header--nav-hidden",
|
|
641
642
|
className,
|
|
642
643
|
].filter(Boolean).join(" ")}
|
|
643
644
|
>
|
|
@@ -666,7 +667,7 @@ export function TopHeader({
|
|
|
666
667
|
item={item}
|
|
667
668
|
openId={openSubmenu}
|
|
668
669
|
onOpen={setOpenSubmenu}
|
|
669
|
-
iconAbove={
|
|
670
|
+
iconAbove={navMode === "above"}
|
|
670
671
|
/>
|
|
671
672
|
))}
|
|
672
673
|
</ul>
|
|
@@ -698,7 +699,7 @@ export function TopHeader({
|
|
|
698
699
|
</div>
|
|
699
700
|
</header>
|
|
700
701
|
|
|
701
|
-
{mobileNavOpen && (
|
|
702
|
+
{mobileNavOpen && navMode !== "hidden" && (
|
|
702
703
|
<MobileDrawer
|
|
703
704
|
navItems={navItems}
|
|
704
705
|
onClose={() => setMobileNavOpen(false)}
|
|
@@ -322,21 +322,27 @@
|
|
|
322
322
|
|
|
323
323
|
/* ── Icon-above nav layout ────────────────────────────────────────────────── */
|
|
324
324
|
|
|
325
|
-
/* Nav links become a column: icon above, label (+ optional chevron) below.
|
|
325
|
+
/* Nav links become a column: icon above, label (+ optional chevron) below.
|
|
326
|
+
Uses the same --a1-nav-stacked-* variables as BottomDrawer so both stay
|
|
327
|
+
visually in sync. */
|
|
326
328
|
.a1-top-header--nav-icon-above .a1-top-header__nav-link {
|
|
327
329
|
flex-direction: column;
|
|
328
330
|
align-items: center;
|
|
329
|
-
gap: var(--base-spacing-2);
|
|
331
|
+
gap: var(--a1-nav-stacked-gap, var(--base-spacing-2));
|
|
330
332
|
padding-block: var(--base-spacing-8);
|
|
331
333
|
padding-inline: var(--base-spacing-8);
|
|
332
|
-
font-size: var(--semantic-font-size-body-xs);
|
|
334
|
+
font-size: var(--a1-nav-stacked-label-size, var(--semantic-font-size-body-xs));
|
|
333
335
|
}
|
|
334
336
|
|
|
335
337
|
/* Icon slightly larger so it reads clearly at the top of each item. */
|
|
336
338
|
.a1-top-header--nav-icon-above .a1-top-header__nav-link-icon {
|
|
337
|
-
font-size: var(--semantic-font-size-heading-sm);
|
|
339
|
+
font-size: var(--a1-nav-stacked-icon-size, var(--semantic-font-size-heading-sm));
|
|
338
340
|
}
|
|
339
341
|
|
|
342
|
+
/* Nav-hidden mode: BottomDrawer provides nav — suppress hamburger and nav entirely. */
|
|
343
|
+
.a1-top-header--nav-hidden .a1-top-header__hamburger { display: none; }
|
|
344
|
+
.a1-top-header--nav-hidden .a1-top-header__nav { display: none; }
|
|
345
|
+
|
|
340
346
|
/* Label row: inline-flex so the small chevron sits beside the text. */
|
|
341
347
|
.a1-top-header--nav-icon-above .a1-top-header__nav-link-label {
|
|
342
348
|
display: inline-flex;
|
package/src/index.js
CHANGED
|
@@ -54,6 +54,7 @@ export { LabelsProvider, useLabel } from "./components/labels/Labels.jsx";
|
|
|
54
54
|
export { Menu, MenuSection, MenuItem } from "./components/menu/Menu.jsx";
|
|
55
55
|
export { SideNav, SideNavItem, SideNavGroup } from "./components/side-nav/SideNav.jsx";
|
|
56
56
|
export { TokenSelect } from "./components/token-select/TokenSelect.jsx";
|
|
57
|
+
export { BottomDrawer } from "./components/bottom-drawer/BottomDrawer.jsx";
|
|
57
58
|
export { TopHeader } from "./components/top-header/TopHeader.jsx";
|
|
58
59
|
export { DataTable } from "./components/data-table/DataTable.jsx";
|
|
59
60
|
export { DataTableFilters } from "./components/data-table/DataTableFilters.jsx";
|
package/src/themes.css
CHANGED
|
@@ -364,6 +364,8 @@ html.a1-theme-fresh {
|
|
|
364
364
|
--semantic-color-text-inverse: var(--base-color-neutral-0);
|
|
365
365
|
--semantic-color-text-accent: var(--base-color-accent-600);
|
|
366
366
|
--semantic-color-surface-page: #D7FFF8;
|
|
367
|
+
--semantic-color-surface-card: var(--base-color-neutral-0);
|
|
368
|
+
--semantic-color-surface-field: var(--base-color-neutral-0);
|
|
367
369
|
--semantic-color-surface-panel: var(--base-color-neutral-0);
|
|
368
370
|
--semantic-color-surface-raised: var(--base-color-neutral-50);
|
|
369
371
|
--semantic-color-border-subtle: var(--base-color-neutral-200);
|
package/src/tokens.css
CHANGED
|
@@ -127,6 +127,7 @@
|
|
|
127
127
|
--base-spacing-32: 2rem;
|
|
128
128
|
--base-spacing-40: 2.5rem;
|
|
129
129
|
--base-spacing-48: 3rem;
|
|
130
|
+
--base-spacing-56: 3.5rem;
|
|
130
131
|
--base-spacing-64: 4rem;
|
|
131
132
|
--base-spacing-96: 6rem;
|
|
132
133
|
--base-spacing-128: 8rem;
|
|
@@ -181,6 +182,8 @@
|
|
|
181
182
|
--semantic-color-text-inverse: #ffffff;
|
|
182
183
|
--semantic-color-text-accent: #7c3aed;
|
|
183
184
|
--semantic-color-surface-page: #ffffff;
|
|
185
|
+
--semantic-color-surface-card: #ffffff;
|
|
186
|
+
--semantic-color-surface-field: #ffffff;
|
|
184
187
|
--semantic-color-surface-panel: #f0f6fe;
|
|
185
188
|
--semantic-color-surface-raised: #e1e8f3;
|
|
186
189
|
--semantic-color-surface-inverse: #060b14;
|
|
@@ -333,6 +336,9 @@
|
|
|
333
336
|
--component-blockquote-padding-inline: 1.5rem;
|
|
334
337
|
--component-blockquote-mark-size: 5rem;
|
|
335
338
|
--component-blockquote-cite-font-weight: 500;
|
|
339
|
+
--component-bottom-drawer-height: 3.5rem;
|
|
340
|
+
--component-bottom-drawer-border-width: 1px;
|
|
341
|
+
--component-bottom-drawer-z-index: 200;
|
|
336
342
|
--component-calendar-month-gap: 2rem;
|
|
337
343
|
--component-calendar-heading-padding-block: 0.75rem;
|
|
338
344
|
--component-calendar-heading-padding-block-compact: 0.5rem;
|