rift-ds 1.0.0 → 1.1.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.
Files changed (37) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +11 -3
  3. package/components/AppLayout/AppLayout.css +13 -3
  4. package/components/AppLayout/AppLayout.d.ts +5 -1
  5. package/components/AppLayout/AppLayout.js +4 -0
  6. package/components/AppSidebar/AppSidebar.css +237 -0
  7. package/components/AppSidebar/AppSidebar.d.ts +32 -3
  8. package/components/AppSidebar/AppSidebar.js +187 -16
  9. package/components/BottomNav/BottomNav.css +417 -0
  10. package/components/BottomNav/BottomNav.d.ts +73 -0
  11. package/components/BottomNav/BottomNav.js +100 -0
  12. package/components/Chart/AreaChart.js +67 -56
  13. package/components/Chart/BarChart.js +67 -56
  14. package/components/Chart/ComboChart.js +93 -82
  15. package/components/Chart/LineChart.js +59 -48
  16. package/components/Chart/PieChart.js +56 -45
  17. package/components/Chart/RadarChart.js +60 -49
  18. package/components/Chart/RadialChart.js +70 -59
  19. package/components/Chart/ScatterChart.js +61 -50
  20. package/components/Chart/StackedBarChart.js +64 -53
  21. package/components/Chart/Treemap.js +34 -23
  22. package/components/Nav/Nav.d.ts +11 -3
  23. package/components/Nav/Nav.js +3 -2
  24. package/components/SidebarPanel/SidebarPanel.css +291 -0
  25. package/components/SidebarPanel/SidebarPanel.d.ts +45 -0
  26. package/components/SidebarPanel/SidebarPanel.js +166 -0
  27. package/components/Stepper/Stepper.d.ts +3 -1
  28. package/components/Stepper/Stepper.js +1 -1
  29. package/components/TopAppBar/TopAppBar.css +464 -0
  30. package/components/TopAppBar/TopAppBar.d.ts +80 -0
  31. package/components/TopAppBar/TopAppBar.js +138 -0
  32. package/components/registry.json +25 -1
  33. package/components/registry.json.d.ts +25 -1
  34. package/components/registry.json.js +1 -1
  35. package/index.d.ts +3 -0
  36. package/index.js +6 -0
  37. package/package.json +5 -1
@@ -1,5 +1,5 @@
1
1
  import { jsx, jsxs } from "react/jsx-runtime";
2
- import { useCallback } from "react";
2
+ import { useId, useCallback } from "react";
3
3
  import { ResponsiveContainer, BarChart, CartesianGrid, XAxis, YAxis, Tooltip, Bar } from "recharts";
4
4
  import { getChartSeriesColors } from "./palette.js";
5
5
  import "./Chart.css";
