@gtivr4/a1-design-system-react 0.3.0 → 0.4.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 +10 -0
- package/src/components/choice-group/ChoiceGroup.jsx +2 -0
- package/src/components/choice-group/choice-group.css +11 -0
- package/src/components/data-table/DataTable.jsx +5 -5
- package/src/components/data-table/data-table-filters.css +2 -2
- package/src/components/data-table/data-table.css +2 -2
- package/src/components/field/SelectField.jsx +1 -1
- package/src/components/field/TextField.jsx +1 -1
- package/src/components/field/TextareaField.jsx +1 -1
- package/src/components/field/field.css +8 -8
- package/src/components/field-row/FieldRow.jsx +2 -2
- package/src/components/fieldset/Fieldset.jsx +1 -1
- package/src/components/grid/Grid.jsx +1 -0
- package/src/components/heading/Heading.jsx +1 -1
- package/src/components/heading/heading.css +2 -0
- package/src/components/notification/Notification.jsx +4 -4
- package/src/components/notification/notification.css +1 -1
- package/src/components/paragraph/Paragraph.jsx +1 -1
- package/src/components/paragraph/paragraph.css +2 -0
- package/src/components/section/Section.jsx +9 -9
- package/src/components/section/section.css +14 -2
- package/src/components/segmented-control/segmented.css +0 -1
- package/src/components/status-bar/StatusBar.jsx +92 -0
- package/src/components/status-bar/status-bar.css +146 -0
- package/src/components/token-select/token-select.css +1 -1
- package/src/components/top-header/top-header.css +4 -4
- package/src/index.js +1 -0
- package/src/themes.css +1 -0
- package/src/tokens.css +336 -326
package/package.json
CHANGED
package/src/color-scheme.css
CHANGED
|
@@ -9,6 +9,16 @@
|
|
|
9
9
|
box-sizing: border-box;
|
|
10
10
|
}
|
|
11
11
|
|
|
12
|
+
/* ─── Root typeface ──────────────────────────────────────────────────────────
|
|
13
|
+
Apply the design system body font to the document so unscoped elements
|
|
14
|
+
inherit the correct typeface without requiring any A1 component to be
|
|
15
|
+
rendered first. Without this, the browser default (serif) is used until a
|
|
16
|
+
component's own CSS loads. */
|
|
17
|
+
|
|
18
|
+
body {
|
|
19
|
+
font-family: var(--component-paragraph-font-family);
|
|
20
|
+
}
|
|
21
|
+
|
|
12
22
|
/* ─── Form field interaction tokens — light mode defaults ────────────────────
|
|
13
23
|
Single source of truth for hover, active, and read-only states across all
|
|
14
24
|
form field components. Overridden in every dark-mode context below.
|
|
@@ -16,6 +16,7 @@ export function ChoiceGroup({
|
|
|
16
16
|
size = "default",
|
|
17
17
|
columns,
|
|
18
18
|
multiple = false,
|
|
19
|
+
inlineIcon = false,
|
|
19
20
|
required = false,
|
|
20
21
|
name,
|
|
21
22
|
options = [],
|
|
@@ -76,6 +77,7 @@ export function ChoiceGroup({
|
|
|
76
77
|
"a1-choice-group",
|
|
77
78
|
resolvedSize !== "default" && `a1-choice-group--${resolvedSize}`,
|
|
78
79
|
multiple ? "a1-choice-group--multiple" : "a1-choice-group--single",
|
|
80
|
+
inlineIcon && "a1-choice-group--inline-icon",
|
|
79
81
|
isFixedColumns && "a1-choice-group--fixed-columns",
|
|
80
82
|
responsiveClass,
|
|
81
83
|
error && "a1-choice-group--error",
|
|
@@ -317,6 +317,17 @@
|
|
|
317
317
|
background-size: 80%;
|
|
318
318
|
}
|
|
319
319
|
|
|
320
|
+
/* ─── Inline icon layout ────────────────────────────────────────────────────── */
|
|
321
|
+
|
|
322
|
+
.a1-choice-group--inline-icon .a1-choice-item {
|
|
323
|
+
flex-direction: row;
|
|
324
|
+
align-items: center;
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
.a1-choice-group--inline-icon .a1-choice-item__icon {
|
|
328
|
+
flex-shrink: 0;
|
|
329
|
+
}
|
|
330
|
+
|
|
320
331
|
/* ─── Error state ───────────────────────────────────────────────────────────── */
|
|
321
332
|
|
|
322
333
|
.a1-choice-group--error
|
|
@@ -22,8 +22,8 @@ import "./data-table.css";
|
|
|
22
22
|
* }>
|
|
23
23
|
* rows: Array<Record<string, any>>
|
|
24
24
|
* getRowId?: (row: Record<string, any>, index: number) => string | number
|
|
25
|
-
*
|
|
26
|
-
*
|
|
25
|
+
* size?: "comfortable" | "default" | "compact"
|
|
26
|
+
* omit (default) — switches between densities automatically based on available container width
|
|
27
27
|
*/
|
|
28
28
|
|
|
29
29
|
// Estimated minimum content width per column type at a "neutral" padding level
|
|
@@ -193,7 +193,7 @@ function SelectionCheckbox({ checked, indeterminate = false, label, onChange })
|
|
|
193
193
|
export function DataTable({
|
|
194
194
|
columns = [],
|
|
195
195
|
rows = [],
|
|
196
|
-
|
|
196
|
+
size,
|
|
197
197
|
zebra = false,
|
|
198
198
|
scrollable = false,
|
|
199
199
|
caption,
|
|
@@ -238,14 +238,14 @@ export function DataTable({
|
|
|
238
238
|
const [internalSearchColumn, setInternalSearchColumn] = useState(defaultSearchColumn);
|
|
239
239
|
const [internalSelectedRowIds, setInternalSelectedRowIds] = useState(() => normalizeRowIds(defaultSelectedRowIds));
|
|
240
240
|
|
|
241
|
-
const isAuto =
|
|
241
|
+
const isAuto = size === undefined;
|
|
242
242
|
const isSortControlled = sort !== undefined;
|
|
243
243
|
const isPageControlled = page !== undefined;
|
|
244
244
|
const isFilterControlled = filterValue !== undefined;
|
|
245
245
|
const isSearchControlled = searchValue !== undefined;
|
|
246
246
|
const isSearchColumnControlled = searchColumn !== undefined;
|
|
247
247
|
const isSelectionControlled = selectedRowIds !== undefined;
|
|
248
|
-
const activeDensity = isAuto ? autoDensity :
|
|
248
|
+
const activeDensity = isAuto ? autoDensity : size;
|
|
249
249
|
const activeSort = isSortControlled ? normalizeSort(sort) : internalSort;
|
|
250
250
|
const activePage = isPageControlled ? page : internalPage;
|
|
251
251
|
const activeFilterValue = isFilterControlled
|
|
@@ -104,7 +104,7 @@
|
|
|
104
104
|
}
|
|
105
105
|
|
|
106
106
|
.a1-dt-filters__chip-icon {
|
|
107
|
-
font-size:
|
|
107
|
+
font-size: var(--component-menu-item-icon-size);
|
|
108
108
|
flex-shrink: 0;
|
|
109
109
|
margin-inline-start: -2px;
|
|
110
110
|
}
|
|
@@ -134,7 +134,7 @@
|
|
|
134
134
|
border-radius: 9999px;
|
|
135
135
|
background: var(--semantic-color-action-background);
|
|
136
136
|
color: var(--semantic-color-action-foreground);
|
|
137
|
-
font-size:
|
|
137
|
+
font-size: var(--component-tab-count-font-size);
|
|
138
138
|
font-weight: 600;
|
|
139
139
|
line-height: 1;
|
|
140
140
|
margin-inline-start: var(--base-spacing-4);
|
|
@@ -263,7 +263,7 @@
|
|
|
263
263
|
border-radius: 9999px;
|
|
264
264
|
background: var(--semantic-color-action-background);
|
|
265
265
|
color: var(--semantic-color-action-foreground);
|
|
266
|
-
font-size:
|
|
266
|
+
font-size: var(--semantic-font-size-body-xs);
|
|
267
267
|
font-weight: 600;
|
|
268
268
|
letter-spacing: 0.02em;
|
|
269
269
|
user-select: none;
|
|
@@ -272,7 +272,7 @@
|
|
|
272
272
|
.a1-data-table--compact .a1-data-table__avatar {
|
|
273
273
|
width: 22px;
|
|
274
274
|
height: 22px;
|
|
275
|
-
font-size:
|
|
275
|
+
font-size: var(--component-notification-font-size);
|
|
276
276
|
}
|
|
277
277
|
|
|
278
278
|
.a1-data-table__actions {
|
|
@@ -6,7 +6,7 @@ import { FieldsetContext } from "../fieldset/FieldsetContext.js";
|
|
|
6
6
|
import "./field.css";
|
|
7
7
|
|
|
8
8
|
const SIZES = ["comfortable", "default", "compact"];
|
|
9
|
-
const LABEL_POSITIONS = ["above", "
|
|
9
|
+
const LABEL_POSITIONS = ["above", "before"];
|
|
10
10
|
|
|
11
11
|
export const SelectField = forwardRef(function SelectField({
|
|
12
12
|
label,
|
|
@@ -5,7 +5,7 @@ import { FieldsetContext } from "../fieldset/FieldsetContext.js";
|
|
|
5
5
|
import "./field.css";
|
|
6
6
|
|
|
7
7
|
const SIZES = ["comfortable", "default", "compact"];
|
|
8
|
-
const LABEL_POSITIONS = ["above", "
|
|
8
|
+
const LABEL_POSITIONS = ["above", "before"];
|
|
9
9
|
|
|
10
10
|
export const TextField = forwardRef(function TextField({
|
|
11
11
|
label,
|
|
@@ -6,7 +6,7 @@ import "./field.css";
|
|
|
6
6
|
import "./textarea-field.css";
|
|
7
7
|
|
|
8
8
|
const SIZES = ["comfortable", "default", "compact"];
|
|
9
|
-
const LABEL_POSITIONS = ["above", "
|
|
9
|
+
const LABEL_POSITIONS = ["above", "before"];
|
|
10
10
|
const ROW_SIZES = { sm: 2, md: 4, lg: 8, xl: 12 };
|
|
11
11
|
|
|
12
12
|
function resolveRows(rows) {
|
|
@@ -336,9 +336,9 @@
|
|
|
336
336
|
color: var(--semantic-color-status-error-background);
|
|
337
337
|
}
|
|
338
338
|
|
|
339
|
-
/* ───
|
|
339
|
+
/* ─── Before label layout ───────────────────────────────────────────────────── */
|
|
340
340
|
|
|
341
|
-
.a1-field--label-
|
|
341
|
+
.a1-field--label-before {
|
|
342
342
|
display: grid;
|
|
343
343
|
grid-template-columns: var(--a1-field-side-label-width) 1fr;
|
|
344
344
|
column-gap: var(--base-spacing-16);
|
|
@@ -346,12 +346,12 @@
|
|
|
346
346
|
align-items: start;
|
|
347
347
|
}
|
|
348
348
|
|
|
349
|
-
.a1-field--label-
|
|
349
|
+
.a1-field--label-before .a1-field__label {
|
|
350
350
|
padding-top: calc((var(--a1-field-height) - 1lh) / 2);
|
|
351
351
|
padding-bottom: 0;
|
|
352
352
|
}
|
|
353
353
|
|
|
354
|
-
.a1-field--label-
|
|
354
|
+
.a1-field--label-before .a1-field__message {
|
|
355
355
|
grid-column: 1;
|
|
356
356
|
grid-row: 2;
|
|
357
357
|
/* Fine-tuned margin: overrides the base margin-top added for the above-label
|
|
@@ -359,22 +359,22 @@
|
|
|
359
359
|
margin-top: calc((var(--a1-field-label-size) * 1.5 - var(--a1-field-height)) / 2 + var(--base-spacing-2));
|
|
360
360
|
}
|
|
361
361
|
|
|
362
|
-
/* ───
|
|
362
|
+
/* ─── Before label → stacked on xs/sm ─────────────────────────────────────── */
|
|
363
363
|
|
|
364
364
|
@media (--bp-sm-down) {
|
|
365
|
-
.a1-field--label-
|
|
365
|
+
.a1-field--label-before {
|
|
366
366
|
display: flex;
|
|
367
367
|
flex-direction: column;
|
|
368
368
|
gap: var(--a1-field-gap); /* restore: grid overrode row-gap to base-spacing-4 */
|
|
369
369
|
align-items: stretch; /* restore: grid set align-items: start */
|
|
370
370
|
}
|
|
371
371
|
|
|
372
|
-
.a1-field--label-
|
|
372
|
+
.a1-field--label-before .a1-field__label {
|
|
373
373
|
padding-top: 0;
|
|
374
374
|
padding-bottom: var(--a1-field-gap);
|
|
375
375
|
}
|
|
376
376
|
|
|
377
|
-
.a1-field--label-
|
|
377
|
+
.a1-field--label-before .a1-field__message {
|
|
378
378
|
margin-top: var(--a1-field-gap);
|
|
379
379
|
}
|
|
380
380
|
}
|
|
@@ -5,9 +5,9 @@ import "./field-row.css";
|
|
|
5
5
|
export function FieldRow({ children, className = "", ...props }) {
|
|
6
6
|
const ctx = useContext(FieldsetContext);
|
|
7
7
|
|
|
8
|
-
//
|
|
8
|
+
// Before-label fields already use an internal two-column grid;
|
|
9
9
|
// stacking the row prevents layout conflicts.
|
|
10
|
-
const stacked = ctx?.labelPosition === "
|
|
10
|
+
const stacked = ctx?.labelPosition === "before";
|
|
11
11
|
|
|
12
12
|
const classes = [
|
|
13
13
|
"a1-field-row",
|
|
@@ -2,7 +2,7 @@ import { FieldsetContext } from "./FieldsetContext.js";
|
|
|
2
2
|
import "./fieldset.css";
|
|
3
3
|
|
|
4
4
|
const SIZES = ["comfortable", "default", "compact"];
|
|
5
|
-
const LABEL_POSITIONS = ["above", "
|
|
5
|
+
const LABEL_POSITIONS = ["above", "before"];
|
|
6
6
|
|
|
7
7
|
export function Fieldset({
|
|
8
8
|
legend,
|
|
@@ -7,7 +7,7 @@ const margins = ["sm", "md", "lg"];
|
|
|
7
7
|
const levels = ["h1", "h2", "h3", "h4", "h5", "h6"];
|
|
8
8
|
const breakpoints = ["xs", "sm", "md", "lg", "xl"];
|
|
9
9
|
const textWraps = ["balance"];
|
|
10
|
-
const aligns = ["left", "center", "right"];
|
|
10
|
+
const aligns = ["left", "center", "right", "start", "end"];
|
|
11
11
|
|
|
12
12
|
const levelDefaults = { h1: "xl", h2: "lg", h3: "md", h4: "sm", h5: "xs", h6: "xs" };
|
|
13
13
|
|
|
@@ -88,6 +88,8 @@
|
|
|
88
88
|
.a1-heading--align-left { text-align: start; }
|
|
89
89
|
.a1-heading--align-center { text-align: center; }
|
|
90
90
|
.a1-heading--align-right { text-align: end; }
|
|
91
|
+
.a1-heading--align-start { text-align: start; }
|
|
92
|
+
.a1-heading--align-end { text-align: end; }
|
|
91
93
|
|
|
92
94
|
/* Expressive marks */
|
|
93
95
|
.a1-heading-mark {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import "./notification.css";
|
|
2
2
|
|
|
3
|
-
const
|
|
3
|
+
const statuses = ["neutral", "error", "success", "warn", "info"];
|
|
4
4
|
const positions = ["top-right", "top-left", "bottom-right", "bottom-left"];
|
|
5
5
|
|
|
6
6
|
function formatCount(n, max) {
|
|
@@ -15,11 +15,11 @@ export function Notification({
|
|
|
15
15
|
count,
|
|
16
16
|
label,
|
|
17
17
|
dot = false,
|
|
18
|
-
|
|
18
|
+
status = "neutral",
|
|
19
19
|
position = "top-right",
|
|
20
20
|
max = 99,
|
|
21
21
|
}) {
|
|
22
|
-
const
|
|
22
|
+
const resolvedStatus = statuses.includes(status) ? status : "neutral";
|
|
23
23
|
const resolvedPosition = positions.includes(position) ? position : "top-right";
|
|
24
24
|
|
|
25
25
|
const isDot = dot || (count === undefined && label === undefined);
|
|
@@ -35,7 +35,7 @@ export function Notification({
|
|
|
35
35
|
|
|
36
36
|
const classes = [
|
|
37
37
|
"a1-notification",
|
|
38
|
-
`a1-notification--${
|
|
38
|
+
`a1-notification--${resolvedStatus}`,
|
|
39
39
|
`a1-notification--${resolvedPosition}`,
|
|
40
40
|
isDot && "a1-notification--dot",
|
|
41
41
|
]
|
|
@@ -43,7 +43,7 @@
|
|
|
43
43
|
|
|
44
44
|
/* ── Variants ───────────────────────────────────────────────────────────── */
|
|
45
45
|
|
|
46
|
-
.a1-notification--
|
|
46
|
+
.a1-notification--neutral {
|
|
47
47
|
--a1-notification-background: var(--base-color-neutral-600);
|
|
48
48
|
--a1-notification-foreground: var(--base-color-neutral-0);
|
|
49
49
|
}
|
|
@@ -4,7 +4,7 @@ const sizes = ["xs", "sm", "md", "lg", "xl"];
|
|
|
4
4
|
const colors = ["default", "muted"];
|
|
5
5
|
const breakpoints = ["xs", "sm", "md", "lg", "xl"];
|
|
6
6
|
const textWraps = ["balance"];
|
|
7
|
-
const aligns = ["left", "center", "right"];
|
|
7
|
+
const aligns = ["left", "center", "right", "start", "end"];
|
|
8
8
|
|
|
9
9
|
function isResponsiveSize(size) {
|
|
10
10
|
return size && typeof size === "object" && !Array.isArray(size);
|
|
@@ -46,6 +46,8 @@
|
|
|
46
46
|
.a1-paragraph--align-left { text-align: start; }
|
|
47
47
|
.a1-paragraph--align-center { text-align: center; }
|
|
48
48
|
.a1-paragraph--align-right { text-align: end; }
|
|
49
|
+
.a1-paragraph--align-start { text-align: start; }
|
|
50
|
+
.a1-paragraph--align-end { text-align: end; }
|
|
49
51
|
|
|
50
52
|
.a1-paragraph + .a1-paragraph,
|
|
51
53
|
.a1-paragraph + .a1-heading {
|
|
@@ -2,7 +2,7 @@ import "../../themes.css";
|
|
|
2
2
|
import "../../color-scheme.css";
|
|
3
3
|
import "./section.css";
|
|
4
4
|
|
|
5
|
-
const VALID_PADDING = ["lg", "md", "sm", "none"];
|
|
5
|
+
const VALID_PADDING = ["lg", "md", "sm", "xs", "none"];
|
|
6
6
|
const VALID_SURFACES = ["page", "panel", "raised"];
|
|
7
7
|
const VALID_GAPS = ["xs", "sm", "md", "lg"];
|
|
8
8
|
const VALID_GRADIENTS = ["accent", "highlight", "info", "success", "warn"];
|
|
@@ -31,7 +31,7 @@ export function Section({
|
|
|
31
31
|
inverse = false,
|
|
32
32
|
contentWidth,
|
|
33
33
|
height,
|
|
34
|
-
|
|
34
|
+
align,
|
|
35
35
|
className = "",
|
|
36
36
|
children,
|
|
37
37
|
...props
|
|
@@ -72,14 +72,14 @@ export function Section({
|
|
|
72
72
|
classes.push(`a1-section--height-${height}`);
|
|
73
73
|
}
|
|
74
74
|
|
|
75
|
-
if (typeof
|
|
76
|
-
if (VALID_ALIGNMENTS.includes(
|
|
77
|
-
classes.push(`a1-section--align-${
|
|
75
|
+
if (typeof align === "string") {
|
|
76
|
+
if (VALID_ALIGNMENTS.includes(align)) {
|
|
77
|
+
classes.push(`a1-section--align-${align}`);
|
|
78
78
|
}
|
|
79
|
-
} else if (
|
|
80
|
-
for (const [bp,
|
|
81
|
-
if (VALID_ALIGNMENTS.includes(
|
|
82
|
-
classes.push(`a1-section--${bp}-align-${
|
|
79
|
+
} else if (align && typeof align === "object") {
|
|
80
|
+
for (const [bp, alignVal] of Object.entries(align)) {
|
|
81
|
+
if (VALID_ALIGNMENTS.includes(alignVal)) {
|
|
82
|
+
classes.push(`a1-section--${bp}-align-${alignVal}`);
|
|
83
83
|
}
|
|
84
84
|
}
|
|
85
85
|
}
|
|
@@ -111,7 +111,7 @@
|
|
|
111
111
|
/* ── Alignment ─────────────────────────────────────────────────────────────── */
|
|
112
112
|
/*
|
|
113
113
|
* Aligns direct children as layout items.
|
|
114
|
-
* Use the object prop for responsive changes:
|
|
114
|
+
* Use the object prop for responsive changes: align={{ xs: "center", lg: "left" }}
|
|
115
115
|
*/
|
|
116
116
|
|
|
117
117
|
.a1-section[class*="-align-"] {
|
|
@@ -206,10 +206,11 @@
|
|
|
206
206
|
|
|
207
207
|
/* ── Padding — static (with built-in responsive scaling) ───────────────────── */
|
|
208
208
|
/*
|
|
209
|
-
*
|
|
209
|
+
* Four tiers scale across three breakpoints:
|
|
210
210
|
* lg 96/64 → 96/40 at ≤1024 → 64/24 at ≤640
|
|
211
211
|
* md 64/40 → 40/24 at ≤1024 → 32/16 at ≤640
|
|
212
212
|
* sm 32/24 → 24/16 at ≤1024 → 16/12 at ≤640
|
|
213
|
+
* xs 16/16 → 12/12 at ≤1024 → 8/8 at ≤640
|
|
213
214
|
* Block (top/bottom) / Inline (left/right)
|
|
214
215
|
*/
|
|
215
216
|
|
|
@@ -217,6 +218,11 @@
|
|
|
217
218
|
padding: 0;
|
|
218
219
|
}
|
|
219
220
|
|
|
221
|
+
.a1-section--padding-xs {
|
|
222
|
+
padding-block: var(--base-spacing-16);
|
|
223
|
+
padding-inline: var(--base-spacing-16);
|
|
224
|
+
}
|
|
225
|
+
|
|
220
226
|
.a1-section--padding-sm {
|
|
221
227
|
padding-block: var(--base-spacing-32);
|
|
222
228
|
padding-inline: var(--base-spacing-24);
|
|
@@ -233,12 +239,14 @@
|
|
|
233
239
|
}
|
|
234
240
|
|
|
235
241
|
@media (--bp-md-down) {
|
|
242
|
+
.a1-section--padding-xs { padding-block: var(--base-spacing-12); padding-inline: var(--base-spacing-12); }
|
|
236
243
|
.a1-section--padding-sm { padding-block: var(--base-spacing-24); padding-inline: var(--base-spacing-16); }
|
|
237
244
|
.a1-section--padding-md { padding-block: var(--base-spacing-40); padding-inline: var(--base-spacing-24); }
|
|
238
245
|
.a1-section--padding-lg { padding-block: var(--base-spacing-64); padding-inline: var(--base-spacing-40); }
|
|
239
246
|
}
|
|
240
247
|
|
|
241
248
|
@media (--bp-sm-down) {
|
|
249
|
+
.a1-section--padding-xs { padding-block: var(--base-spacing-8); padding-inline: var(--base-spacing-8); }
|
|
242
250
|
.a1-section--padding-sm { padding-block: var(--base-spacing-16); padding-inline: var(--base-spacing-12); }
|
|
243
251
|
.a1-section--padding-md { padding-block: var(--base-spacing-24); padding-inline: var(--base-spacing-16); }
|
|
244
252
|
.a1-section--padding-lg { padding-block: var(--base-spacing-40); padding-inline: var(--base-spacing-24); }
|
|
@@ -251,6 +259,7 @@
|
|
|
251
259
|
|
|
252
260
|
/* xs: base, no media query */
|
|
253
261
|
.a1-section--xs-padding-none { padding: 0; }
|
|
262
|
+
.a1-section--xs-padding-xs { padding-block: var(--base-spacing-16); padding-inline: var(--base-spacing-16); }
|
|
254
263
|
.a1-section--xs-padding-sm { padding-block: var(--base-spacing-32); padding-inline: var(--base-spacing-24); }
|
|
255
264
|
.a1-section--xs-padding-md { padding-block: var(--base-spacing-64); padding-inline: var(--base-spacing-40); }
|
|
256
265
|
.a1-section--xs-padding-lg { padding-block: var(--base-spacing-96); padding-inline: var(--base-spacing-64); }
|
|
@@ -258,6 +267,7 @@
|
|
|
258
267
|
/* sm: ≥481px */
|
|
259
268
|
@media (--bp-sm-up) {
|
|
260
269
|
.a1-section--sm-padding-none { padding: 0; }
|
|
270
|
+
.a1-section--sm-padding-xs { padding-block: var(--base-spacing-16); padding-inline: var(--base-spacing-16); }
|
|
261
271
|
.a1-section--sm-padding-sm { padding-block: var(--base-spacing-32); padding-inline: var(--base-spacing-24); }
|
|
262
272
|
.a1-section--sm-padding-md { padding-block: var(--base-spacing-64); padding-inline: var(--base-spacing-40); }
|
|
263
273
|
.a1-section--sm-padding-lg { padding-block: var(--base-spacing-96); padding-inline: var(--base-spacing-64); }
|
|
@@ -266,6 +276,7 @@
|
|
|
266
276
|
/* md: ≥769px */
|
|
267
277
|
@media (min-width: 769px) {
|
|
268
278
|
.a1-section--md-padding-none { padding: 0; }
|
|
279
|
+
.a1-section--md-padding-xs { padding-block: var(--base-spacing-12); padding-inline: var(--base-spacing-12); }
|
|
269
280
|
.a1-section--md-padding-sm { padding-block: var(--base-spacing-24); padding-inline: var(--base-spacing-16); }
|
|
270
281
|
.a1-section--md-padding-md { padding-block: var(--base-spacing-40); padding-inline: var(--base-spacing-24); }
|
|
271
282
|
.a1-section--md-padding-lg { padding-block: var(--base-spacing-64); padding-inline: var(--base-spacing-40); }
|
|
@@ -274,6 +285,7 @@
|
|
|
274
285
|
/* lg: ≥1025px */
|
|
275
286
|
@media (--bp-lg-up) {
|
|
276
287
|
.a1-section--lg-padding-none { padding: 0; }
|
|
288
|
+
.a1-section--lg-padding-xs { padding-block: var(--base-spacing-16); padding-inline: var(--base-spacing-16); }
|
|
277
289
|
.a1-section--lg-padding-sm { padding-block: var(--base-spacing-32); padding-inline: var(--base-spacing-24); }
|
|
278
290
|
.a1-section--lg-padding-md { padding-block: var(--base-spacing-64); padding-inline: var(--base-spacing-40); }
|
|
279
291
|
.a1-section--lg-padding-lg { padding-block: var(--base-spacing-96); padding-inline: var(--base-spacing-64); }
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { useId, useState, useEffect } from "react";
|
|
2
|
+
import { Button } from "../button/Button.jsx";
|
|
3
|
+
import { useLabel } from "../labels/Labels.jsx";
|
|
4
|
+
import "./status-bar.css";
|
|
5
|
+
|
|
6
|
+
const SIZES = ["sm", "md", "lg"];
|
|
7
|
+
const POSITIONS = ["above", "below", "before", "after"];
|
|
8
|
+
|
|
9
|
+
export function StatusBar({
|
|
10
|
+
value = 0,
|
|
11
|
+
max = 100,
|
|
12
|
+
label,
|
|
13
|
+
labelPosition = "above",
|
|
14
|
+
size = "md",
|
|
15
|
+
indeterminate = false,
|
|
16
|
+
className = "",
|
|
17
|
+
...props
|
|
18
|
+
}) {
|
|
19
|
+
const labelId = useId();
|
|
20
|
+
|
|
21
|
+
const pauseLabel = useLabel("statusBar.pause", "Pause");
|
|
22
|
+
const playLabel = useLabel("statusBar.play", "Play");
|
|
23
|
+
|
|
24
|
+
const [paused, setPaused] = useState(false);
|
|
25
|
+
const [showPause, setShowPause] = useState(false);
|
|
26
|
+
|
|
27
|
+
useEffect(() => {
|
|
28
|
+
if (!indeterminate) {
|
|
29
|
+
setShowPause(false);
|
|
30
|
+
setPaused(false);
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
const t = setTimeout(() => setShowPause(true), 3000);
|
|
34
|
+
return () => clearTimeout(t);
|
|
35
|
+
}, [indeterminate]);
|
|
36
|
+
|
|
37
|
+
const resolvedSize = SIZES.includes(size) ? size : "md";
|
|
38
|
+
const resolvedPosition = POSITIONS.includes(labelPosition) ? labelPosition : "above";
|
|
39
|
+
|
|
40
|
+
const pct = Math.min(100, Math.max(0, max > 0 ? (value / max) * 100 : 0));
|
|
41
|
+
const isLabelFirst = resolvedPosition === "above" || resolvedPosition === "before";
|
|
42
|
+
|
|
43
|
+
const classes = [
|
|
44
|
+
"a1-status-bar",
|
|
45
|
+
resolvedSize !== "md" && `a1-status-bar--${resolvedSize}`,
|
|
46
|
+
`a1-status-bar--${resolvedPosition}`,
|
|
47
|
+
indeterminate && "a1-status-bar--indeterminate",
|
|
48
|
+
indeterminate && paused && "a1-status-bar--paused",
|
|
49
|
+
className,
|
|
50
|
+
].filter(Boolean).join(" ");
|
|
51
|
+
|
|
52
|
+
const labelEl = label ? (
|
|
53
|
+
<span id={labelId} className="a1-status-bar__label">{label}</span>
|
|
54
|
+
) : null;
|
|
55
|
+
|
|
56
|
+
return (
|
|
57
|
+
<div className={classes}>
|
|
58
|
+
{isLabelFirst && labelEl}
|
|
59
|
+
<div className="a1-status-bar__bar-row">
|
|
60
|
+
<div
|
|
61
|
+
className="a1-status-bar__track"
|
|
62
|
+
role="progressbar"
|
|
63
|
+
aria-valuenow={indeterminate ? undefined : value}
|
|
64
|
+
aria-valuemin={0}
|
|
65
|
+
aria-valuemax={max}
|
|
66
|
+
aria-labelledby={label ? labelId : undefined}
|
|
67
|
+
{...props}
|
|
68
|
+
>
|
|
69
|
+
<div
|
|
70
|
+
className={[
|
|
71
|
+
"a1-status-bar__fill",
|
|
72
|
+
indeterminate && "a1-status-bar__fill--indeterminate",
|
|
73
|
+
].filter(Boolean).join(" ")}
|
|
74
|
+
style={indeterminate ? undefined : { inlineSize: `${pct}%` }}
|
|
75
|
+
/>
|
|
76
|
+
</div>
|
|
77
|
+
{showPause && (
|
|
78
|
+
<Button
|
|
79
|
+
size="sm"
|
|
80
|
+
variant="secondary"
|
|
81
|
+
icon={paused ? "play_arrow" : "pause"}
|
|
82
|
+
className="a1-status-bar__pause"
|
|
83
|
+
onClick={() => setPaused(p => !p)}
|
|
84
|
+
>
|
|
85
|
+
{paused ? playLabel : pauseLabel}
|
|
86
|
+
</Button>
|
|
87
|
+
)}
|
|
88
|
+
</div>
|
|
89
|
+
{!isLabelFirst && labelEl}
|
|
90
|
+
</div>
|
|
91
|
+
);
|
|
92
|
+
}
|