@robr0/design-system 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/README.md +12 -7
- package/components/Accordion/Accordion.js +1 -0
- package/components/AgentPlan/AgentPlan.css +2 -2
- package/components/AgentPlan/AgentPlan.js +1 -0
- package/components/AgentStatus/AgentStatus.js +1 -0
- package/components/AiButton/AiButton.js +1 -0
- package/components/AlertDialog/AlertDialog.js +1 -0
- package/components/AnchorNav/AnchorNav.css +63 -0
- package/components/AnchorNav/AnchorNav.d.ts +33 -0
- package/components/AnchorNav/AnchorNav.js +82 -0
- package/components/AppLayout/AppLayout.js +1 -0
- package/components/AppSidebar/AppSidebar.css +13 -3
- package/components/AppSidebar/AppSidebar.d.ts +6 -2
- package/components/AppSidebar/AppSidebar.js +68 -36
- package/components/Avatar/Avatar.js +1 -0
- package/components/Button/Button.js +1 -0
- package/components/Card/Card.js +1 -0
- package/components/Carousel/Carousel.js +1 -0
- package/components/Chart/AreaChart.js +1 -1
- package/components/Chart/BarChart.js +1 -1
- package/components/Chart/LineChart.js +1 -1
- package/components/Chart/PieChart.js +2 -2
- package/components/Chart/RadarChart.js +1 -1
- package/components/Chart/RadialChart.js +2 -2
- package/components/Chart/StackedBarChart.js +2 -2
- package/components/ChatThread/ChatThread.js +1 -0
- package/components/Checkbox/Checkbox.js +1 -0
- package/components/CodeBlock/CodeBlock.js +1 -0
- package/components/CodeDiff/CodeDiff.css +175 -0
- package/components/CodeDiff/CodeDiff.d.ts +58 -0
- package/components/CodeDiff/CodeDiff.js +114 -0
- package/components/ColorPicker/ColorPicker.js +1 -0
- package/components/Combobox/Combobox.js +1 -0
- package/components/CommandPalette/CommandPalette.js +1 -0
- package/components/Composer/Composer.js +1 -0
- package/components/ContactCard/ContactCard.js +1 -0
- package/components/ContextMenu/ContextMenu.js +1 -0
- package/components/DataTable/DataTable.js +1 -0
- package/components/DateInput/DateInput.js +1 -0
- package/components/DatePicker/DatePicker.js +1 -0
- package/components/Dialog/Dialog.js +1 -0
- package/components/Drawer/Drawer.js +1 -0
- package/components/Dropdown/Dropdown.js +1 -0
- package/components/DropdownMenu/DropdownMenu.css +1 -1
- package/components/DropdownMenu/DropdownMenu.js +1 -0
- package/components/EventCalendar/EventCalendar.js +1 -0
- package/components/Field/Field.js +1 -0
- package/components/Field/FieldContext.js +1 -0
- package/components/FileInput/FileInput.js +1 -0
- package/components/Input/Input.js +1 -0
- package/components/InterruptCard/InterruptCard.css +11 -21
- package/components/InterruptCard/InterruptCard.d.ts +5 -6
- package/components/MessageCard/MessageCard.css +1 -2
- package/components/ModelPicker/ModelPicker.css +3 -1
- package/components/ModelPicker/ModelPicker.js +1 -0
- package/components/NavList/NavList.js +1 -0
- package/components/NotificationCenter/NotificationCenter.js +1 -0
- package/components/NumberInput/NumberInput.css +154 -0
- package/components/NumberInput/NumberInput.d.ts +44 -0
- package/components/NumberInput/NumberInput.js +147 -0
- package/components/Pagination/Pagination.js +1 -0
- package/components/PinInput/PinInput.css +92 -0
- package/components/PinInput/PinInput.d.ts +46 -0
- package/components/PinInput/PinInput.js +161 -0
- package/components/Popover/Popover.css +5 -6
- package/components/Popover/Popover.js +1 -0
- package/components/RadioButton/RadioButton.js +1 -0
- package/components/Reasoning/Reasoning.js +1 -0
- package/components/SectionTitle/SectionTitle.css +8 -0
- package/components/SectionTitle/SectionTitle.d.ts +3 -1
- package/components/SectionTitle/SectionTitle.js +6 -1
- package/components/SegmentedControl/SegmentedControl.js +1 -0
- package/components/SelectionCard/SelectionCard.js +1 -0
- package/components/ShaderField/ShaderField.js +1 -0
- package/components/ShaderField/useShaderField.js +1 -0
- package/components/Slider/Slider.js +1 -0
- package/components/Sparkline/Sparkline.css +52 -0
- package/components/Sparkline/Sparkline.d.ts +49 -0
- package/components/Sparkline/Sparkline.js +86 -0
- package/components/Stepper/Stepper.css +207 -0
- package/components/Stepper/Stepper.d.ts +41 -0
- package/components/Stepper/Stepper.js +61 -0
- package/components/Tabs/Tabs.js +1 -0
- package/components/TagInput/TagInput.css +160 -0
- package/components/TagInput/TagInput.d.ts +43 -0
- package/components/TagInput/TagInput.js +137 -0
- package/components/Textarea/Textarea.js +1 -0
- package/components/TimePicker/TimePicker.css +230 -0
- package/components/TimePicker/TimePicker.d.ts +46 -0
- package/components/TimePicker/TimePicker.js +246 -0
- package/components/Toast/Toast.js +1 -0
- package/components/ToggleGroup/ToggleGroup.js +1 -0
- package/components/ToggleSwitch/ToggleSwitch.js +1 -0
- package/components/ToolCall/ToolCall.css +7 -11
- package/components/ToolCall/ToolCall.js +1 -0
- package/components/Tooltip/Tooltip.js +1 -0
- package/components/TreeView/TreeView.css +119 -0
- package/components/TreeView/TreeView.d.ts +45 -0
- package/components/TreeView/TreeView.js +232 -0
- package/components/registry.d.ts +22 -6
- package/components/registry.js +3 -1
- package/components/registry.json +206 -20
- package/components/registry.json.d.ts +206 -20
- package/components/registry.json.js +2 -2
- package/index.d.ts +11 -2
- package/index.js +21 -1
- package/package.json +1 -1
- package/tokens/tokens-dark.css +17 -13
- package/tokens/tokens-light.css +15 -12
|
@@ -11,6 +11,14 @@
|
|
|
11
11
|
padding-bottom: var(--padding-xl);
|
|
12
12
|
}
|
|
13
13
|
|
|
14
|
+
/* Divider-less variant: for headings above content that draws its own
|
|
15
|
+
lines, where a second rule would double up. The generous clearance
|
|
16
|
+
the divider needed drops to a small breathing gap. */
|
|
17
|
+
.ds-section-title--no-divider {
|
|
18
|
+
border-bottom: none;
|
|
19
|
+
padding-bottom: var(--padding-md);
|
|
20
|
+
}
|
|
21
|
+
|
|
14
22
|
/* ============================================
|
|
15
23
|
HEADING
|
|
16
24
|
============================================ */
|
|
@@ -4,7 +4,9 @@ export interface SectionTitleProps {
|
|
|
4
4
|
title: string;
|
|
5
5
|
/** Optional trailing content (count, badge, metadata) */
|
|
6
6
|
trailing?: React.ReactNode;
|
|
7
|
+
/** Whether to draw the bottom divider line. Set false above content that draws its own lines (bordered tables, calendars), so the section separates by whitespace alone. */
|
|
8
|
+
divider?: boolean;
|
|
7
9
|
/** Additional CSS classes */
|
|
8
10
|
className?: string;
|
|
9
11
|
}
|
|
10
|
-
export declare function SectionTitle({ title, trailing, className, }: SectionTitleProps): React.JSX.Element;
|
|
12
|
+
export declare function SectionTitle({ title, trailing, divider, className, }: SectionTitleProps): React.JSX.Element;
|
|
@@ -4,9 +4,14 @@ import "./SectionTitle.css";
|
|
|
4
4
|
function SectionTitle({
|
|
5
5
|
title,
|
|
6
6
|
trailing,
|
|
7
|
+
divider = true,
|
|
7
8
|
className
|
|
8
9
|
}) {
|
|
9
|
-
const classes = [
|
|
10
|
+
const classes = [
|
|
11
|
+
"ds-section-title",
|
|
12
|
+
!divider && "ds-section-title--no-divider",
|
|
13
|
+
className
|
|
14
|
+
].filter(Boolean).join(" ");
|
|
10
15
|
return /* @__PURE__ */ jsxs("div", { className: classes, children: [
|
|
11
16
|
/* @__PURE__ */ jsx("h2", { className: "ds-section-title__heading", children: title }),
|
|
12
17
|
trailing !== void 0 && trailing !== null && /* @__PURE__ */ jsx("span", { className: "ds-section-title__trailing", children: trailing })
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/* ============================================
|
|
2
|
+
SPARKLINE COMPONENT
|
|
3
|
+
Inline trend line for stats and table cells.
|
|
4
|
+
Colour flows through `color` — the line, fill
|
|
5
|
+
and dot all draw with currentColor, so a tone
|
|
6
|
+
is one custom property swap.
|
|
7
|
+
============================================ */
|
|
8
|
+
|
|
9
|
+
/* Base */
|
|
10
|
+
|
|
11
|
+
.ds-sparkline {
|
|
12
|
+
display: inline-block;
|
|
13
|
+
vertical-align: middle;
|
|
14
|
+
flex-shrink: 0;
|
|
15
|
+
color: var(--color-action-primary-bg);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/* Parts */
|
|
19
|
+
|
|
20
|
+
.ds-sparkline__line {
|
|
21
|
+
fill: none;
|
|
22
|
+
stroke: currentColor;
|
|
23
|
+
stroke-linecap: round;
|
|
24
|
+
stroke-linejoin: round;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
.ds-sparkline__fill {
|
|
28
|
+
fill: currentColor;
|
|
29
|
+
opacity: 0.15;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
.ds-sparkline__dot {
|
|
33
|
+
fill: currentColor;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/* Tones */
|
|
37
|
+
|
|
38
|
+
.ds-sparkline--accent {
|
|
39
|
+
color: var(--color-action-primary-bg);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
.ds-sparkline--positive {
|
|
43
|
+
color: var(--color-status-positive-text);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
.ds-sparkline--negative {
|
|
47
|
+
color: var(--color-status-error-text);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
.ds-sparkline--neutral {
|
|
51
|
+
color: var(--color-text-secondary);
|
|
52
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { default as React } from 'react';
|
|
2
|
+
/** Props owned by Sparkline itself — everything else falls through to the SVG node. */
|
|
3
|
+
type SparklineOwnProps = {
|
|
4
|
+
/** The series to plot, in order. Values are normalised into the viewBox. */
|
|
5
|
+
data: number[];
|
|
6
|
+
/** Rendering treatment — a bare line, or a line with a soft fill underneath. */
|
|
7
|
+
variant?: 'line' | 'area';
|
|
8
|
+
/**
|
|
9
|
+
* Colour role for the line and end dot. `accent` follows the chart palette's
|
|
10
|
+
* lead colour; `positive`/`negative` map to the status text tokens; `neutral`
|
|
11
|
+
* recedes to secondary text.
|
|
12
|
+
*/
|
|
13
|
+
tone?: 'accent' | 'positive' | 'negative' | 'neutral';
|
|
14
|
+
/** Marks the final point with a small dot. */
|
|
15
|
+
showDot?: boolean;
|
|
16
|
+
/** Line thickness in viewBox units — an SVG geometry attribute, not a token. */
|
|
17
|
+
strokeWidth?: number;
|
|
18
|
+
/**
|
|
19
|
+
* ViewBox width. Also the default rendered width; the SVG scales to its
|
|
20
|
+
* container when sized externally via CSS. Intentionally shadows the native
|
|
21
|
+
* SVG `width` attribute, which it sets.
|
|
22
|
+
*/
|
|
23
|
+
width?: number;
|
|
24
|
+
/**
|
|
25
|
+
* ViewBox height. Also the default rendered height; the SVG scales to its
|
|
26
|
+
* container when sized externally via CSS. Intentionally shadows the native
|
|
27
|
+
* SVG `height` attribute, which it sets.
|
|
28
|
+
*/
|
|
29
|
+
height?: number;
|
|
30
|
+
/**
|
|
31
|
+
* Accessible description of the trend, e.g. "Revenue, trending up". When
|
|
32
|
+
* omitted the SVG is decorative and hidden from assistive technology.
|
|
33
|
+
*/
|
|
34
|
+
label?: string;
|
|
35
|
+
/** Additional CSS classes */
|
|
36
|
+
className?: string;
|
|
37
|
+
};
|
|
38
|
+
export interface SparklineProps extends SparklineOwnProps, Omit<React.ComponentPropsWithoutRef<'svg'>, keyof SparklineOwnProps | 'children'> {
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Sparkline — an inline trend line for stats and table cells, drawn without
|
|
42
|
+
* axes or chrome. Pure SVG computed from props: no charting library, no hooks,
|
|
43
|
+
* no dependencies, so it renders from a Server Component and costs nothing in
|
|
44
|
+
* a dense table. Degenerate data never breaks the path: an all-equal series
|
|
45
|
+
* renders a horizontal midline, and fewer than two points render just the
|
|
46
|
+
* end dot, or nothing.
|
|
47
|
+
*/
|
|
48
|
+
export declare const Sparkline: React.ForwardRefExoticComponent<SparklineProps & React.RefAttributes<SVGSVGElement>>;
|
|
49
|
+
export {};
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { jsxs, jsx } from "react/jsx-runtime";
|
|
2
|
+
import React from "react";
|
|
3
|
+
import "./Sparkline.css";
|
|
4
|
+
const round = (n) => Math.round(n * 100) / 100;
|
|
5
|
+
const Sparkline = React.forwardRef(
|
|
6
|
+
({
|
|
7
|
+
data,
|
|
8
|
+
variant = "line",
|
|
9
|
+
tone = "accent",
|
|
10
|
+
showDot = true,
|
|
11
|
+
strokeWidth = 2,
|
|
12
|
+
width = 120,
|
|
13
|
+
height = 32,
|
|
14
|
+
label,
|
|
15
|
+
className = "",
|
|
16
|
+
...rest
|
|
17
|
+
}, ref) => {
|
|
18
|
+
const baseClass = "ds-sparkline";
|
|
19
|
+
const classes = [
|
|
20
|
+
baseClass,
|
|
21
|
+
`${baseClass}--${variant}`,
|
|
22
|
+
`${baseClass}--${tone}`,
|
|
23
|
+
className
|
|
24
|
+
].filter(Boolean).join(" ");
|
|
25
|
+
const dotRadius = strokeWidth * 1.5;
|
|
26
|
+
const pad = dotRadius + strokeWidth / 2;
|
|
27
|
+
const innerWidth = Math.max(width - pad * 2, 0);
|
|
28
|
+
const innerHeight = Math.max(height - pad * 2, 0);
|
|
29
|
+
const min = Math.min(...data);
|
|
30
|
+
const max = Math.max(...data);
|
|
31
|
+
const range = max - min;
|
|
32
|
+
const points = data.map((value, i) => ({
|
|
33
|
+
x: round(
|
|
34
|
+
data.length < 2 ? pad + innerWidth : pad + i / (data.length - 1) * innerWidth
|
|
35
|
+
),
|
|
36
|
+
// A flat series (range 0) draws a horizontal midline instead of dividing by zero.
|
|
37
|
+
y: round(
|
|
38
|
+
range === 0 ? pad + innerHeight / 2 : pad + (1 - (value - min) / range) * innerHeight
|
|
39
|
+
)
|
|
40
|
+
}));
|
|
41
|
+
const lastPoint = points.length > 0 ? points[points.length - 1] : void 0;
|
|
42
|
+
const hasLine = points.length >= 2;
|
|
43
|
+
const linePath = hasLine ? `M ${points.map((p) => `${p.x} ${p.y}`).join(" L ")}` : void 0;
|
|
44
|
+
const baseline = round(height - pad);
|
|
45
|
+
const areaPath = hasLine && variant === "area" ? `${linePath} L ${points[points.length - 1].x} ${baseline} L ${points[0].x} ${baseline} Z` : void 0;
|
|
46
|
+
return /* @__PURE__ */ jsxs(
|
|
47
|
+
"svg",
|
|
48
|
+
{
|
|
49
|
+
...rest,
|
|
50
|
+
ref,
|
|
51
|
+
className: classes,
|
|
52
|
+
viewBox: `0 0 ${width} ${height}`,
|
|
53
|
+
width,
|
|
54
|
+
height,
|
|
55
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
56
|
+
role: label ? "img" : void 0,
|
|
57
|
+
"aria-label": label,
|
|
58
|
+
"aria-hidden": label ? void 0 : true,
|
|
59
|
+
children: [
|
|
60
|
+
areaPath && /* @__PURE__ */ jsx("path", { className: `${baseClass}__fill`, d: areaPath }),
|
|
61
|
+
linePath && /* @__PURE__ */ jsx(
|
|
62
|
+
"path",
|
|
63
|
+
{
|
|
64
|
+
className: `${baseClass}__line`,
|
|
65
|
+
d: linePath,
|
|
66
|
+
strokeWidth
|
|
67
|
+
}
|
|
68
|
+
),
|
|
69
|
+
showDot && lastPoint && /* @__PURE__ */ jsx(
|
|
70
|
+
"circle",
|
|
71
|
+
{
|
|
72
|
+
className: `${baseClass}__dot`,
|
|
73
|
+
cx: lastPoint.x,
|
|
74
|
+
cy: lastPoint.y,
|
|
75
|
+
r: dotRadius
|
|
76
|
+
}
|
|
77
|
+
)
|
|
78
|
+
]
|
|
79
|
+
}
|
|
80
|
+
);
|
|
81
|
+
}
|
|
82
|
+
);
|
|
83
|
+
Sparkline.displayName = "Sparkline";
|
|
84
|
+
export {
|
|
85
|
+
Sparkline
|
|
86
|
+
};
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
/* ============================================
|
|
2
|
+
STEPPER COMPONENT
|
|
3
|
+
Step-by-step progress indicator for wizards
|
|
4
|
+
and multi-stage flows
|
|
5
|
+
============================================ */
|
|
6
|
+
|
|
7
|
+
/* Base */
|
|
8
|
+
|
|
9
|
+
.ds-stepper {
|
|
10
|
+
display: flex;
|
|
11
|
+
align-items: flex-start;
|
|
12
|
+
list-style: none;
|
|
13
|
+
margin: 0;
|
|
14
|
+
padding: 0;
|
|
15
|
+
width: 100%;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
.ds-stepper__step {
|
|
19
|
+
position: relative;
|
|
20
|
+
flex: 1 1 0;
|
|
21
|
+
min-width: 0;
|
|
22
|
+
display: flex;
|
|
23
|
+
justify-content: center;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/* Shared inner layout — a <button> when the step is clickable,
|
|
27
|
+
a <span> otherwise */
|
|
28
|
+
.ds-stepper__content {
|
|
29
|
+
display: flex;
|
|
30
|
+
flex-direction: column;
|
|
31
|
+
align-items: center;
|
|
32
|
+
gap: var(--gap-sm); /* 8px */
|
|
33
|
+
padding: 0;
|
|
34
|
+
border: none;
|
|
35
|
+
background-color: transparent;
|
|
36
|
+
font-family: inherit;
|
|
37
|
+
text-align: center;
|
|
38
|
+
color: inherit;
|
|
39
|
+
min-width: 0;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
button.ds-stepper__content {
|
|
43
|
+
cursor: pointer;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
button.ds-stepper__content:focus-visible {
|
|
47
|
+
outline: 2px solid var(--color-action-primary-bg);
|
|
48
|
+
outline-offset: 2px;
|
|
49
|
+
border-radius: var(--radius-sm);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/* ============================================
|
|
53
|
+
INDICATOR
|
|
54
|
+
32px circle — check for complete, number
|
|
55
|
+
for active and upcoming
|
|
56
|
+
============================================ */
|
|
57
|
+
|
|
58
|
+
.ds-stepper__indicator {
|
|
59
|
+
--icon-size: var(--icon-size-sm);
|
|
60
|
+
display: inline-flex;
|
|
61
|
+
align-items: center;
|
|
62
|
+
justify-content: center;
|
|
63
|
+
width: 32px;
|
|
64
|
+
height: 32px;
|
|
65
|
+
flex-shrink: 0;
|
|
66
|
+
border: var(--border-xs) solid transparent;
|
|
67
|
+
border-radius: var(--radius-full);
|
|
68
|
+
font-family: var(--font-paragraph-sm-em-family);
|
|
69
|
+
font-size: var(--font-paragraph-sm-em-size);
|
|
70
|
+
font-weight: var(--font-paragraph-sm-em-weight);
|
|
71
|
+
line-height: var(--font-paragraph-sm-em-line-height);
|
|
72
|
+
letter-spacing: var(--font-paragraph-sm-em-letter-spacing);
|
|
73
|
+
transition:
|
|
74
|
+
background-color var(--motion-duration-base) var(--motion-ease-standard),
|
|
75
|
+
border-color var(--motion-duration-base) var(--motion-ease-standard),
|
|
76
|
+
color var(--motion-duration-base) var(--motion-ease-standard);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/* States */
|
|
80
|
+
|
|
81
|
+
.ds-stepper__step--complete .ds-stepper__indicator {
|
|
82
|
+
border-color: var(--color-action-primary-border-secondary);
|
|
83
|
+
color: var(--color-action-primary-text-tertiary);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
.ds-stepper__step--active .ds-stepper__indicator {
|
|
87
|
+
background-color: var(--color-action-primary-bg);
|
|
88
|
+
border-color: var(--color-action-primary-bg);
|
|
89
|
+
color: var(--color-action-primary-text);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
.ds-stepper__step--upcoming .ds-stepper__indicator {
|
|
93
|
+
border-color: var(--color-bg-container-border);
|
|
94
|
+
color: var(--color-text-tertiary);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
button.ds-stepper__content:hover .ds-stepper__indicator {
|
|
98
|
+
background-color: var(--color-action-passive-bg-hover);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
.ds-stepper__step--active button.ds-stepper__content:hover .ds-stepper__indicator {
|
|
102
|
+
background-color: var(--color-action-primary-bg-hover);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/* ============================================
|
|
106
|
+
LABEL + DESCRIPTION
|
|
107
|
+
============================================ */
|
|
108
|
+
|
|
109
|
+
.ds-stepper__text {
|
|
110
|
+
display: flex;
|
|
111
|
+
flex-direction: column;
|
|
112
|
+
gap: var(--gap-xxs); /* 2px */
|
|
113
|
+
min-width: 0;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
.ds-stepper__label {
|
|
117
|
+
font-family: var(--font-paragraph-sm-em-family);
|
|
118
|
+
font-size: var(--font-paragraph-sm-em-size);
|
|
119
|
+
font-weight: var(--font-paragraph-sm-em-weight);
|
|
120
|
+
line-height: var(--font-paragraph-sm-em-line-height);
|
|
121
|
+
letter-spacing: var(--font-paragraph-sm-em-letter-spacing);
|
|
122
|
+
color: var(--color-text-secondary);
|
|
123
|
+
transition: color var(--motion-duration-base) var(--motion-ease-standard);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
.ds-stepper__step--active .ds-stepper__label {
|
|
127
|
+
color: var(--color-text-primary);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
.ds-stepper__step--upcoming .ds-stepper__label {
|
|
131
|
+
color: var(--color-text-tertiary);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
button.ds-stepper__content:hover .ds-stepper__label {
|
|
135
|
+
color: var(--color-text-primary);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
.ds-stepper__description {
|
|
139
|
+
font-family: var(--font-paragraph-sm-family);
|
|
140
|
+
font-size: var(--font-paragraph-sm-size);
|
|
141
|
+
font-weight: var(--font-paragraph-sm-weight);
|
|
142
|
+
line-height: var(--font-paragraph-sm-line-height);
|
|
143
|
+
letter-spacing: var(--font-paragraph-sm-letter-spacing);
|
|
144
|
+
color: var(--color-text-tertiary);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/* ============================================
|
|
148
|
+
CONNECTOR
|
|
149
|
+
Drawn between an indicator and the next;
|
|
150
|
+
turns action-coloured behind completed steps
|
|
151
|
+
============================================ */
|
|
152
|
+
|
|
153
|
+
.ds-stepper__connector {
|
|
154
|
+
position: absolute;
|
|
155
|
+
top: 16px; /* vertical centre of the 32px indicator */
|
|
156
|
+
left: calc(50% + 24px);
|
|
157
|
+
right: calc(-50% + 24px);
|
|
158
|
+
height: var(--border-md);
|
|
159
|
+
transform: translateY(-50%);
|
|
160
|
+
background-color: var(--color-divider);
|
|
161
|
+
transition: background-color var(--motion-duration-base) var(--motion-ease-standard);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
.ds-stepper__step--complete .ds-stepper__connector {
|
|
165
|
+
background-color: var(--color-action-primary-border-secondary);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/* ============================================
|
|
169
|
+
VERTICAL
|
|
170
|
+
============================================ */
|
|
171
|
+
|
|
172
|
+
.ds-stepper--vertical {
|
|
173
|
+
flex-direction: column;
|
|
174
|
+
align-items: stretch;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
.ds-stepper--vertical .ds-stepper__step {
|
|
178
|
+
flex: none;
|
|
179
|
+
justify-content: flex-start;
|
|
180
|
+
padding-bottom: var(--gap-lg); /* 20px */
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
.ds-stepper--vertical .ds-stepper__step:last-child {
|
|
184
|
+
padding-bottom: 0;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
.ds-stepper--vertical .ds-stepper__content {
|
|
188
|
+
flex-direction: row;
|
|
189
|
+
align-items: flex-start;
|
|
190
|
+
gap: var(--gap-sm-md); /* 12px */
|
|
191
|
+
text-align: left;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/* Centre the first label line on the 32px indicator */
|
|
195
|
+
.ds-stepper--vertical .ds-stepper__text {
|
|
196
|
+
padding-top: calc((32px - var(--font-paragraph-sm-em-line-height)) / 2);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
.ds-stepper--vertical .ds-stepper__connector {
|
|
200
|
+
top: calc(32px + var(--gap-xs));
|
|
201
|
+
bottom: var(--gap-xs);
|
|
202
|
+
left: 16px; /* horizontal centre of the 32px indicator */
|
|
203
|
+
right: auto;
|
|
204
|
+
width: var(--border-md);
|
|
205
|
+
height: auto;
|
|
206
|
+
transform: translateX(-50%);
|
|
207
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { default as React } from 'react';
|
|
2
|
+
/** A single step in a Stepper flow. */
|
|
3
|
+
export type StepperStep = {
|
|
4
|
+
/** Step label shown beside or below the indicator */
|
|
5
|
+
label: string;
|
|
6
|
+
/** Optional supporting line rendered under the label */
|
|
7
|
+
description?: string;
|
|
8
|
+
};
|
|
9
|
+
/** Props owned by Stepper itself — everything else falls through to the `<ol>`. */
|
|
10
|
+
type StepperOwnProps = {
|
|
11
|
+
/** Ordered list of steps in the flow */
|
|
12
|
+
steps: StepperStep[];
|
|
13
|
+
/**
|
|
14
|
+
* Index of the active step. Steps before it render as complete (check
|
|
15
|
+
* icon), steps after it as upcoming (step number). Pass `steps.length`
|
|
16
|
+
* to mark the whole flow complete.
|
|
17
|
+
*/
|
|
18
|
+
activeStep: number;
|
|
19
|
+
/**
|
|
20
|
+
* Callback with the clicked step's index. When provided, completed steps
|
|
21
|
+
* and the active step become buttons so the reader can revisit an earlier
|
|
22
|
+
* stage; upcoming steps stay non-interactive either way.
|
|
23
|
+
*/
|
|
24
|
+
onStepClick?: (index: number) => void;
|
|
25
|
+
/** Layout direction */
|
|
26
|
+
orientation?: 'horizontal' | 'vertical';
|
|
27
|
+
/** Additional CSS classes */
|
|
28
|
+
className?: string;
|
|
29
|
+
};
|
|
30
|
+
export interface StepperProps extends StepperOwnProps, Omit<React.ComponentPropsWithoutRef<'ol'>, keyof StepperOwnProps> {
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Stepper component — step-by-step progress indicator for wizards and
|
|
34
|
+
* multi-stage flows. Renders an ordered list of steps with CSS-drawn
|
|
35
|
+
* connectors: completed steps show a check, the active step carries
|
|
36
|
+
* `aria-current="step"`, and upcoming steps show their number.
|
|
37
|
+
*
|
|
38
|
+
* Forwards a ref to the `<ol>` and spreads unrecognised props onto it.
|
|
39
|
+
*/
|
|
40
|
+
export declare const Stepper: React.ForwardRefExoticComponent<StepperProps & React.RefAttributes<HTMLOListElement>>;
|
|
41
|
+
export {};
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import React from "react";
|
|
4
|
+
import "./Stepper.css";
|
|
5
|
+
import "../../fonts/material-symbols.css";
|
|
6
|
+
const Stepper = React.forwardRef(
|
|
7
|
+
({
|
|
8
|
+
steps,
|
|
9
|
+
activeStep,
|
|
10
|
+
onStepClick,
|
|
11
|
+
orientation = "horizontal",
|
|
12
|
+
className = "",
|
|
13
|
+
...rest
|
|
14
|
+
}, ref) => {
|
|
15
|
+
const baseClass = "ds-stepper";
|
|
16
|
+
const classes = [
|
|
17
|
+
baseClass,
|
|
18
|
+
orientation === "vertical" && `${baseClass}--vertical`,
|
|
19
|
+
className
|
|
20
|
+
].filter(Boolean).join(" ");
|
|
21
|
+
return /* @__PURE__ */ jsx("ol", { ...rest, ref, className: classes, children: steps.map((step, index) => {
|
|
22
|
+
const status = index < activeStep ? "complete" : index === activeStep ? "active" : "upcoming";
|
|
23
|
+
const interactive = Boolean(onStepClick) && status !== "upcoming";
|
|
24
|
+
const indicator = /* @__PURE__ */ jsx("span", { className: `${baseClass}__indicator`, "aria-hidden": "true", children: status === "complete" ? /* @__PURE__ */ jsx("span", { className: "material-symbols-rounded", children: "check" }) : index + 1 });
|
|
25
|
+
const text = /* @__PURE__ */ jsxs("span", { className: `${baseClass}__text`, children: [
|
|
26
|
+
/* @__PURE__ */ jsx("span", { className: `${baseClass}__label`, children: step.label }),
|
|
27
|
+
step.description && /* @__PURE__ */ jsx("span", { className: `${baseClass}__description`, children: step.description })
|
|
28
|
+
] });
|
|
29
|
+
return /* @__PURE__ */ jsxs(
|
|
30
|
+
"li",
|
|
31
|
+
{
|
|
32
|
+
className: `${baseClass}__step ${baseClass}__step--${status}`,
|
|
33
|
+
"aria-current": status === "active" ? "step" : void 0,
|
|
34
|
+
children: [
|
|
35
|
+
interactive ? /* @__PURE__ */ jsxs(
|
|
36
|
+
"button",
|
|
37
|
+
{
|
|
38
|
+
type: "button",
|
|
39
|
+
className: `${baseClass}__content`,
|
|
40
|
+
onClick: () => onStepClick?.(index),
|
|
41
|
+
children: [
|
|
42
|
+
indicator,
|
|
43
|
+
text
|
|
44
|
+
]
|
|
45
|
+
}
|
|
46
|
+
) : /* @__PURE__ */ jsxs("span", { className: `${baseClass}__content`, children: [
|
|
47
|
+
indicator,
|
|
48
|
+
text
|
|
49
|
+
] }),
|
|
50
|
+
index < steps.length - 1 && /* @__PURE__ */ jsx("span", { className: `${baseClass}__connector`, "aria-hidden": "true" })
|
|
51
|
+
]
|
|
52
|
+
},
|
|
53
|
+
index
|
|
54
|
+
);
|
|
55
|
+
}) });
|
|
56
|
+
}
|
|
57
|
+
);
|
|
58
|
+
Stepper.displayName = "Stepper";
|
|
59
|
+
export {
|
|
60
|
+
Stepper
|
|
61
|
+
};
|
package/components/Tabs/Tabs.js
CHANGED