@@ -29,6 +29,8 @@ const StackedBarChart = ({
29
29
  bare = false,
30
30
  className = ""
31
31
  }) => {
32
+ const titleId = useId();
33
+ const subtitleId = useId();
32
34
  const baseClass = "ds-chart";
33
35
  const classes = [baseClass, bare ? `${baseClass}--bare` : "", className].filter(Boolean).join(" ");
34
36
  const textSecondary = getCSSVar("--color-text-secondary", "#303030");
@@ -40,58 +42,67 @@ const StackedBarChart = ({
40
42
  (props) => /* @__PURE__ */ jsx(StackedTooltip, { ...props }),
41
43
  []
42
44
  );
43
- return /* @__PURE__ */ jsxs("div", { className: classes, children: [
44
- (title || subtitle || summaryItems) && /* @__PURE__ */ jsxs("div", { className: `${baseClass}__header`, children: [
45
- /* @__PURE__ */ jsxs("div", { className: `${baseClass}__header-text`, children: [
46
- title && /* @__PURE__ */ jsx("h3", { className: `${baseClass}__title`, children: title }),
47
- subtitle && /* @__PURE__ */ jsx("p", { className: `${baseClass}__subtitle`, children: subtitle })
48
- ] }),
49
- summaryItems && summaryItems.length > 0 && /* @__PURE__ */ jsx("div", { className: `${baseClass}__summary`, children: summaryItems.map((item, i) => /* @__PURE__ */ jsxs("div", { className: `${baseClass}__summary-item`, children: [
50
- /* @__PURE__ */ jsx("span", { className: `${baseClass}__summary-label`, children: item.label }),
51
- /* @__PURE__ */ jsx("span", { className: `${baseClass}__summary-value`, children: typeof item.value === "number" ? item.value.toLocaleString() : item.value })
52
- ] }, i)) })
53
- ] }),
54
- /* @__PURE__ */ jsx("div", { className: `${baseClass}__body`, children: /* @__PURE__ */ jsx(ResponsiveContainer, { width: "100%", height, children: /* @__PURE__ */ jsxs(BarChart, { data, margin: { top: 8, right: 4, bottom: 0, left: -12 }, children: [
55
- /* @__PURE__ */ jsx(CartesianGrid, { vertical: false, stroke: gridColor }),
56
- /* @__PURE__ */ jsx(
57
- XAxis,
58
- {
59
- dataKey: xKey,
60
- tick: { fill: textSecondary, fontSize: 12 },
61
- tickLine: false,
62
- axisLine: { stroke: gridColor }
63
- }
64
- ),
65
- /* @__PURE__ */ jsx(
66
- YAxis,
67
- {
68
- tick: { fill: textSecondary, fontSize: 12 },
69
- tickLine: false,
70
- axisLine: false,
71
- tickFormatter: yAxisFormatter
72
- }
73
- ),
74
- /* @__PURE__ */ jsx(
75
- Tooltip,
76
- {
77
- content: renderTooltip,
78
- cursor: { fill: cursorColor, opacity: 0.4 }
79
- }
80
- ),
81
- series.map((s, i) => /* @__PURE__ */ jsx(
82
- Bar,
83
- {
84
- dataKey: s.dataKey,
85
- name: s.label,
86
- fill: s.color || defaultColors[i % defaultColors.length],
87
- stackId: "stack",
88
- radius: i === series.length - 1 ? [3, 3, 0, 0] : [0, 0, 0, 0],
89
- maxBarSize: 32
90
- },
91
- s.dataKey
92
- ))
93
- ] }) }) })
94
- ] });
45
+ return /* @__PURE__ */ jsxs(
46
+ "div",
47
+ {
48
+ className: classes,
49
+ role: "figure",
50
+ "aria-labelledby": title ? titleId : void 0,
51
+ "aria-describedby": subtitle ? subtitleId : void 0,
52
+ children: [
53
+ (title || subtitle || summaryItems) && /* @__PURE__ */ jsxs("div", { className: `${baseClass}__header`, children: [
54
+ /* @__PURE__ */ jsxs("div", { className: `${baseClass}__header-text`, children: [
55
+ title && /* @__PURE__ */ jsx("h3", { id: titleId, className: `${baseClass}__title`, children: title }),
56
+ subtitle && /* @__PURE__ */ jsx("p", { id: subtitleId, className: `${baseClass}__subtitle`, children: subtitle })
57
+ ] }),
58
+ summaryItems && summaryItems.length > 0 && /* @__PURE__ */ jsx("div", { className: `${baseClass}__summary`, children: summaryItems.map((item, i) => /* @__PURE__ */ jsxs("div", { className: `${baseClass}__summary-item`, children: [
59
+ /* @__PURE__ */ jsx("span", { className: `${baseClass}__summary-label`, children: item.label }),
60
+ /* @__PURE__ */ jsx("span", { className: `${baseClass}__summary-value`, children: typeof item.value === "number" ? item.value.toLocaleString() : item.value })
61
+ ] }, i)) })
62
+ ] }),
63
+ /* @__PURE__ */ jsx("div", { className: `${baseClass}__body`, children: /* @__PURE__ */ jsx(ResponsiveContainer, { width: "100%", height, children: /* @__PURE__ */ jsxs(BarChart, { data, margin: { top: 8, right: 4, bottom: 0, left: -12 }, children: [
64
+ /* @__PURE__ */ jsx(CartesianGrid, { vertical: false, stroke: gridColor }),
65
+ /* @__PURE__ */ jsx(
66
+ XAxis,
67
+ {
68
+ dataKey: xKey,
69
+ tick: { fill: textSecondary, fontSize: 12 },
70
+ tickLine: false,
71
+ axisLine: { stroke: gridColor }
72
+ }
73
+ ),
74
+ /* @__PURE__ */ jsx(
75
+ YAxis,
76
+ {
77
+ tick: { fill: textSecondary, fontSize: 12 },
78
+ tickLine: false,
79
+ axisLine: false,
80
+ tickFormatter: yAxisFormatter
81
+ }
82
+ ),
83
+ /* @__PURE__ */ jsx(
84
+ Tooltip,
85
+ {
86
+ content: renderTooltip,
87
+ cursor: { fill: cursorColor, opacity: 0.4 }
88
+ }
89
+ ),
90
+ series.map((s, i) => /* @__PURE__ */ jsx(
91
+ Bar,
92
+ {
93
+ dataKey: s.dataKey,
94
+ name: s.label,
95
+ fill: s.color || defaultColors[i % defaultColors.length],
96
+ stackId: "stack",
97
+ radius: i === series.length - 1 ? [3, 3, 0, 0] : [0, 0, 0, 0],
98
+ maxBarSize: 32
99
+ },
100
+ s.dataKey
101
+ ))
102
+ ] }) }) })
103
+ ]
104
+ }
105
+ );
95
106
  };
96
107
  export {
97
108
  StackedBarChart
@@ -1,5 +1,5 @@
1
1
  import { jsx, jsxs } from "react/jsx-runtime";
2
- import { useCallback } from "react";
2
+ import { useId, useCallback } from "react";
3
3
  import { ResponsiveContainer, Treemap as Treemap$1, Tooltip } from "recharts";
4
4
  import { getChartSeriesColors } from "./palette.js";
5
5
  import "./Chart.css";
@@ -60,6 +60,8 @@ const Treemap = ({
60
60
  bare = false,
61
61
  className = ""
62
62
  }) => {
63
+ const titleId = useId();
64
+ const subtitleId = useId();
63
65
  const baseClass = "ds-chart";
64
66
  const classes = [baseClass, bare ? `${baseClass}--bare` : "", className].filter(Boolean).join(" ");
65
67
  const renderTooltip = useCallback(
@@ -67,28 +69,37 @@ const Treemap = ({
67
69
  (props) => /* @__PURE__ */ jsx(TreemapTooltip, { ...props }),
68
70
  []
69
71
  );
70
- return /* @__PURE__ */ jsxs("div", { className: classes, children: [
71
- (title || subtitle || summaryItems) && /* @__PURE__ */ jsxs("div", { className: `${baseClass}__header`, children: [
72
- /* @__PURE__ */ jsxs("div", { className: `${baseClass}__header-text`, children: [
73
- title && /* @__PURE__ */ jsx("h3", { className: `${baseClass}__title`, children: title }),
74
- subtitle && /* @__PURE__ */ jsx("p", { className: `${baseClass}__subtitle`, children: subtitle })
75
- ] }),
76
- summaryItems && summaryItems.length > 0 && /* @__PURE__ */ jsx("div", { className: `${baseClass}__summary`, children: summaryItems.map((item, i) => /* @__PURE__ */ jsxs("div", { className: `${baseClass}__summary-item`, children: [
77
- /* @__PURE__ */ jsx("span", { className: `${baseClass}__summary-label`, children: item.label }),
78
- /* @__PURE__ */ jsx("span", { className: `${baseClass}__summary-value`, children: typeof item.value === "number" ? item.value.toLocaleString() : item.value })
79
- ] }, i)) })
80
- ] }),
81
- /* @__PURE__ */ jsx("div", { className: `${baseClass}__body`, children: /* @__PURE__ */ jsx(ResponsiveContainer, { width: "100%", height, children: /* @__PURE__ */ jsx(
82
- Treemap$1,
83
- {
84
- data,
85
- dataKey,
86
- aspectRatio: 4 / 3,
87
- content: /* @__PURE__ */ jsx(TreemapContent, {}),
88
- children: /* @__PURE__ */ jsx(Tooltip, { content: renderTooltip })
89
- }
90
- ) }) })
91
- ] });
72
+ return /* @__PURE__ */ jsxs(
73
+ "div",
74
+ {
75
+ className: classes,
76
+ role: "figure",
77
+ "aria-labelledby": title ? titleId : void 0,
78
+ "aria-describedby": subtitle ? subtitleId : void 0,
79
+ children: [
80
+ (title || subtitle || summaryItems) && /* @__PURE__ */ jsxs("div", { className: `${baseClass}__header`, children: [
81
+ /* @__PURE__ */ jsxs("div", { className: `${baseClass}__header-text`, children: [
82
+ title && /* @__PURE__ */ jsx("h3", { id: titleId, className: `${baseClass}__title`, children: title }),
83
+ subtitle && /* @__PURE__ */ jsx("p", { id: subtitleId, className: `${baseClass}__subtitle`, children: subtitle })
84
+ ] }),
85
+ summaryItems && summaryItems.length > 0 && /* @__PURE__ */ jsx("div", { className: `${baseClass}__summary`, children: summaryItems.map((item, i) => /* @__PURE__ */ jsxs("div", { className: `${baseClass}__summary-item`, children: [
86
+ /* @__PURE__ */ jsx("span", { className: `${baseClass}__summary-label`, children: item.label }),
87
+ /* @__PURE__ */ jsx("span", { className: `${baseClass}__summary-value`, children: typeof item.value === "number" ? item.value.toLocaleString() : item.value })
88
+ ] }, i)) })
89
+ ] }),
90
+ /* @__PURE__ */ jsx("div", { className: `${baseClass}__body`, children: /* @__PURE__ */ jsx(ResponsiveContainer, { width: "100%", height, children: /* @__PURE__ */ jsx(
91
+ Treemap$1,
92
+ {
93
+ data,
94
+ dataKey,
95
+ aspectRatio: 4 / 3,
96
+ content: /* @__PURE__ */ jsx(TreemapContent, {}),
97
+ children: /* @__PURE__ */ jsx(Tooltip, { content: renderTooltip })
98
+ }
99
+ ) }) })
100
+ ]
101
+ }
102
+ );
92
103
  };
93
104
  export {
94
105
  Treemap
@@ -1,6 +1,7 @@
1
1
  import { default as React } from 'react';
2
2
  import { ButtonGroupProps } from '../ButtonGroup/ButtonGroup';
3
- export interface NavProps {
3
+ /** Props owned by Nav itself — everything else falls through to the `<nav>`. */
4
+ type NavOwnProps = {
4
5
  /** Brand/logo text */
5
6
  brandText?: string;
6
7
  /** Brand icon element (img, svg, etc.) */
@@ -11,9 +12,16 @@ export interface NavProps {
11
12
  trailing?: React.ReactNode;
12
13
  /** Additional CSS classes */
13
14
  className?: string;
15
+ };
16
+ export interface NavProps extends NavOwnProps, Omit<React.ComponentPropsWithoutRef<'nav'>, keyof NavOwnProps> {
14
17
  }
15
18
  /**
16
19
  * Nav component from Figma design system
17
- * Renders a horizontal navigation bar using ButtonGroup
20
+ * Renders a horizontal navigation bar using ButtonGroup.
21
+ *
22
+ * The landmark is labelled "Main" by default so assistive tech can tell it
23
+ * apart from any other navigation on the page; pass `aria-label` to name it
24
+ * differently. Unrecognised props are spread onto the `<nav>`.
18
25
  */
19
- export declare const Nav: ({ brandText, brandIcon, buttons, trailing, className, }: NavProps) => React.JSX.Element;
26
+ export declare const Nav: ({ brandText, brandIcon, buttons, trailing, className, ...rest }: NavProps) => React.JSX.Element;
27
+ export {};
@@ -7,10 +7,11 @@ const Nav = ({
7
7
  brandIcon,
8
8
  buttons,
9
9
  trailing,
10
- className = ""
10
+ className = "",
11
+ ...rest
11
12
  }) => {
12
13
  const baseClass = "ds-nav";
13
- return /* @__PURE__ */ jsxs("nav", { className: `${baseClass} ${className}`, children: [
14
+ return /* @__PURE__ */ jsxs("nav", { "aria-label": "Main", ...rest, className: `${baseClass} ${className}`, children: [
14
15
  (brandText || brandIcon) && /* @__PURE__ */ jsxs("div", { className: `${baseClass}__brand`, children: [
15
16
  brandIcon && /* @__PURE__ */ jsx("span", { className: `${baseClass}__brand-icon`, children: brandIcon }),
16
17
  brandText && /* @__PURE__ */ jsx("span", { className: `${baseClass}__brand-text`, children: brandText })
@@ -0,0 +1,291 @@
1
+ /* ============================================
2
+ SIDEBAR PANEL — the secondary navigation column
3
+ A heading, optional group labels, flat rows and
4
+ one level of accordion nesting. Row geometry
5
+ repeats AppSidebar's sub-items (40px pill rows,
6
+ 12px edge inset, tree-line connectors), so the
7
+ two columns read as one navigation system.
8
+ ============================================ */
9
+
10
+ /* ============================================
11
+ WRAPPER
12
+ ============================================ */
13
+ .ds-sidebar-panel {
14
+ display: flex;
15
+ flex-direction: column;
16
+ gap: var(--gap-400);
17
+ width: var(--ds-sidebar-panel-width, 240px);
18
+ height: 100%;
19
+ min-height: 0;
20
+ padding: var(--padding-500) 0;
21
+ box-sizing: border-box;
22
+ background-color: var(--color-bg-page-primary);
23
+ color: var(--color-text-primary);
24
+ }
25
+
26
+ /* ============================================
27
+ BACK ROW — the drill-in's way out
28
+ ============================================ */
29
+ .ds-sidebar-panel__back {
30
+ display: flex;
31
+ align-items: center;
32
+ gap: var(--gap-100);
33
+ height: 40px;
34
+ margin: 0 var(--padding-300);
35
+ padding: var(--padding-200);
36
+ /* The chevron glyph carries its own side bearing, so the row drops its
37
+ left padding and the glyph's ink lands on the rows' text edge. */
38
+ padding-left: 0;
39
+ border: none;
40
+ border-radius: var(--radius-pill);
41
+ background: transparent;
42
+ color: var(--color-text-secondary);
43
+ cursor: pointer;
44
+ text-align: left;
45
+ flex-shrink: 0;
46
+ transition: background-color var(--motion-duration-fast) var(--motion-ease-standard);
47
+ }
48
+
49
+ .ds-sidebar-panel__back:hover {
50
+ background-color: var(--color-action-passive-bg-hover);
51
+ }
52
+
53
+ .ds-sidebar-panel__back-icon {
54
+ --icon-size: var(--icon-size-600);
55
+ color: inherit;
56
+ }
57
+
58
+ /* ============================================
59
+ HEADER — title + collapse control
60
+ ============================================ */
61
+ .ds-sidebar-panel__header {
62
+ display: flex;
63
+ justify-content: space-between;
64
+ align-items: flex-start;
65
+ gap: var(--gap-200);
66
+ min-height: 24px;
67
+ padding: 0 var(--padding-500);
68
+ flex-shrink: 0;
69
+ }
70
+
71
+ .ds-sidebar-panel__title {
72
+ margin: 0;
73
+ font-family: var(--font-overline-family);
74
+ font-size: var(--font-overline-size);
75
+ font-weight: var(--font-overline-weight);
76
+ line-height: var(--font-overline-line-height);
77
+ letter-spacing: var(--font-overline-letter-spacing);
78
+ text-transform: uppercase;
79
+ color: var(--color-text-secondary);
80
+ min-width: 0;
81
+ /* Wraps rather than truncates: the heading names the section, and a
82
+ clipped name is the one thing the reader cannot recover from. */
83
+ overflow-wrap: anywhere;
84
+ }
85
+
86
+ .ds-sidebar-panel__collapse {
87
+ display: flex;
88
+ align-items: center;
89
+ justify-content: center;
90
+ width: 32px;
91
+ height: 32px;
92
+ /* Pulls the 32px hit area back so the 24px glyph sits on the same
93
+ right edge as the rows' badges and chevrons. */
94
+ margin-right: calc(var(--padding-100) * -1);
95
+ /* Centred on the heading's first line however many lines it wraps to. */
96
+ margin-top: calc((var(--font-overline-line-height) - 32px) / 2);
97
+ margin-bottom: calc((var(--font-overline-line-height) - 32px) / 2);
98
+ padding: 0;
99
+ flex-shrink: 0;
100
+ border: none;
101
+ border-radius: var(--radius-pill);
102
+ background: transparent;
103
+ color: var(--color-icon-primary);
104
+ cursor: pointer;
105
+ transition: background-color var(--motion-duration-fast) var(--motion-ease-standard);
106
+ }
107
+
108
+ .ds-sidebar-panel__collapse:hover {
109
+ background-color: var(--color-action-passive-bg-hover);
110
+ }
111
+
112
+ .ds-sidebar-panel__collapse .material-symbols-rounded {
113
+ --icon-size: var(--icon-size-600);
114
+ color: inherit;
115
+ }
116
+
117
+ /* ============================================
118
+ BODY + GROUPS
119
+ ============================================ */
120
+ .ds-sidebar-panel__body {
121
+ display: flex;
122
+ flex-direction: column;
123
+ gap: var(--gap-500);
124
+ flex: 1;
125
+ min-height: 0;
126
+ overflow-y: auto;
127
+ overflow-x: hidden;
128
+ }
129
+
130
+ .ds-sidebar-panel__group {
131
+ display: flex;
132
+ flex-direction: column;
133
+ gap: var(--gap-100);
134
+ }
135
+
136
+ .ds-sidebar-panel__group-label {
137
+ padding: 0 var(--padding-500);
138
+ font-family: var(--font-paragraph-sm-family);
139
+ font-size: var(--font-paragraph-sm-size);
140
+ font-weight: var(--font-paragraph-sm-weight);
141
+ line-height: var(--font-paragraph-sm-line-height);
142
+ color: var(--color-text-tertiary);
143
+ white-space: nowrap;
144
+ }
145
+
146
+ .ds-sidebar-panel__list,
147
+ .ds-sidebar-panel__nested-list {
148
+ display: flex;
149
+ flex-direction: column;
150
+ gap: var(--gap-100);
151
+ margin: 0;
152
+ padding: 0;
153
+ list-style: none;
154
+ }
155
+
156
+ /* ============================================
157
+ ROWS
158
+ ============================================ */
159
+ .ds-sidebar-panel__row {
160
+ display: flex;
161
+ align-items: center;
162
+ gap: var(--gap-200);
163
+ width: calc(100% - var(--padding-300) * 2);
164
+ height: 40px;
165
+ margin: 0 var(--padding-300);
166
+ padding: var(--padding-200);
167
+ box-sizing: border-box;
168
+ border: none;
169
+ border-radius: var(--radius-pill);
170
+ background: transparent;
171
+ color: var(--color-text-primary);
172
+ cursor: pointer;
173
+ text-align: left;
174
+ text-decoration: none;
175
+ transition: background-color var(--motion-duration-fast) var(--motion-ease-standard);
176
+ }
177
+
178
+ .ds-sidebar-panel__row:hover,
179
+ .ds-sidebar-panel__row--active {
180
+ background-color: var(--color-action-passive-bg-hover);
181
+ }
182
+
183
+ .ds-sidebar-panel__row:focus-visible,
184
+ .ds-sidebar-panel__back:focus-visible,
185
+ .ds-sidebar-panel__collapse:focus-visible {
186
+ outline: 2px solid var(--color-action-primary-bg);
187
+ outline-offset: -2px;
188
+ }
189
+
190
+ .ds-sidebar-panel__label {
191
+ flex: 1;
192
+ min-width: 0;
193
+ font-family: var(--font-paragraph-family);
194
+ font-size: var(--font-paragraph-size);
195
+ font-weight: var(--font-paragraph-weight);
196
+ line-height: var(--font-paragraph-line-height);
197
+ letter-spacing: var(--font-paragraph-letter-spacing);
198
+ color: inherit;
199
+ white-space: nowrap;
200
+ overflow: hidden;
201
+ text-overflow: ellipsis;
202
+ }
203
+
204
+ /* The current page and a collapsed accordion hiding it carry the same
205
+ emphasis, so a reader can find their place without opening anything. */
206
+ .ds-sidebar-panel__row--active .ds-sidebar-panel__label,
207
+ .ds-sidebar-panel__row--holds-active .ds-sidebar-panel__label {
208
+ font-family: var(--font-paragraph-emphasis-family);
209
+ font-weight: var(--font-paragraph-emphasis-weight);
210
+ }
211
+
212
+ .ds-sidebar-panel__badge {
213
+ flex-shrink: 0;
214
+ font-family: var(--font-caption-family);
215
+ font-size: var(--font-caption-size);
216
+ font-weight: var(--font-paragraph-sm-emphasis-weight);
217
+ line-height: var(--font-caption-line-height);
218
+ color: var(--color-text-secondary);
219
+ background-color: var(--color-bg-container-tertiary);
220
+ border-radius: var(--radius-pill);
221
+ padding: var(--padding-050) var(--padding-200);
222
+ }
223
+
224
+ /* On a filled row the pill steps back to the page ground, or it would
225
+ share the row's fill and dissolve into it. */
226
+ .ds-sidebar-panel__row--active .ds-sidebar-panel__badge,
227
+ .ds-sidebar-panel__row:hover .ds-sidebar-panel__badge {
228
+ background-color: var(--color-bg-page-primary);
229
+ }
230
+
231
+ .ds-sidebar-panel__chevron {
232
+ --icon-size: var(--icon-size-500);
233
+ flex-shrink: 0;
234
+ /* Pulls the glyph's side bearing out, so its ink ends on the same right
235
+ edge as a badge in the row above. */
236
+ margin-right: calc(var(--padding-100) * -1);
237
+ color: var(--color-icon-secondary);
238
+ transition: transform var(--motion-duration-slow) var(--motion-ease-emphasized);
239
+ }
240
+
241
+ .ds-sidebar-panel__chevron--open {
242
+ transform: rotate(180deg);
243
+ }
244
+
245
+ /* ============================================
246
+ NESTED ROWS (accordion children)
247
+ The grid-row trick animates to the content's
248
+ own height, with no max-height guess to clip.
249
+ ============================================ */
250
+ .ds-sidebar-panel__nested {
251
+ display: grid;
252
+ grid-template-rows: 0fr;
253
+ transition: grid-template-rows var(--motion-duration-slow) var(--motion-ease-emphasized);
254
+ }
255
+
256
+ .ds-sidebar-panel__nested--open {
257
+ grid-template-rows: 1fr;
258
+ }
259
+
260
+ .ds-sidebar-panel__nested-list {
261
+ min-height: 0;
262
+ overflow: hidden;
263
+ /* The tree line: aligned under the parent label's first letter, so the
264
+ children visibly hang from it (12px inset + 8px row padding). */
265
+ position: relative;
266
+ padding-top: var(--gap-100);
267
+ }
268
+
269
+ .ds-sidebar-panel__nested-list::before {
270
+ content: '';
271
+ position: absolute;
272
+ top: var(--gap-100);
273
+ bottom: 0;
274
+ left: calc(var(--padding-300) + var(--padding-200));
275
+ width: var(--border-025);
276
+ background-color: var(--color-bg-container-border);
277
+ }
278
+
279
+ .ds-sidebar-panel__row--nested {
280
+ width: calc(100% - var(--padding-300) * 2 - var(--padding-400));
281
+ margin-left: calc(var(--padding-300) + var(--padding-400));
282
+ }
283
+
284
+ /* Icons take the colour of the text around them, whatever a consumer
285
+ stylesheet sets on the icon font class globally. */
286
+ .ds-sidebar-panel .material-symbols-rounded {
287
+ color: inherit;
288
+ }
289
+ .ds-sidebar-panel .ds-sidebar-panel__chevron {
290
+ color: var(--color-icon-secondary);
291
+ }
@@ -0,0 +1,45 @@
1
+ import { default as React } from 'react';
2
+ export interface SidebarPanelItem {
3
+ /** Unique key for this item */
4
+ key: string;
5
+ /** Display label */
6
+ label: string;
7
+ /** Renders the item as a real link to this URL instead of a button. Ignored when the item has children, whose row is the accordion toggle */
8
+ href?: string;
9
+ /** Click handler — also fires on a link item, so a consumer can route client-side */
10
+ onClick?: () => void;
11
+ /** Trailing count pill, e.g. unread items */
12
+ badge?: string | number;
13
+ /** Group heading this item sits under; consecutive items sharing a group render beneath one heading */
14
+ group?: string;
15
+ /** Nested items — turns this row into an accordion. One level deep: grandchildren are not rendered */
16
+ children?: SidebarPanelItem[];
17
+ }
18
+ type SidebarPanelOwnProps = {
19
+ /** Items to list, optionally grouped with `group` and nested one level with `children` */
20
+ items: SidebarPanelItem[];
21
+ /** Heading at the top of the panel, usually the label of the item that opened it. Also the panel's accessible name */
22
+ title?: string;
23
+ /** Key of the item for the current page. Its accordion parent starts open */
24
+ activeKey?: string;
25
+ /** Keys of the accordion rows open on first render, in addition to the active item's parent */
26
+ defaultOpenKeys?: string[];
27
+ /** Renders a collapse button beside the heading and calls this when it is pressed */
28
+ onCollapse?: () => void;
29
+ /** Renders a back row above the heading and calls this when it is pressed; the drill-in navigation on small screens */
30
+ onBack?: () => void;
31
+ /** Label of the back row */
32
+ backLabel?: string;
33
+ /** Additional CSS classes */
34
+ className?: string;
35
+ };
36
+ export interface SidebarPanelProps extends SidebarPanelOwnProps, Omit<React.ComponentPropsWithoutRef<'nav'>, keyof SidebarPanelOwnProps> {
37
+ }
38
+ /**
39
+ * SidebarPanel — the secondary column of app navigation. Lists the pages
40
+ * inside one section, with optional group headings and one level of
41
+ * accordion nesting. Stands alone, or opens beside `AppSidebar` when its
42
+ * items carry deeper levels (see `subNav` there).
43
+ */
44
+ export declare const SidebarPanel: React.ForwardRefExoticComponent<SidebarPanelProps & React.RefAttributes<HTMLElement>>;
45
+ export {};