@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
package/README.md
CHANGED
|
@@ -18,7 +18,7 @@ The **live site** is the portfolio built on the design system. **Storybook** is
|
|
|
18
18
|
| Part | Description |
|
|
19
19
|
|---|---|
|
|
20
20
|
| **Portfolio website** (`/website`) | Next.js app with case studies, work history, writing, and about pages, all built exclusively with the design system components below. **[Live site →](https://robertritacca.com/)** |
|
|
21
|
-
| **Design system** (`/src`) | <!-- component-count -->
|
|
21
|
+
| **Design system** (`/src`) | <!-- component-count -->101<!-- /component-count --> React components, a three-tier token architecture, dark mode, a WebGL2 ambient background that themes itself from your tokens, and a full documentation site. Built to production standards. **[Storybook →](https://design-system-iota-one.vercel.app/?path=/docs/robr0-ds--docs)** |
|
|
22
22
|
| **AI layer** (`ai` components in `/src`, chat in `/website`, `/evals`) | A site-wide chat that answers questions about the work: built from the library's own `ai` components, grounded in a corpus generated from the site's published content, and scored by a golden-set eval. |
|
|
23
23
|
|
|
24
24
|
---
|
|
@@ -28,7 +28,7 @@ The **live site** is the portfolio built on the design system. **Storybook** is
|
|
|
28
28
|
### Components
|
|
29
29
|
|
|
30
30
|
<!-- component-list:start -->
|
|
31
|
-
Accordion · Agent plan · Agent status · AI button · Alert · Alert dialog · App layout · App sidebar · Avatar · Badge · Breadcrumb · Button · Button group · Card · Carousel ·
|
|
31
|
+
Accordion · Agent plan · Agent status · AI button · Alert · Alert dialog · Anchor nav · App layout · App sidebar · Area chart · Avatar · Badge · Bar chart · Breadcrumb · Button · Button group · Card · Carousel · Chat header · Chat marker · Chat message · Chat thread · Checkbox · Chip · Circular button · Code block · Code diff · Colour picker · Combobox · Command palette · Composer · Contact card · Context menu · Contribution graph · Data table · Date input · Date picker · Dialog · Divider · Document chip · Drawer · Dropdown · Dropdown menu · Empty state · Entity card · Event calendar · Field · Figure · File input · Input · Instructions · Interrupt card · Kbd · Line chart · Link list · Message actions · Message card · Model picker · Nav · Nav list · Notification centre · Number input · Pagination · Pie chart · Pin input · Popover · Progress bar · Prompt suggestions · Prose · Quote · Radar chart · Radial chart · Radio button · Reasoning · Scatter chart · Section title · Segmented control · Selection card · Shader field · Skeleton · Slider · Source chip · Sparkline · Spinner · Stacked bar chart · Stat · Stepper · Swatch · Table · Tabs · Tag input · Textarea · Time picker · Timeline · Toast · Toggle group · Toggle switch · Tool call · Tooltip · Tree view · Treemap
|
|
32
32
|
<!-- component-list:end -->
|
|
33
33
|
|
|
34
34
|
### Ambient background
|
|
@@ -71,7 +71,7 @@ import '@robr0/design-system/tokens/tokens.css';
|
|
|
71
71
|
import { Button, Card, Badge } from '@robr0/design-system';
|
|
72
72
|
```
|
|
73
73
|
|
|
74
|
-
Deep imports work too (`@robr0/design-system/components/Button/Button`).
|
|
74
|
+
Deep imports work too (`@robr0/design-system/components/Button/Button`). The Recharts-backed charts live behind a separate entry so the optional `recharts` peer dependency is only needed if you use them; Sparkline and ContributionGraph are dependency-free and export from the main barrel:
|
|
75
75
|
|
|
76
76
|
```tsx
|
|
77
77
|
import { BarChart, LineChart } from '@robr0/design-system/charts';
|
|
@@ -84,10 +84,15 @@ import { BarChart, LineChart } from '@robr0/design-system/charts';
|
|
|
84
84
|
```css
|
|
85
85
|
:root { --font-family-primary: 'Inter', sans-serif; }
|
|
86
86
|
```
|
|
87
|
-
- **Colours, radius, spacing**: every semantic token chains to a primitive, so overriding a primitive re-themes everything built on it:
|
|
87
|
+
- **Colours, radius, spacing**: every semantic token chains to a primitive, so overriding a primitive re-themes everything built on it. The action colour is theme-split by design (light fills run teal-08/09/10, dark inverts to teal-05/04/03), so rebranding it means re-keying those steps:
|
|
88
88
|
```css
|
|
89
89
|
:root {
|
|
90
|
-
--primitive-teal-
|
|
90
|
+
--primitive-teal-08: #6D31D3; /* light fill */
|
|
91
|
+
--primitive-teal-09: #4C2293; /* light hover */
|
|
92
|
+
--primitive-teal-10: #2E1560; /* light active, dark label */
|
|
93
|
+
--primitive-teal-05: #A78BFA; /* dark fill */
|
|
94
|
+
--primitive-teal-04: #C4B5FD; /* dark hover */
|
|
95
|
+
--primitive-teal-03: #DDD6FE; /* dark active */
|
|
91
96
|
--primitive-radius-full: 12px; /* pill buttons become rounded rectangles */
|
|
92
97
|
}
|
|
93
98
|
```
|
|
@@ -99,9 +104,9 @@ Icons use a bundled Material Symbols Rounded variable font (woff2), with no extr
|
|
|
99
104
|
Tokens flow in one direction, and primitives are never referenced directly in components:
|
|
100
105
|
|
|
101
106
|
```
|
|
102
|
-
tokens-primitives.css --primitive-teal-
|
|
107
|
+
tokens-primitives.css --primitive-teal-08: #0E6E8F
|
|
103
108
|
↓
|
|
104
|
-
tokens-light/dark.css --color-action-primary-bg: var(--primitive-teal-
|
|
109
|
+
tokens-light/dark.css --color-action-primary-bg: var(--primitive-teal-08)
|
|
105
110
|
↓
|
|
106
111
|
Component CSS background-color: var(--color-action-primary-bg)
|
|
107
112
|
```
|
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
align-items: center;
|
|
30
30
|
gap: var(--gap-sm);
|
|
31
31
|
width: 100%;
|
|
32
|
-
padding: var(--padding-
|
|
32
|
+
padding: var(--padding-sm) var(--padding-md);
|
|
33
33
|
background: none;
|
|
34
34
|
border: none;
|
|
35
35
|
text-align: left;
|
|
@@ -121,7 +121,7 @@
|
|
|
121
121
|
display: flex;
|
|
122
122
|
flex-direction: column;
|
|
123
123
|
margin: 0;
|
|
124
|
-
padding: var(--padding-sm) var(--padding-
|
|
124
|
+
padding: var(--padding-sm-md) var(--padding-md);
|
|
125
125
|
list-style: none;
|
|
126
126
|
}
|
|
127
127
|
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/* ============================================
|
|
2
|
+
ANCHOR NAV
|
|
3
|
+
"On this page" list of anchor links with a
|
|
4
|
+
position indicator riding the left rail.
|
|
5
|
+
============================================ */
|
|
6
|
+
.ds-anchor-nav {
|
|
7
|
+
font-family: var(--font-family-primary);
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
.ds-anchor-nav__header {
|
|
11
|
+
display: flex;
|
|
12
|
+
align-items: center;
|
|
13
|
+
gap: var(--gap-xs);
|
|
14
|
+
margin: 0 0 var(--gap-sm);
|
|
15
|
+
font-size: var(--font-paragraph-em-size);
|
|
16
|
+
font-weight: var(--font-paragraph-em-weight);
|
|
17
|
+
line-height: var(--font-paragraph-em-line-height);
|
|
18
|
+
color: var(--color-text-primary);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
.ds-anchor-nav__icon {
|
|
22
|
+
--icon-size: var(--icon-size-sm);
|
|
23
|
+
color: var(--color-icon-primary);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
.ds-anchor-nav__list {
|
|
27
|
+
list-style: none;
|
|
28
|
+
margin: 0;
|
|
29
|
+
padding: 0;
|
|
30
|
+
border-left: var(--border-xs) solid var(--color-divider);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
.ds-anchor-nav__link {
|
|
34
|
+
display: block;
|
|
35
|
+
padding: var(--padding-xs) 0 var(--padding-xs) var(--padding-md);
|
|
36
|
+
/* Sits over the list rail; turns into the indicator when active */
|
|
37
|
+
margin-left: calc(var(--border-xs) * -1);
|
|
38
|
+
border-left: var(--border-md) solid transparent;
|
|
39
|
+
font-size: var(--font-paragraph-size);
|
|
40
|
+
font-weight: var(--font-paragraph-weight);
|
|
41
|
+
line-height: var(--font-paragraph-line-height);
|
|
42
|
+
color: var(--color-text-tertiary);
|
|
43
|
+
text-decoration: none;
|
|
44
|
+
transition:
|
|
45
|
+
color var(--motion-duration-fast) var(--motion-ease-standard),
|
|
46
|
+
border-color var(--motion-duration-fast) var(--motion-ease-standard);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
.ds-anchor-nav__link:hover {
|
|
50
|
+
color: var(--color-text-primary);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
.ds-anchor-nav__link:focus-visible {
|
|
54
|
+
outline: 2px solid var(--color-action-primary-bg);
|
|
55
|
+
outline-offset: -2px;
|
|
56
|
+
border-radius: var(--radius-xxs);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
.ds-anchor-nav__link--active {
|
|
60
|
+
color: var(--color-text-primary);
|
|
61
|
+
font-weight: var(--font-paragraph-em-weight);
|
|
62
|
+
border-left-color: var(--color-text-primary);
|
|
63
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { default as React } from 'react';
|
|
2
|
+
export interface AnchorNavItem {
|
|
3
|
+
/** The id of the element the item jumps to (rendered as `href="#id"`) */
|
|
4
|
+
id: string;
|
|
5
|
+
/** Link text */
|
|
6
|
+
label: string;
|
|
7
|
+
}
|
|
8
|
+
type AnchorNavOwnProps = {
|
|
9
|
+
/** The on-page sections to list, in document order */
|
|
10
|
+
items: AnchorNavItem[];
|
|
11
|
+
/** Header text above the list; pass an empty string to render no header */
|
|
12
|
+
title?: string;
|
|
13
|
+
/** Material Symbols icon name beside the header; pass an empty string for none */
|
|
14
|
+
icon?: string;
|
|
15
|
+
/** Controlled active item id — set it to drive the highlight yourself and skip scroll tracking */
|
|
16
|
+
activeId?: string;
|
|
17
|
+
/** Fires when the tracked (or clicked) active item changes */
|
|
18
|
+
onActiveChange?: (id: string) => void;
|
|
19
|
+
/** Distance in px from the viewport top at which a section counts as current, e.g. a sticky header's height */
|
|
20
|
+
offset?: number;
|
|
21
|
+
};
|
|
22
|
+
export interface AnchorNavProps extends AnchorNavOwnProps, Omit<React.ComponentPropsWithoutRef<'nav'>, keyof AnchorNavOwnProps> {
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* An "On this page" list of anchor links that tracks the reader's position.
|
|
26
|
+
*
|
|
27
|
+
* Uncontrolled by default: a scroll listener marks the last section whose top
|
|
28
|
+
* has passed `offset` as current. Pass `activeId` to control the highlight
|
|
29
|
+
* instead (no listener is attached). The active link carries
|
|
30
|
+
* `aria-current="location"`.
|
|
31
|
+
*/
|
|
32
|
+
export declare const AnchorNav: React.ForwardRefExoticComponent<AnchorNavProps & React.RefAttributes<HTMLElement>>;
|
|
33
|
+
export {};
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsxs, jsx } from "react/jsx-runtime";
|
|
3
|
+
import React, { useRef, useImperativeHandle, useState, useCallback, useEffect } from "react";
|
|
4
|
+
import "./AnchorNav.css";
|
|
5
|
+
const AnchorNav = React.forwardRef(
|
|
6
|
+
({
|
|
7
|
+
items,
|
|
8
|
+
title = "On this page",
|
|
9
|
+
icon = "toc",
|
|
10
|
+
activeId,
|
|
11
|
+
onActiveChange,
|
|
12
|
+
offset = 96,
|
|
13
|
+
className,
|
|
14
|
+
...rest
|
|
15
|
+
}, ref) => {
|
|
16
|
+
const navRef = useRef(null);
|
|
17
|
+
useImperativeHandle(ref, () => navRef.current);
|
|
18
|
+
const isControlled = activeId !== void 0;
|
|
19
|
+
const [trackedId, setTrackedId] = useState(void 0);
|
|
20
|
+
const currentId = isControlled ? activeId : trackedId;
|
|
21
|
+
const onActiveChangeRef = useRef(onActiveChange);
|
|
22
|
+
onActiveChangeRef.current = onActiveChange;
|
|
23
|
+
const setActive = useCallback(
|
|
24
|
+
(id) => {
|
|
25
|
+
setTrackedId((prev) => {
|
|
26
|
+
if (id !== void 0 && id !== prev) onActiveChangeRef.current?.(id);
|
|
27
|
+
return id;
|
|
28
|
+
});
|
|
29
|
+
},
|
|
30
|
+
[]
|
|
31
|
+
);
|
|
32
|
+
useEffect(() => {
|
|
33
|
+
if (isControlled || items.length === 0) return;
|
|
34
|
+
let frame = 0;
|
|
35
|
+
const measure = () => {
|
|
36
|
+
frame = 0;
|
|
37
|
+
let current = items[0]?.id;
|
|
38
|
+
for (const item of items) {
|
|
39
|
+
const el = document.getElementById(item.id);
|
|
40
|
+
if (!el) continue;
|
|
41
|
+
if (el.getBoundingClientRect().top <= offset + 1) current = item.id;
|
|
42
|
+
}
|
|
43
|
+
setActive(current);
|
|
44
|
+
};
|
|
45
|
+
const schedule = () => {
|
|
46
|
+
if (frame === 0) frame = window.requestAnimationFrame(measure);
|
|
47
|
+
};
|
|
48
|
+
measure();
|
|
49
|
+
window.addEventListener("scroll", schedule, { passive: true });
|
|
50
|
+
window.addEventListener("resize", schedule);
|
|
51
|
+
return () => {
|
|
52
|
+
window.removeEventListener("scroll", schedule);
|
|
53
|
+
window.removeEventListener("resize", schedule);
|
|
54
|
+
if (frame !== 0) window.cancelAnimationFrame(frame);
|
|
55
|
+
};
|
|
56
|
+
}, [isControlled, items, offset, setActive]);
|
|
57
|
+
const classes = ["ds-anchor-nav", className].filter(Boolean).join(" ");
|
|
58
|
+
return /* @__PURE__ */ jsxs("nav", { ref: navRef, "aria-label": title || "On this page", className: classes, ...rest, children: [
|
|
59
|
+
title && /* @__PURE__ */ jsxs("p", { className: "ds-anchor-nav__header", children: [
|
|
60
|
+
icon && /* @__PURE__ */ jsx("span", { className: "material-symbols-rounded ds-anchor-nav__icon", "aria-hidden": "true", children: icon }),
|
|
61
|
+
title
|
|
62
|
+
] }),
|
|
63
|
+
/* @__PURE__ */ jsx("ul", { className: "ds-anchor-nav__list", children: items.map((item) => /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsx(
|
|
64
|
+
"a",
|
|
65
|
+
{
|
|
66
|
+
href: `#${item.id}`,
|
|
67
|
+
className: item.id === currentId ? "ds-anchor-nav__link ds-anchor-nav__link--active" : "ds-anchor-nav__link",
|
|
68
|
+
"aria-current": item.id === currentId ? "location" : void 0,
|
|
69
|
+
onClick: () => {
|
|
70
|
+
if (!isControlled) setActive(item.id);
|
|
71
|
+
else onActiveChangeRef.current?.(item.id);
|
|
72
|
+
},
|
|
73
|
+
children: item.label
|
|
74
|
+
}
|
|
75
|
+
) }, item.id)) })
|
|
76
|
+
] });
|
|
77
|
+
}
|
|
78
|
+
);
|
|
79
|
+
AnchorNav.displayName = "AnchorNav";
|
|
80
|
+
export {
|
|
81
|
+
AnchorNav
|
|
82
|
+
};
|
|
@@ -166,12 +166,17 @@
|
|
|
166
166
|
cursor: pointer;
|
|
167
167
|
overflow: hidden;
|
|
168
168
|
text-align: left;
|
|
169
|
-
|
|
169
|
+
/* The row renders as an <a> when the item carries an href. */
|
|
170
|
+
text-decoration: none;
|
|
171
|
+
transition: background-color var(--motion-duration-fast) var(--motion-ease-standard), padding var(--motion-duration-slow) var(--motion-ease-emphasized), margin var(--motion-duration-slow) var(--motion-ease-emphasized);
|
|
170
172
|
}
|
|
171
173
|
|
|
174
|
+
/* Inset via margin + reduced padding (12 + 8 = the 20px the label sat at
|
|
175
|
+
before), so the pill floats off the rail edges while the icon and label
|
|
176
|
+
keep their exact x-positions against the category labels above. */
|
|
172
177
|
.ds-app-sidebar--expanded .ds-app-sidebar__btn {
|
|
173
|
-
|
|
174
|
-
padding: var(--padding-sm)
|
|
178
|
+
margin: 0 var(--padding-sm-md);
|
|
179
|
+
padding: var(--padding-sm);
|
|
175
180
|
justify-content: space-between;
|
|
176
181
|
}
|
|
177
182
|
|
|
@@ -282,6 +287,9 @@
|
|
|
282
287
|
align-items: center;
|
|
283
288
|
gap: 8px;
|
|
284
289
|
height: 40px;
|
|
290
|
+
/* The same edge inset the parent pills get; sub-items only render while
|
|
291
|
+
the rail is expanded, so this needs no state selector. */
|
|
292
|
+
margin-right: var(--padding-sm-md);
|
|
285
293
|
padding: var(--padding-sm);
|
|
286
294
|
border-radius: var(--radius-full);
|
|
287
295
|
background: transparent;
|
|
@@ -292,6 +300,8 @@
|
|
|
292
300
|
text-align: left;
|
|
293
301
|
overflow: hidden;
|
|
294
302
|
white-space: nowrap;
|
|
303
|
+
/* The row renders as an <a> when the sub-item carries an href. */
|
|
304
|
+
text-decoration: none;
|
|
295
305
|
transition: background-color var(--motion-duration-fast) var(--motion-ease-standard);
|
|
296
306
|
}
|
|
297
307
|
|
|
@@ -4,8 +4,10 @@ export interface AppSidebarSubItem {
|
|
|
4
4
|
key: string;
|
|
5
5
|
/** Display label */
|
|
6
6
|
label: string;
|
|
7
|
-
/** Click handler */
|
|
7
|
+
/** Click handler — also fires on a link sub-item, so a consumer can route client-side */
|
|
8
8
|
onClick?: () => void;
|
|
9
|
+
/** Renders the sub-item as a real link to this URL instead of a button */
|
|
10
|
+
href?: string;
|
|
9
11
|
}
|
|
10
12
|
export interface AppSidebarItem {
|
|
11
13
|
/** Unique key for this item */
|
|
@@ -14,8 +16,10 @@ export interface AppSidebarItem {
|
|
|
14
16
|
icon: string;
|
|
15
17
|
/** Display label (shown when expanded) */
|
|
16
18
|
label: string;
|
|
17
|
-
/** Click handler */
|
|
19
|
+
/** Click handler — also fires on a link item, so a consumer can route client-side */
|
|
18
20
|
onClick?: () => void;
|
|
21
|
+
/** 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 */
|
|
22
|
+
href?: string;
|
|
19
23
|
/** Sub-items — turns this into an accordion */
|
|
20
24
|
children?: AppSidebarSubItem[];
|
|
21
25
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsxs, jsx, Fragment } from "react/jsx-runtime";
|
|
2
3
|
import React, { useState, useCallback } from "react";
|
|
3
4
|
import "./AppSidebar.css";
|
|
4
5
|
import "../../fonts/material-symbols.css";
|
|
@@ -186,14 +187,43 @@ const AppSidebar = ({
|
|
|
186
187
|
const hasChildren = item.children && item.children.length > 0;
|
|
187
188
|
const isOpen = openKeys.has(item.key);
|
|
188
189
|
const isActive = activeKey === item.key;
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
190
|
+
const isLink = Boolean(item.href) && !hasChildren;
|
|
191
|
+
const itemClasses = [
|
|
192
|
+
`${baseClass}__btn`,
|
|
193
|
+
isActive ? `${baseClass}__btn--active` : ""
|
|
194
|
+
].filter(Boolean).join(" ");
|
|
195
|
+
const itemContent = /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
196
|
+
/* @__PURE__ */ jsxs("span", { className: `${baseClass}__btn-left`, children: [
|
|
197
|
+
/* @__PURE__ */ jsx("span", { className: `${baseClass}__btn-icon material-symbols-rounded`, children: item.icon }),
|
|
198
|
+
/* @__PURE__ */ jsx("span", { className: `${baseClass}__btn-label`, children: item.label })
|
|
199
|
+
] }),
|
|
200
|
+
hasChildren && /* @__PURE__ */ jsx(
|
|
201
|
+
"span",
|
|
192
202
|
{
|
|
193
203
|
className: [
|
|
194
|
-
`${baseClass}__btn`,
|
|
195
|
-
|
|
204
|
+
`${baseClass}__btn-chevron material-symbols-rounded`,
|
|
205
|
+
isOpen ? `${baseClass}__btn-chevron--open` : ""
|
|
196
206
|
].filter(Boolean).join(" "),
|
|
207
|
+
children: "chevron_right"
|
|
208
|
+
}
|
|
209
|
+
)
|
|
210
|
+
] });
|
|
211
|
+
return /* @__PURE__ */ jsxs(React.Fragment, { children: [
|
|
212
|
+
isLink ? /* @__PURE__ */ jsx(
|
|
213
|
+
"a",
|
|
214
|
+
{
|
|
215
|
+
className: itemClasses,
|
|
216
|
+
href: item.href,
|
|
217
|
+
onClick: () => item.onClick?.(),
|
|
218
|
+
"aria-current": isActive ? "page" : void 0,
|
|
219
|
+
title: !isExpanded ? item.label : void 0,
|
|
220
|
+
children: itemContent
|
|
221
|
+
}
|
|
222
|
+
) : /* @__PURE__ */ jsx(
|
|
223
|
+
"button",
|
|
224
|
+
{
|
|
225
|
+
type: "button",
|
|
226
|
+
className: itemClasses,
|
|
197
227
|
onClick: () => {
|
|
198
228
|
if (hasChildren && isExpanded) {
|
|
199
229
|
toggleAccordion(item.key);
|
|
@@ -201,23 +231,9 @@ const AppSidebar = ({
|
|
|
201
231
|
item.onClick?.();
|
|
202
232
|
},
|
|
203
233
|
"aria-expanded": hasChildren ? isOpen : void 0,
|
|
234
|
+
"aria-current": isActive ? "page" : void 0,
|
|
204
235
|
title: !isExpanded ? item.label : void 0,
|
|
205
|
-
children:
|
|
206
|
-
/* @__PURE__ */ jsxs("span", { className: `${baseClass}__btn-left`, children: [
|
|
207
|
-
/* @__PURE__ */ jsx("span", { className: `${baseClass}__btn-icon material-symbols-rounded`, children: item.icon }),
|
|
208
|
-
/* @__PURE__ */ jsx("span", { className: `${baseClass}__btn-label`, children: item.label })
|
|
209
|
-
] }),
|
|
210
|
-
hasChildren && /* @__PURE__ */ jsx(
|
|
211
|
-
"span",
|
|
212
|
-
{
|
|
213
|
-
className: [
|
|
214
|
-
`${baseClass}__btn-chevron material-symbols-rounded`,
|
|
215
|
-
isOpen ? `${baseClass}__btn-chevron--open` : ""
|
|
216
|
-
].filter(Boolean).join(" "),
|
|
217
|
-
children: "chevron_right"
|
|
218
|
-
}
|
|
219
|
-
)
|
|
220
|
-
]
|
|
236
|
+
children: itemContent
|
|
221
237
|
}
|
|
222
238
|
),
|
|
223
239
|
hasChildren && /* @__PURE__ */ jsx(
|
|
@@ -227,20 +243,36 @@ const AppSidebar = ({
|
|
|
227
243
|
`${baseClass}__sub-items`,
|
|
228
244
|
isOpen && isExpanded ? `${baseClass}__sub-items--open` : ""
|
|
229
245
|
].filter(Boolean).join(" "),
|
|
230
|
-
children: item.children.map((sub) =>
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
{
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
246
|
+
children: item.children.map((sub) => {
|
|
247
|
+
const subActive = activeSubKey === sub.key;
|
|
248
|
+
const subClasses = [
|
|
249
|
+
`${baseClass}__sub-btn`,
|
|
250
|
+
subActive ? `${baseClass}__sub-btn--active` : ""
|
|
251
|
+
].filter(Boolean).join(" ");
|
|
252
|
+
const subLabel = /* @__PURE__ */ jsx("span", { className: `${baseClass}__sub-label`, children: sub.label });
|
|
253
|
+
return /* @__PURE__ */ jsxs("div", { className: `${baseClass}__sub-item`, children: [
|
|
254
|
+
/* @__PURE__ */ jsx("div", { className: `${baseClass}__sub-line` }),
|
|
255
|
+
sub.href ? /* @__PURE__ */ jsx(
|
|
256
|
+
"a",
|
|
257
|
+
{
|
|
258
|
+
className: subClasses,
|
|
259
|
+
href: sub.href,
|
|
260
|
+
onClick: sub.onClick,
|
|
261
|
+
"aria-current": subActive ? "page" : void 0,
|
|
262
|
+
children: subLabel
|
|
263
|
+
}
|
|
264
|
+
) : /* @__PURE__ */ jsx(
|
|
265
|
+
"button",
|
|
266
|
+
{
|
|
267
|
+
type: "button",
|
|
268
|
+
className: subClasses,
|
|
269
|
+
onClick: sub.onClick,
|
|
270
|
+
"aria-current": subActive ? "page" : void 0,
|
|
271
|
+
children: subLabel
|
|
272
|
+
}
|
|
273
|
+
)
|
|
274
|
+
] }, sub.key);
|
|
275
|
+
})
|
|
244
276
|
}
|
|
245
277
|
)
|
|
246
278
|
] }, item.key);
|
package/components/Card/Card.js
CHANGED
|
@@ -32,7 +32,7 @@ const AreaChart = ({
|
|
|
32
32
|
const classes = [baseClass, className].filter(Boolean).join(" ");
|
|
33
33
|
const textSecondary = getCSSVar("--color-text-secondary", "#A2A2A2");
|
|
34
34
|
const gridColor = getCSSVar("--color-divider", "#232323");
|
|
35
|
-
const defaultColor = getCSSVar("--color-action-primary-bg", "#
|
|
35
|
+
const defaultColor = getCSSVar("--color-action-primary-bg", "#0E6E8F");
|
|
36
36
|
const renderTooltip = useCallback(
|
|
37
37
|
/* eslint-disable-next-line @typescript-eslint/no-explicit-any */
|
|
38
38
|
(props) => /* @__PURE__ */ jsx(AreaTooltip, { ...props }),
|
|
@@ -37,7 +37,7 @@ const BarChart = ({
|
|
|
37
37
|
}) => {
|
|
38
38
|
const baseClass = "ds-chart";
|
|
39
39
|
const classes = [baseClass, className].filter(Boolean).join(" ");
|
|
40
|
-
const resolvedBarColor = barColor || getCSSVar("--color-action-primary-bg", "#
|
|
40
|
+
const resolvedBarColor = barColor || getCSSVar("--color-action-primary-bg", "#0E6E8F");
|
|
41
41
|
const textSecondary = getCSSVar("--color-text-secondary", "#A2A2A2");
|
|
42
42
|
const gridColor = getCSSVar("--color-divider", "#232323");
|
|
43
43
|
const cursorColor = getCSSVar("--color-bg-container-secondary", "#303030");
|
|
@@ -31,7 +31,7 @@ const LineChart = ({
|
|
|
31
31
|
const classes = [baseClass, className].filter(Boolean).join(" ");
|
|
32
32
|
const textSecondary = getCSSVar("--color-text-secondary", "#A2A2A2");
|
|
33
33
|
const gridColor = getCSSVar("--color-divider", "#232323");
|
|
34
|
-
const defaultColor = getCSSVar("--color-action-primary-bg", "#
|
|
34
|
+
const defaultColor = getCSSVar("--color-action-primary-bg", "#0E6E8F");
|
|
35
35
|
const renderTooltip = useCallback(
|
|
36
36
|
/* eslint-disable-next-line @typescript-eslint/no-explicit-any */
|
|
37
37
|
(props) => /* @__PURE__ */ jsx(LineChartTooltip, { ...props }),
|
|
@@ -4,11 +4,11 @@ import { ResponsiveContainer, PieChart as PieChart$1, Pie, Cell, Tooltip, Legend
|
|
|
4
4
|
import "./Chart.css";
|
|
5
5
|
function getDefaultColors() {
|
|
6
6
|
if (typeof window === "undefined") {
|
|
7
|
-
return ["#
|
|
7
|
+
return ["#0E6E8F", "#06D6A0", "#FFD166", "#EF476F", "#9E47EF", "#EF8247"];
|
|
8
8
|
}
|
|
9
9
|
const get = (v, fb) => getComputedStyle(document.documentElement).getPropertyValue(v).trim() || fb;
|
|
10
10
|
return [
|
|
11
|
-
get("--color-action-primary-bg", "#
|
|
11
|
+
get("--color-action-primary-bg", "#0E6E8F"),
|
|
12
12
|
get("--color-core-accent-mint", "#06D6A0"),
|
|
13
13
|
get("--color-core-accent-gold", "#FFD166"),
|
|
14
14
|
get("--color-core-accent-coral", "#EF476F"),
|
|
@@ -31,7 +31,7 @@ const RadarChart = ({
|
|
|
31
31
|
const classes = [baseClass, className].filter(Boolean).join(" ");
|
|
32
32
|
const textSecondary = getCSSVar("--color-text-secondary", "#A2A2A2");
|
|
33
33
|
const gridColor = getCSSVar("--color-divider", "#232323");
|
|
34
|
-
const defaultColor = getCSSVar("--color-action-primary-bg", "#
|
|
34
|
+
const defaultColor = getCSSVar("--color-action-primary-bg", "#0E6E8F");
|
|
35
35
|
const renderTooltip = useCallback(
|
|
36
36
|
/* eslint-disable-next-line @typescript-eslint/no-explicit-any */
|
|
37
37
|
(props) => /* @__PURE__ */ jsx(RadarTooltip, { ...props }),
|
|
@@ -4,11 +4,11 @@ import { ResponsiveContainer, RadialBarChart, PolarAngleAxis, RadialBar, Tooltip
|
|
|
4
4
|
import "./Chart.css";
|
|
5
5
|
function getDefaultColors() {
|
|
6
6
|
if (typeof window === "undefined") {
|
|
7
|
-
return ["#
|
|
7
|
+
return ["#0E6E8F", "#06D6A0", "#FFD166", "#EF476F", "#9E47EF", "#EF8247"];
|
|
8
8
|
}
|
|
9
9
|
const get = (v, fb) => getComputedStyle(document.documentElement).getPropertyValue(v).trim() || fb;
|
|
10
10
|
return [
|
|
11
|
-
get("--color-action-primary-bg", "#
|
|
11
|
+
get("--color-action-primary-bg", "#0E6E8F"),
|
|
12
12
|
get("--color-core-accent-mint", "#06D6A0"),
|
|
13
13
|
get("--color-core-accent-gold", "#FFD166"),
|
|
14
14
|
get("--color-core-accent-coral", "#EF476F"),
|
|
@@ -8,11 +8,11 @@ function getCSSVar(name, fallback) {
|
|
|
8
8
|
}
|
|
9
9
|
function getDefaultColors() {
|
|
10
10
|
if (typeof window === "undefined") {
|
|
11
|
-
return ["#
|
|
11
|
+
return ["#0E6E8F", "#06D6A0", "#FFD166", "#EF476F", "#9E47EF", "#EF8247"];
|
|
12
12
|
}
|
|
13
13
|
const get = (v, fb) => getComputedStyle(document.documentElement).getPropertyValue(v).trim() || fb;
|
|
14
14
|
return [
|
|
15
|
-
get("--color-action-primary-bg", "#
|
|
15
|
+
get("--color-action-primary-bg", "#0E6E8F"),
|
|
16
16
|
get("--color-core-accent-mint", "#06D6A0"),
|
|
17
17
|
get("--color-core-accent-gold", "#FFD166"),
|
|
18
18
|
get("--color-core-accent-coral", "#EF476F"),
|