atlas-react-kit 0.13.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.
- package/COMPONENTS.md +144 -120
- package/DESIGN_RULES.md +53 -32
- package/README.md +12 -53
- package/dist/accordion.d.ts +1 -0
- package/dist/avatar.d.ts +1 -0
- package/dist/badge.d.ts +2 -3
- package/dist/breadcrumb.d.ts +2 -3
- package/dist/button.d.ts +2 -3
- package/dist/calendar.d.ts +2 -0
- package/dist/card.d.ts +3 -0
- package/dist/chart.d.ts +152 -0
- package/dist/chart.js +177 -0
- package/dist/color-legend.d.ts +8 -0
- package/dist/combobox.d.ts +13 -4
- package/dist/combobox.js +1 -8
- package/dist/date-picker.d.ts +26 -3
- package/dist/date-picker.js +37 -31
- package/dist/date-time-picker.d.ts +5 -2
- package/dist/date-time-picker.js +25 -38
- package/dist/dialog.d.ts +1 -0
- package/dist/dialog.js +4 -2
- package/dist/dropdown-menu.d.ts +9 -8
- package/dist/empty-panel.d.ts +8 -2
- package/dist/empty-panel.js +2 -2
- package/dist/error-boundary.d.ts +6 -1
- package/dist/field.d.ts +3 -0
- package/dist/field.js +1 -1
- package/dist/form/checkbox-field.d.ts +45 -0
- package/dist/form/checkbox-field.js +39 -0
- package/dist/form/combobox-field.d.ts +13 -0
- package/dist/form/combobox-field.js +14 -0
- package/dist/form/control-field.d.ts +6 -7
- package/dist/form/date-picker-field.d.ts +12 -21
- package/dist/form/date-picker-field.js +5 -4
- package/dist/form/date-time-picker-field.d.ts +7 -22
- package/dist/form/date-time-picker-field.js +4 -3
- package/dist/form/feedback-field.d.ts +2 -0
- package/dist/form/field-error.d.ts +35 -3
- package/dist/form/field-shell.d.ts +35 -6
- package/dist/form/field-shell.js +1 -1
- package/dist/form/focus-first-invalid.d.ts +28 -0
- package/dist/form/focus-first-invalid.js +29 -0
- package/dist/form/markdown-field.d.ts +4 -16
- package/dist/form/select-field.d.ts +13 -7
- package/dist/form/select-field.js +3 -2
- package/dist/form/text-field.d.ts +29 -10
- package/dist/form/text-field.js +4 -0
- package/dist/form/toggle-group-field.d.ts +30 -0
- package/dist/form/toggle-group-field.js +19 -0
- package/dist/info-tip.d.ts +10 -1
- package/dist/internal/as-child.d.ts +8 -0
- package/dist/internal/as-child.js +1 -0
- package/dist/internal/form-control.d.ts +3 -2
- package/dist/internal/form-control.js +3 -2
- package/dist/internal/kanban-layout.d.ts +55 -0
- package/dist/internal/kanban-layout.js +80 -0
- package/dist/internal/kanban-plugins.d.ts +17 -0
- package/dist/internal/kanban-plugins.js +17 -0
- package/dist/internal/listbox-navigation.d.ts +0 -11
- package/dist/internal/listbox-navigation.js +2 -2
- package/dist/internal/listbox-option.d.ts +0 -15
- package/dist/internal/listbox-option.js +1 -1
- package/dist/internal/use-drag-scroll.d.ts +17 -0
- package/dist/internal/use-drag-scroll.js +98 -0
- package/dist/internal/use-media-query.d.ts +4 -2
- package/dist/internal/use-media-query.js +11 -15
- package/dist/kanban.d.ts +84 -0
- package/dist/kanban.js +197 -0
- package/dist/markdown.d.ts +2 -0
- package/dist/meter.d.ts +7 -0
- package/dist/nav-link.d.ts +15 -6
- package/dist/nav-link.js +14 -4
- package/dist/next/nav-link.d.ts +6 -3
- package/dist/next/nav-link.js +8 -6
- package/dist/next/period-stepper.d.ts +11 -0
- package/dist/next/period-stepper.js +16 -0
- package/dist/next/tabs-nav-link.d.ts +6 -0
- package/dist/next/tabs-nav-link.js +8 -3
- package/dist/page-header.d.ts +19 -0
- package/dist/page-header.js +11 -0
- package/dist/pagination.d.ts +2 -0
- package/dist/period-stepper.d.ts +32 -0
- package/dist/period-stepper.js +27 -0
- package/dist/picker-shell.d.ts +33 -18
- package/dist/picker-shell.js +12 -10
- package/dist/progress.d.ts +1 -0
- package/dist/search-picker.d.ts +12 -0
- package/dist/select.d.ts +1 -0
- package/dist/skip-link.d.ts +7 -0
- package/dist/skip-link.js +10 -0
- package/dist/slider.d.ts +11 -0
- package/dist/sortable-table-head.d.ts +2 -0
- package/dist/stat-tile.d.ts +5 -0
- package/dist/table-body-skeleton.d.ts +2 -0
- package/dist/table.d.ts +1 -0
- package/dist/tabs.d.ts +27 -12
- package/dist/tabs.js +23 -10
- package/dist/toast.d.ts +7 -1
- package/dist/toggle-group.d.ts +9 -2
- package/dist/toggle-group.js +2 -1
- package/package.json +72 -6
- package/skills/atlas-react-kit/SKILL.md +46 -40
- package/theme.css +13 -0
package/DESIGN_RULES.md
CHANGED
|
@@ -32,6 +32,36 @@ its own soft tint in both themes. Status tones have no paired foreground token;
|
|
|
32
32
|
avoid placing text on solid status fills. Application-specific identity colors
|
|
33
33
|
belong to the application.
|
|
34
34
|
|
|
35
|
+
### Public tokens and slots
|
|
36
|
+
|
|
37
|
+
These are public API from 1.0, so apps may use them and a rename or removal
|
|
38
|
+
is a breaking change:
|
|
39
|
+
|
|
40
|
+
- Color tokens and their `--color-*` utilities: `background`, `foreground`,
|
|
41
|
+
`card`, `popover`, `primary`, `secondary`, `muted`, `accent`, `selected`
|
|
42
|
+
(each with its `-foreground` pair where one exists), `destructive`,
|
|
43
|
+
`success`, `warning`, `border`, `input`, `ring`, `chart-1` to `chart-5`,
|
|
44
|
+
and the `sidebar` set.
|
|
45
|
+
- `--radius` and the `--radius-*` scale, and `--font-heading`.
|
|
46
|
+
- Every `data-slot` listed under "Styling parts" in
|
|
47
|
+
[COMPONENTS.md](./COMPONENTS.md#styling-parts).
|
|
48
|
+
|
|
49
|
+
Everything else in `theme.css` is internal and may change in a minor
|
|
50
|
+
release: the scroll-shadow variables (`--scroll-fade`, `--scroll-fill`,
|
|
51
|
+
`--table-scroll-fade`, `--tabs-scroll-fade`) and any `data-slot` not listed
|
|
52
|
+
in COMPONENTS.md.
|
|
53
|
+
|
|
54
|
+
### Prop names
|
|
55
|
+
|
|
56
|
+
- A controlled value is `value` with `onValueChange`; open state is `open`
|
|
57
|
+
with `onOpenChange`; a checked state is `checked` with `onCheckedChange`.
|
|
58
|
+
- Sizes run `sm`, `default`, `lg`; `Button` adds `xs` and `icon-*` sizes.
|
|
59
|
+
- `variant="default"` is the plain look; other variants name what they mean
|
|
60
|
+
(`outline`, `ghost`, `destructive`).
|
|
61
|
+
|
|
62
|
+
Deprecated aliases (`onChange` on the date pickers, `EmptyPanel`
|
|
63
|
+
`size="md"`) keep working until 2.0.
|
|
64
|
+
|
|
35
65
|
## Accessibility
|
|
36
66
|
|
|
37
67
|
The target is WCAG 2.2 AA. CI runs axe-core's WCAG 2.2 A and AA rules against
|
|
@@ -99,13 +129,10 @@ or the viewport minus gutters on small screens.
|
|
|
99
129
|
|
|
100
130
|
Use `EmptyPanel` for empty states, `TableBodySkeleton` for loading table bodies,
|
|
101
131
|
and `<Card flush>` for an edge-to-edge list or table. `Table` scrolls wide
|
|
102
|
-
columns inside itself
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
column, and `min-w-0` so nested tables scroll instead of widening the card.
|
|
107
|
-
`Dialog` already scrolls inside the viewport on small screens; avoid
|
|
108
|
-
additional height limits.
|
|
132
|
+
columns inside itself, `Card` neither collapses in a bounded flex column nor
|
|
133
|
+
widens around a nested table, and `Dialog` already scrolls inside the
|
|
134
|
+
viewport on small screens; avoid additional height limits. The classes each
|
|
135
|
+
one uses are in its COMPONENTS.md row.
|
|
109
136
|
|
|
110
137
|
Apps decide how wide and tall containers and overlays are. The kit sets
|
|
111
138
|
intrinsic control sizes (the button, input, tab and row heights, icons,
|
|
@@ -114,12 +141,10 @@ clamps overlays to the viewport, but no design widths or heights on
|
|
|
114
141
|
dialogs, popovers, menus, or lists. Set them with `className`: give each
|
|
115
142
|
`DialogContent` a width such as `sm:max-w-md`, and each `PopoverContent`
|
|
116
143
|
one such as `w-72`. Where the sized part is not the element `className`
|
|
117
|
-
reaches, a prop names it
|
|
118
|
-
|
|
119
|
-
`
|
|
120
|
-
|
|
121
|
-
`min-h-16` starting height, and toasts their 356px width; override the first
|
|
122
|
-
two with `className`.
|
|
144
|
+
reaches, a `*ClassName` prop names it, with its default where there is one
|
|
145
|
+
(COMPONENTS.md, "Styling parts"). The few parts that keep a reading width or
|
|
146
|
+
a starting height (`Tooltip`, `Textarea`) take `className` to override it;
|
|
147
|
+
toasts keep their width.
|
|
123
148
|
|
|
124
149
|
Parents do not force sizes on their children. A vertical `Field` stretches
|
|
125
150
|
its controls through the flex column, so a control's own width class (such
|
|
@@ -142,32 +167,28 @@ triggers as outline buttons in application code.
|
|
|
142
167
|
error sits below the control. It takes no space until it shows, so a row of
|
|
143
168
|
fields keeps its controls on one top edge only with `items-start`
|
|
144
169
|
(`grid grid-cols-2 items-start gap-3`); the default `stretch` is fine for a
|
|
145
|
-
single column. Wrap a control the kit
|
|
170
|
+
single column. Wrap a control the kit has no form field for (a colour
|
|
146
171
|
picker) in `FormControlField` instead of `Field` and `FieldLabel`, whose
|
|
147
|
-
`gap-3` spacing does not match. A field shows its error once
|
|
148
|
-
`meta.isTouched`
|
|
149
|
-
|
|
172
|
+
`gap-3` spacing does not match. A field shows its error once the person has
|
|
173
|
+
touched it (`meta.isTouched`), never on a field they have not reached yet.
|
|
174
|
+
`submitted` is for form libraries that do not mark every field touched on
|
|
175
|
+
submit; TanStack Form does, so its users pass `meta` only.
|
|
150
176
|
|
|
151
177
|
Use `Tabs` with actual `TabsContent` panels. For URL view switches, use a labelled
|
|
152
|
-
`TabsNav`
|
|
153
|
-
Next adapter provides `TabsNavLink` with this behavior. App-shell destinations
|
|
178
|
+
`TabsNav` of `TabsNavLink`s with `active` from the app. App-shell destinations
|
|
154
179
|
(Calendar, Tasks, Settings) use `NavLink` from `nav-link`: a pill with `active`
|
|
155
|
-
from the app, not tabs.
|
|
156
|
-
Next
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
Use `FormTextField`, `FormSelectField`, `FormDatePickerField`,
|
|
164
|
-
`FormDateTimePickerField`, `FormMarkdownField`, and `FormFeedbackField` for
|
|
165
|
-
portable form controls, and `FieldError` for field messages. `Combobox` (popover)
|
|
180
|
+
from the app, not tabs. Both links take `asChild` for a router link, or come
|
|
181
|
+
as Next adapters (`next/tabs-nav-link`, `next/nav-link`); `isNavActive`
|
|
182
|
+
compares the pathname with a link's `href`. `TabsNav` and `TabsList` scroll overflowing
|
|
183
|
+
labels inside themselves, like `Table`. Do not wrap them in another
|
|
184
|
+
horizontal scroller: that pairing adds a stray vertical scrollbar.
|
|
185
|
+
|
|
186
|
+
Use the `form/*` fields (one row each in COMPONENTS.md) for portable form
|
|
187
|
+
controls, and `FieldError` for field messages. `Combobox` (popover)
|
|
166
188
|
and `SearchPicker` (always-visible list), `DatePickerButton` (including
|
|
167
189
|
`mode="range"`), `DateTimePickerButton`, `Slider`, `Meter` (scale with ticks;
|
|
168
190
|
`Progress` is a plain 0–100 job bar), `InfoTip` (longer than `Tooltip`),
|
|
169
191
|
`Markdown`, `ColorLegend`, `StatTile`, `ThemeProvider`, `ErrorBoundary`, and
|
|
170
192
|
`SortableTableHead` are first-class kit pieces. Domain data and application
|
|
171
193
|
shells stay with the application: currency or timezone option lists,
|
|
172
|
-
query-library error views, WYSIWYG editors
|
|
173
|
-
widgets.
|
|
194
|
+
query-library error views, and WYSIWYG editors.
|
package/README.md
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
# Atlas React Kit
|
|
2
2
|
|
|
3
3
|
Shared React components and a Tailwind theme with square corners, semantic
|
|
4
|
-
colors, and distinct selected states.
|
|
5
|
-
|
|
6
|
-
|
|
4
|
+
colors, and distinct selected states. From 1.0 the API is stable: breaking
|
|
5
|
+
changes to stable components only ship in a major release (see
|
|
6
|
+
[CONTRIBUTING.md](./CONTRIBUTING.md#versions-and-releases)).
|
|
7
7
|
React 19, Tailwind 4, and Node >=20.19 are required. Next 16 is needed only
|
|
8
|
-
when using `next/nav-link` or `next/tabs-nav-link`.
|
|
8
|
+
when using `next/nav-link`, `next/period-stepper`, or `next/tabs-nav-link`.
|
|
9
9
|
|
|
10
10
|
```sh
|
|
11
|
-
npm install --save-exact atlas-react-kit@
|
|
11
|
+
npm install --save-exact atlas-react-kit@1.1.0
|
|
12
12
|
```
|
|
13
13
|
|
|
14
14
|
Import components by subpath:
|
|
@@ -42,8 +42,9 @@ this example assumes it is one directory below the project root.
|
|
|
42
42
|
```
|
|
43
43
|
|
|
44
44
|
The theme includes animations and shared light/dark tokens. Wrap the tree with
|
|
45
|
-
`ThemeProvider` from `atlas-react-kit/theme-provider
|
|
46
|
-
|
|
45
|
+
`ThemeProvider` from `atlas-react-kit/theme-provider` (install
|
|
46
|
+
`next-themes@^0.4.6` first), or set the `dark` class on an ancestor to activate
|
|
47
|
+
dark colors. Supply your own font configuration;
|
|
47
48
|
`font-heading` uses the configured sans font. Application-specific tokens stay
|
|
48
49
|
with the application.
|
|
49
50
|
|
|
@@ -54,7 +55,8 @@ with the application.
|
|
|
54
55
|
- [Design rules](./DESIGN_RULES.md): tokens, accessibility, writing, and
|
|
55
56
|
composition. This guide ships in the npm package.
|
|
56
57
|
- [Contributing](./CONTRIBUTING.md): component changes, checks, and releases.
|
|
57
|
-
- [
|
|
58
|
+
- [Decisions behind 1.0 (record)](./ROADMAP-1.0.md): the closed roadmap that
|
|
59
|
+
led to 1.0.0.
|
|
58
60
|
- [Changelog](./CHANGELOG.md): published and upcoming changes.
|
|
59
61
|
|
|
60
62
|
## AI coding assistants
|
|
@@ -85,51 +87,8 @@ and package entry points for search and indexing tools.
|
|
|
85
87
|
|
|
86
88
|
## Development and releases
|
|
87
89
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
npm run check
|
|
91
|
-
npm test
|
|
92
|
-
npm pack
|
|
93
|
-
npm ci --prefix site
|
|
94
|
-
npm run build --prefix site
|
|
95
|
-
npm run preview --prefix site
|
|
96
|
-
```
|
|
97
|
-
|
|
98
|
-
`tsc` emits individual ESM files and TypeScript declarations. CSS is shipped
|
|
99
|
-
without compilation and processed by the consuming application's Tailwind build.
|
|
100
|
-
The public site is Storybook in `site/`. `npm run build --prefix site`
|
|
101
|
-
typechecks the stories, builds Storybook into `site/dist`, then checks the
|
|
102
|
-
static output. Vercel serves that directory; `/playground` and `/storybook`
|
|
103
|
-
redirect to `/`. The site and Storybook dependencies are not part of the npm
|
|
104
|
-
package. Optional feature libraries (markdown, dates, toasts, theme, Next) are
|
|
105
|
-
peer dependencies and are required only when those entry points are imported.
|
|
106
|
-
The package does not bundle React.
|
|
107
|
-
|
|
108
|
-
Run `npm run dev --prefix site` (or `npm run storybook --prefix site`) for the
|
|
109
|
-
playground on port 6006. Existing examples live in `stories/` and are imported
|
|
110
|
-
by CSF story files in `site/stories/`, so component tests and previews share the
|
|
111
|
-
same examples. Add Storybook entries there when adding an example; the
|
|
112
|
-
static-output check reports any existing demo that has no Storybook entry.
|
|
113
|
-
Interactive stories use Storybook args so changing a control or interacting with
|
|
114
|
-
the component updates the same value. Configuration is in `site/.storybook/`.
|
|
115
|
-
|
|
116
|
-
The Storybook preview disables the kit's theme shortcut and uses the theme
|
|
117
|
-
toolbar instead. The accessibility panel checks the rendered story; it does not
|
|
118
|
-
replace keyboard or assistive-technology testing.
|
|
119
|
-
|
|
120
|
-
Bumping the version in `package.json` and merging to `main` (path-filtered
|
|
121
|
-
to package files) or running `workflow_dispatch` publishes through npm OIDC
|
|
122
|
-
trusted publishing after check, test, and pack gates. Versions already on npm
|
|
123
|
-
are skipped.
|
|
124
|
-
|
|
125
|
-
If trusted publishing is not configured, pack and publish a validated
|
|
126
|
-
artifact with the package owner's npm login:
|
|
127
|
-
|
|
128
|
-
```sh
|
|
129
|
-
mkdir -p release-artifacts
|
|
130
|
-
npm pack --pack-destination release-artifacts
|
|
131
|
-
npm publish ./release-artifacts/atlas-react-kit-0.12.0.tgz --access public
|
|
132
|
-
```
|
|
90
|
+
Setup, checks, the component checklist, and releases are in
|
|
91
|
+
[CONTRIBUTING.md](./CONTRIBUTING.md).
|
|
133
92
|
|
|
134
93
|
Pin an exact package version in consuming applications. Roll back an upgrade by
|
|
135
94
|
restoring the previous version and lockfile. License: MIT; upstream notices are
|
package/dist/accordion.d.ts
CHANGED
|
@@ -5,6 +5,7 @@ declare function Accordion({ ...props }: React.ComponentProps<typeof AccordionPr
|
|
|
5
5
|
declare function AccordionItem({ className, ...props }: React.ComponentProps<typeof AccordionPrimitive.Item>): React.JSX.Element;
|
|
6
6
|
/** `headerClassName` styles the heading wrapper (`data-slot="accordion-header"`). */
|
|
7
7
|
declare function AccordionTrigger({ className, headerClassName, children, ...props }: React.ComponentProps<typeof AccordionPrimitive.Trigger> & {
|
|
8
|
+
/** Classes for the heading wrapper around the trigger. */
|
|
8
9
|
headerClassName?: string;
|
|
9
10
|
}): React.JSX.Element;
|
|
10
11
|
/**
|
package/dist/avatar.d.ts
CHANGED
|
@@ -6,6 +6,7 @@ import { Avatar as AvatarPrimitive } from "radix-ui";
|
|
|
6
6
|
* replaces it.
|
|
7
7
|
*/
|
|
8
8
|
declare function Avatar({ className, size, ...props }: React.ComponentProps<typeof AvatarPrimitive.Root> & {
|
|
9
|
+
/** `sm` 24px, `default` 32px, `lg` 40px. Default `default`. */
|
|
9
10
|
size?: "sm" | "default" | "lg";
|
|
10
11
|
}): React.JSX.Element;
|
|
11
12
|
declare function AvatarImage({ className, ...props }: React.ComponentProps<typeof AvatarPrimitive.Image>): React.JSX.Element;
|
package/dist/badge.d.ts
CHANGED
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
2
|
import { type VariantProps } from "class-variance-authority";
|
|
3
|
+
import type { AsChildProps } from "./internal/as-child.js";
|
|
3
4
|
declare const badgeVariants: (props?: ({
|
|
4
5
|
variant?: "default" | "destructive" | "ghost" | "link" | "outline" | "secondary" | "soft" | null | undefined;
|
|
5
6
|
tone?: "destructive" | "neutral" | "success" | "warning" | null | undefined;
|
|
6
7
|
} & import("class-variance-authority/types").ClassProp) | undefined) => string;
|
|
7
|
-
declare function Badge({ className, variant, tone, asChild, ...props }: React.ComponentProps<"span"> & VariantProps<typeof badgeVariants> &
|
|
8
|
-
asChild?: boolean;
|
|
9
|
-
}): React.JSX.Element;
|
|
8
|
+
declare function Badge({ className, variant, tone, asChild, ...props }: React.ComponentProps<"span"> & VariantProps<typeof badgeVariants> & AsChildProps): React.JSX.Element;
|
|
10
9
|
export { Badge, badgeVariants };
|
package/dist/breadcrumb.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
|
+
import type { AsChildProps } from "./internal/as-child.js";
|
|
2
3
|
/**
|
|
3
4
|
* Where the current page sits in a hierarchy. The last item is
|
|
4
5
|
* `BreadcrumbPage` (plain text with `aria-current="page"`), not a link.
|
|
@@ -7,9 +8,7 @@ declare function Breadcrumb({ ...props }: React.ComponentProps<"nav">): React.JS
|
|
|
7
8
|
declare function BreadcrumbList({ className, ...props }: React.ComponentProps<"ol">): React.JSX.Element;
|
|
8
9
|
declare function BreadcrumbItem({ className, ...props }: React.ComponentProps<"li">): React.JSX.Element;
|
|
9
10
|
/** Pass `asChild` to render a router link (e.g. Next `Link`) with these styles. */
|
|
10
|
-
declare function BreadcrumbLink({ asChild, className, ...props }: React.ComponentProps<"a"> &
|
|
11
|
-
asChild?: boolean;
|
|
12
|
-
}): React.JSX.Element;
|
|
11
|
+
declare function BreadcrumbLink({ asChild, className, ...props }: React.ComponentProps<"a"> & AsChildProps): React.JSX.Element;
|
|
13
12
|
declare function BreadcrumbPage({ className, ...props }: React.ComponentProps<"span">): React.JSX.Element;
|
|
14
13
|
declare function BreadcrumbSeparator({ children, className, ...props }: React.ComponentProps<"li">): React.JSX.Element;
|
|
15
14
|
declare function BreadcrumbEllipsis({ className, ...props }: React.ComponentProps<"span">): React.JSX.Element;
|
package/dist/button.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
2
|
import { type VariantProps } from "class-variance-authority";
|
|
3
|
+
import type { AsChildProps } from "./internal/as-child.js";
|
|
3
4
|
/**
|
|
4
5
|
* Outline and ghost hover/focus: selected-foreground border, selected wash, and
|
|
5
6
|
* `selected-foreground` text (primary in light mode, a lighter purple that
|
|
@@ -11,7 +12,5 @@ declare const buttonVariants: (props?: ({
|
|
|
11
12
|
variant?: "default" | "destructive" | "ghost" | "link" | "outline" | "secondary" | null | undefined;
|
|
12
13
|
size?: "default" | "icon" | "icon-lg" | "icon-sm" | "icon-xs" | "lg" | "sm" | "xs" | null | undefined;
|
|
13
14
|
} & import("class-variance-authority/types").ClassProp) | undefined) => string;
|
|
14
|
-
declare function Button({ className, variant, size, asChild, ...props }: React.ComponentProps<"button"> & VariantProps<typeof buttonVariants> &
|
|
15
|
-
asChild?: boolean;
|
|
16
|
-
}): React.JSX.Element;
|
|
15
|
+
declare function Button({ className, variant, size, asChild, ...props }: React.ComponentProps<"button"> & VariantProps<typeof buttonVariants> & AsChildProps): React.JSX.Element;
|
|
17
16
|
export { Button, buttonVariants, quietButtonInteraction };
|
package/dist/calendar.d.ts
CHANGED
|
@@ -2,9 +2,11 @@ import * as React from "react";
|
|
|
2
2
|
import { DayPicker, type DayButton, type Locale } from "react-day-picker";
|
|
3
3
|
import { Button } from "./button.js";
|
|
4
4
|
declare function Calendar({ className, classNames, showOutsideDays, captionLayout, buttonVariant, locale, formatters, labels, components, ...props }: React.ComponentProps<typeof DayPicker> & {
|
|
5
|
+
/** Button variant for the previous / next month buttons. Default `ghost`. */
|
|
5
6
|
buttonVariant?: React.ComponentProps<typeof Button>["variant"];
|
|
6
7
|
}): React.JSX.Element;
|
|
7
8
|
declare function CalendarDayButton({ className, day, modifiers, locale, ...props }: React.ComponentProps<typeof DayButton> & {
|
|
9
|
+
/** Locale for the day's `data-day` date string. */
|
|
8
10
|
locale?: Partial<Locale>;
|
|
9
11
|
}): React.JSX.Element;
|
|
10
12
|
export { Calendar, CalendarDayButton };
|
package/dist/card.d.ts
CHANGED
|
@@ -8,11 +8,14 @@ import * as React from "react";
|
|
|
8
8
|
* scroll instead of widening the card. `overflow-hidden` still clips media.
|
|
9
9
|
*/
|
|
10
10
|
declare function Card({ className, size, flush, role, "aria-label": ariaLabel, "aria-labelledby": ariaLabelledBy, ...props }: React.ComponentProps<"div"> & {
|
|
11
|
+
/** `sm` tightens padding and gaps. Default `default`. */
|
|
11
12
|
size?: "default" | "sm";
|
|
13
|
+
/** Remove the padding so lists and tables run edge to edge. Default `false`. */
|
|
12
14
|
flush?: boolean;
|
|
13
15
|
}): React.JSX.Element;
|
|
14
16
|
declare function CardHeader({ className, ...props }: React.ComponentProps<"div">): React.JSX.Element;
|
|
15
17
|
declare function CardTitle({ as: Component, className, ...props }: React.ComponentProps<"div"> & {
|
|
18
|
+
/** Element to render, e.g. a heading level for the outline. Default `div`. */
|
|
16
19
|
as?: "div" | "h1" | "h2" | "h3" | "h4";
|
|
17
20
|
}): React.JSX.Element;
|
|
18
21
|
declare function CardDescription({ className, ...props }: React.ComponentProps<"div">): React.JSX.Element;
|
package/dist/chart.d.ts
ADDED
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
/**
|
|
3
|
+
* Building blocks for small SVG charts: a plot frame in viewBox units, axis
|
|
4
|
+
* math, a gridline axis, a tooltip, a legend, and pointer/arrow-key
|
|
5
|
+
* scrubbing. The chart itself (bars, lines, what they mean) stays in the
|
|
6
|
+
* app; these pieces keep every chart's axis, readout, and keyboard use alike.
|
|
7
|
+
*/
|
|
8
|
+
/** Space around the plot, in viewBox units, for axis labels. */
|
|
9
|
+
export type ChartPadding = {
|
|
10
|
+
/** Above the plot. Default 10. */
|
|
11
|
+
top: number;
|
|
12
|
+
/** Right of the plot. Default 12. */
|
|
13
|
+
right: number;
|
|
14
|
+
/** Below the baseline, for x labels. Default 20. */
|
|
15
|
+
bottom: number;
|
|
16
|
+
/** Left of the plot, for y labels. Default 40. */
|
|
17
|
+
left: number;
|
|
18
|
+
};
|
|
19
|
+
/** A chart's viewBox and the plot box inside it, in viewBox units. */
|
|
20
|
+
export type ChartFrame = {
|
|
21
|
+
/** The whole chart's width. */
|
|
22
|
+
width: number;
|
|
23
|
+
/** The whole chart's height. */
|
|
24
|
+
height: number;
|
|
25
|
+
/** The `viewBox` attribute for the chart's `<svg>`. */
|
|
26
|
+
viewBox: string;
|
|
27
|
+
/** The plot's left edge on x. */
|
|
28
|
+
left: number;
|
|
29
|
+
/** The plot's right edge on x. */
|
|
30
|
+
right: number;
|
|
31
|
+
/** The plot's top edge on y. */
|
|
32
|
+
top: number;
|
|
33
|
+
/** The plot's bottom edge on y: the baseline, where 0 sits. */
|
|
34
|
+
bottom: number;
|
|
35
|
+
/** `right - left`. */
|
|
36
|
+
plotWidth: number;
|
|
37
|
+
/** `bottom - top`. */
|
|
38
|
+
plotHeight: number;
|
|
39
|
+
};
|
|
40
|
+
/**
|
|
41
|
+
* Frame for a chart `height` units tall. Charts that share a `width` and
|
|
42
|
+
* padding line up when stacked. The SVG scales to its box, so these are
|
|
43
|
+
* viewBox units, not pixels.
|
|
44
|
+
*/
|
|
45
|
+
export declare function chartFrame(height: number, { width, padding, }?: {
|
|
46
|
+
/** The chart's width in viewBox units. Default 640. */
|
|
47
|
+
width?: number;
|
|
48
|
+
/** Overrides for any side of the default padding. */
|
|
49
|
+
padding?: Partial<ChartPadding>;
|
|
50
|
+
}): ChartFrame;
|
|
51
|
+
/**
|
|
52
|
+
* Round `max` up to a clean axis ceiling (1, 2, 2.5, 4, or 5 × 10^n). The 4
|
|
53
|
+
* step keeps a value just past 25 off a half-empty 50 axis.
|
|
54
|
+
*/
|
|
55
|
+
export declare function niceCeil(max: number): number;
|
|
56
|
+
/** `count` even steps from 0 to `niceCeil(max)`, both ends included. */
|
|
57
|
+
export declare function niceTicks(max: number, count?: number): number[];
|
|
58
|
+
/** Linear scale from [0, domainMax] onto [0, rangeMax]; a zero domain maps to 0. */
|
|
59
|
+
export declare function scaleLinear(domainMax: number, rangeMax: number): (value: number) => number;
|
|
60
|
+
/** SVG path through the points (`M x y L x y …`); empty for no points. */
|
|
61
|
+
export declare function linePath(points: ReadonlyArray<{
|
|
62
|
+
x: number;
|
|
63
|
+
y: number;
|
|
64
|
+
}>): string;
|
|
65
|
+
/**
|
|
66
|
+
* A bar with a rounded data end and a square baseline. (x, y) is the top
|
|
67
|
+
* left; the bar grows down to y + h. Pass `r = 0` for a square bar.
|
|
68
|
+
*/
|
|
69
|
+
export declare function roundedTopBarPath(x: number, y: number, w: number, h: number, r?: number): string;
|
|
70
|
+
/** Short axis label: 1500 → "1.5k", 2000000 → "2M". */
|
|
71
|
+
export declare function compactNumber(n: number): string;
|
|
72
|
+
type IndexRange = {
|
|
73
|
+
/** The first data index the plot can pick. */
|
|
74
|
+
min: number;
|
|
75
|
+
/** The last data index the plot can pick; may stop short of the axis end. */
|
|
76
|
+
max: number;
|
|
77
|
+
};
|
|
78
|
+
/** The index range plus how many intervals the plot's width spans. */
|
|
79
|
+
type PlotSteps = IndexRange & {
|
|
80
|
+
/** Intervals across the plot width (data points minus one on a full axis). */
|
|
81
|
+
steps: number;
|
|
82
|
+
};
|
|
83
|
+
/**
|
|
84
|
+
* Data index at viewBox x `viewX`. The plot spans `steps` intervals from
|
|
85
|
+
* `min`; the result is clamped to [min, max], so `max` can stop short of the
|
|
86
|
+
* axis (days not reached yet).
|
|
87
|
+
*/
|
|
88
|
+
export declare function indexAtPlotX(viewX: number, frame: ChartFrame, { steps, min, max }: PlotSteps): number;
|
|
89
|
+
/** Arrow-key move by `delta`, clamped; with nothing picked it starts at `min`. */
|
|
90
|
+
export declare function stepIndex(current: number | null, delta: 1 | -1, { min, max }: IndexRange): number;
|
|
91
|
+
/**
|
|
92
|
+
* Crosshair state: the pointer or the arrow keys pick a data index in
|
|
93
|
+
* [min, max], and Escape or leaving the plot clears it. Put `svgRef` and the
|
|
94
|
+
* handlers on the chart's `<svg>`, which needs `tabIndex={0}`.
|
|
95
|
+
*/
|
|
96
|
+
export declare function useChartScrub({ frame, steps, min, max, }: PlotSteps & {
|
|
97
|
+
/** The chart's frame, for mapping pointer x onto the plot. */
|
|
98
|
+
frame: ChartFrame;
|
|
99
|
+
}): {
|
|
100
|
+
svgRef: React.RefObject<SVGSVGElement | null>;
|
|
101
|
+
index: number | null;
|
|
102
|
+
clear: () => void;
|
|
103
|
+
onPointerMove: (event: React.PointerEvent<SVGSVGElement>) => void;
|
|
104
|
+
onKeyDown: (event: React.KeyboardEvent) => void;
|
|
105
|
+
};
|
|
106
|
+
/**
|
|
107
|
+
* Horizontal gridlines from 0 to `niceCeil(yMax)` with labels in the left
|
|
108
|
+
* padding. Render inside the chart's `<svg>`.
|
|
109
|
+
*/
|
|
110
|
+
export declare function ChartYAxisGrid({ frame, yMax, tickCount, format, }: {
|
|
111
|
+
/** The chart's frame; lines span the plot, labels sit in the left padding. */
|
|
112
|
+
frame: ChartFrame;
|
|
113
|
+
/** The largest data value; the axis tops out at `niceCeil(yMax)`. */
|
|
114
|
+
yMax: number;
|
|
115
|
+
/** How many even steps from 0 to the top. Default 4. */
|
|
116
|
+
tickCount?: number;
|
|
117
|
+
/** Label text for a tick value. Default `compactNumber`. */
|
|
118
|
+
format?: (value: number) => string;
|
|
119
|
+
}): React.JSX.Element;
|
|
120
|
+
/**
|
|
121
|
+
* Readout for the hovered or focused point, placed in percent of the chart
|
|
122
|
+
* box (its wrapper must be `relative`). It sits right of the point and
|
|
123
|
+
* flips left past 55% so it does not clip at the edge.
|
|
124
|
+
*/
|
|
125
|
+
export declare function ChartTooltip({ xPct, yPct, className, style, children, ...props }: {
|
|
126
|
+
/** The point's x, in percent of the chart box's width. */
|
|
127
|
+
xPct: number;
|
|
128
|
+
/** The point's y, in percent of the chart box's height; capped at 60. */
|
|
129
|
+
yPct: number;
|
|
130
|
+
} & React.ComponentProps<"div">): React.JSX.Element;
|
|
131
|
+
/** A series' line key, shared by tooltip rows and legend keys. */
|
|
132
|
+
type SeriesKeyProps = {
|
|
133
|
+
/** The series name. */
|
|
134
|
+
label: React.ReactNode;
|
|
135
|
+
/** The series stroke, e.g. `stroke-primary`. */
|
|
136
|
+
swatchClassName: string;
|
|
137
|
+
/** Draws the swatch dashed, for a dashed series line. */
|
|
138
|
+
dashed?: boolean;
|
|
139
|
+
};
|
|
140
|
+
/** Tooltip row: the value leads, the label follows, keyed by the series stroke. */
|
|
141
|
+
export declare function ChartTooltipRow({ label, value, swatchClassName, dashed, }: SeriesKeyProps & {
|
|
142
|
+
/** The series' value at the point, shown first in bold. */
|
|
143
|
+
value: React.ReactNode;
|
|
144
|
+
}): React.JSX.Element;
|
|
145
|
+
/** Row of legend keys under a chart. */
|
|
146
|
+
export declare function ChartLegend({ className, ...props }: React.ComponentProps<"div">): React.JSX.Element;
|
|
147
|
+
/** One legend entry: a solid or dashed line swatch and its label. */
|
|
148
|
+
export declare function ChartLegendKey({ label, swatchClassName, dashed, className, }: SeriesKeyProps & {
|
|
149
|
+
/** Classes for the key's wrapper. */
|
|
150
|
+
className?: string;
|
|
151
|
+
}): React.JSX.Element;
|
|
152
|
+
export {};
|
package/dist/chart.js
ADDED
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
import { clamp } from "./internal/math.js";
|
|
5
|
+
import { overlaySurface } from "./internal/overlay-styles.js";
|
|
6
|
+
import { cn } from "./utils.js";
|
|
7
|
+
/** Room for compact y labels on the left and x labels underneath. */
|
|
8
|
+
const defaultPadding = {
|
|
9
|
+
top: 10,
|
|
10
|
+
right: 12,
|
|
11
|
+
bottom: 20,
|
|
12
|
+
left: 40,
|
|
13
|
+
};
|
|
14
|
+
/**
|
|
15
|
+
* Frame for a chart `height` units tall. Charts that share a `width` and
|
|
16
|
+
* padding line up when stacked. The SVG scales to its box, so these are
|
|
17
|
+
* viewBox units, not pixels.
|
|
18
|
+
*/
|
|
19
|
+
export function chartFrame(height, { width = 640, padding, } = {}) {
|
|
20
|
+
const pad = { ...defaultPadding, ...padding };
|
|
21
|
+
const plotWidth = width - pad.left - pad.right;
|
|
22
|
+
const plotHeight = height - pad.top - pad.bottom;
|
|
23
|
+
return {
|
|
24
|
+
width,
|
|
25
|
+
height,
|
|
26
|
+
viewBox: `0 0 ${width} ${height}`,
|
|
27
|
+
left: pad.left,
|
|
28
|
+
right: pad.left + plotWidth,
|
|
29
|
+
top: pad.top,
|
|
30
|
+
bottom: pad.top + plotHeight,
|
|
31
|
+
plotWidth,
|
|
32
|
+
plotHeight,
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Round `max` up to a clean axis ceiling (1, 2, 2.5, 4, or 5 × 10^n). The 4
|
|
37
|
+
* step keeps a value just past 25 off a half-empty 50 axis.
|
|
38
|
+
*/
|
|
39
|
+
export function niceCeil(max) {
|
|
40
|
+
if (max <= 0)
|
|
41
|
+
return 1;
|
|
42
|
+
const pow = 10 ** Math.floor(Math.log10(max));
|
|
43
|
+
for (const step of [1, 2, 2.5, 4, 5]) {
|
|
44
|
+
if (max <= step * pow)
|
|
45
|
+
return step * pow;
|
|
46
|
+
}
|
|
47
|
+
return 10 * pow;
|
|
48
|
+
}
|
|
49
|
+
/** `count` even steps from 0 to `niceCeil(max)`, both ends included. */
|
|
50
|
+
export function niceTicks(max, count = 4) {
|
|
51
|
+
const top = niceCeil(max);
|
|
52
|
+
return Array.from({ length: count + 1 }, (_, i) => (top / count) * i);
|
|
53
|
+
}
|
|
54
|
+
/** Linear scale from [0, domainMax] onto [0, rangeMax]; a zero domain maps to 0. */
|
|
55
|
+
export function scaleLinear(domainMax, rangeMax) {
|
|
56
|
+
return (value) => domainMax === 0 ? 0 : (value / domainMax) * rangeMax;
|
|
57
|
+
}
|
|
58
|
+
function round2(n) {
|
|
59
|
+
return Math.round(n * 100) / 100;
|
|
60
|
+
}
|
|
61
|
+
/** SVG path through the points (`M x y L x y …`); empty for no points. */
|
|
62
|
+
export function linePath(points) {
|
|
63
|
+
return points
|
|
64
|
+
.map((p, i) => `${i === 0 ? "M" : "L"} ${round2(p.x)} ${round2(p.y)}`)
|
|
65
|
+
.join(" ");
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* A bar with a rounded data end and a square baseline. (x, y) is the top
|
|
69
|
+
* left; the bar grows down to y + h. Pass `r = 0` for a square bar.
|
|
70
|
+
*/
|
|
71
|
+
export function roundedTopBarPath(x, y, w, h, r = 4) {
|
|
72
|
+
const rad = Math.min(r, w / 2, h);
|
|
73
|
+
return [
|
|
74
|
+
`M ${round2(x)} ${round2(y + h)}`,
|
|
75
|
+
`L ${round2(x)} ${round2(y + rad)}`,
|
|
76
|
+
`Q ${round2(x)} ${round2(y)} ${round2(x + rad)} ${round2(y)}`,
|
|
77
|
+
`L ${round2(x + w - rad)} ${round2(y)}`,
|
|
78
|
+
`Q ${round2(x + w)} ${round2(y)} ${round2(x + w)} ${round2(y + rad)}`,
|
|
79
|
+
`L ${round2(x + w)} ${round2(y + h)}`,
|
|
80
|
+
"Z",
|
|
81
|
+
].join(" ");
|
|
82
|
+
}
|
|
83
|
+
function trimZero(n) {
|
|
84
|
+
const fixed = n.toFixed(1);
|
|
85
|
+
return fixed.endsWith(".0") ? fixed.slice(0, -2) : fixed;
|
|
86
|
+
}
|
|
87
|
+
/** Short axis label: 1500 → "1.5k", 2000000 → "2M". */
|
|
88
|
+
export function compactNumber(n) {
|
|
89
|
+
const abs = Math.abs(n);
|
|
90
|
+
if (abs >= 1_000_000)
|
|
91
|
+
return `${trimZero(n / 1_000_000)}M`;
|
|
92
|
+
if (abs >= 1_000)
|
|
93
|
+
return `${trimZero(n / 1_000)}k`;
|
|
94
|
+
return String(Math.round(n));
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Data index at viewBox x `viewX`. The plot spans `steps` intervals from
|
|
98
|
+
* `min`; the result is clamped to [min, max], so `max` can stop short of the
|
|
99
|
+
* axis (days not reached yet).
|
|
100
|
+
*/
|
|
101
|
+
export function indexAtPlotX(viewX, frame, { steps, min, max }) {
|
|
102
|
+
const index = Math.round(((viewX - frame.left) / frame.plotWidth) * steps) + min;
|
|
103
|
+
return clamp(index, min, max);
|
|
104
|
+
}
|
|
105
|
+
/** Arrow-key move by `delta`, clamped; with nothing picked it starts at `min`. */
|
|
106
|
+
export function stepIndex(current, delta, { min, max }) {
|
|
107
|
+
if (current === null)
|
|
108
|
+
return min;
|
|
109
|
+
return clamp(current + delta, min, max);
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Crosshair state: the pointer or the arrow keys pick a data index in
|
|
113
|
+
* [min, max], and Escape or leaving the plot clears it. Put `svgRef` and the
|
|
114
|
+
* handlers on the chart's `<svg>`, which needs `tabIndex={0}`.
|
|
115
|
+
*/
|
|
116
|
+
export function useChartScrub({ frame, steps, min, max, }) {
|
|
117
|
+
const svgRef = React.useRef(null);
|
|
118
|
+
const [index, setIndex] = React.useState(null);
|
|
119
|
+
const clear = React.useCallback(() => setIndex(null), []);
|
|
120
|
+
const onPointerMove = React.useCallback((event) => {
|
|
121
|
+
const rect = svgRef.current?.getBoundingClientRect();
|
|
122
|
+
if (!rect)
|
|
123
|
+
return;
|
|
124
|
+
const viewX = ((event.clientX - rect.left) / rect.width) * frame.width;
|
|
125
|
+
setIndex(indexAtPlotX(viewX, frame, { steps, min, max }));
|
|
126
|
+
}, [frame, steps, min, max]);
|
|
127
|
+
const onKeyDown = React.useCallback((event) => {
|
|
128
|
+
if (event.key === "ArrowRight" || event.key === "ArrowLeft") {
|
|
129
|
+
const delta = event.key === "ArrowRight" ? 1 : -1;
|
|
130
|
+
setIndex((current) => stepIndex(current, delta, { min, max }));
|
|
131
|
+
event.preventDefault();
|
|
132
|
+
}
|
|
133
|
+
else if (event.key === "Escape") {
|
|
134
|
+
setIndex(null);
|
|
135
|
+
}
|
|
136
|
+
}, [min, max]);
|
|
137
|
+
return { svgRef, index, clear, onPointerMove, onKeyDown };
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Horizontal gridlines from 0 to `niceCeil(yMax)` with labels in the left
|
|
141
|
+
* padding. Render inside the chart's `<svg>`.
|
|
142
|
+
*/
|
|
143
|
+
export function ChartYAxisGrid({ frame, yMax, tickCount = 4, format = compactNumber, }) {
|
|
144
|
+
const ticks = niceTicks(yMax, tickCount);
|
|
145
|
+
const top = ticks[ticks.length - 1] ?? 1;
|
|
146
|
+
const y = (value) => frame.bottom - (value / top) * frame.plotHeight;
|
|
147
|
+
return (_jsx("g", { "data-slot": "chart-y-axis", children: ticks.map((tick) => (_jsxs("g", { children: [_jsx("line", { x1: frame.left, y1: y(tick), x2: frame.right, y2: y(tick), strokeWidth: 1, className: "stroke-border" }), _jsx("text", { x: frame.left - 6, y: y(tick) + 3, textAnchor: "end", className: "fill-muted-foreground text-[10px] tabular-nums", children: format(tick) })] }, tick))) }));
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* Readout for the hovered or focused point, placed in percent of the chart
|
|
151
|
+
* box (its wrapper must be `relative`). It sits right of the point and
|
|
152
|
+
* flips left past 55% so it does not clip at the edge.
|
|
153
|
+
*/
|
|
154
|
+
export function ChartTooltip({ xPct, yPct, className, style, children, ...props }) {
|
|
155
|
+
const flip = xPct > 55;
|
|
156
|
+
return (_jsx("div", { role: "status", "data-slot": "chart-tooltip", className: cn(overlaySurface, "pointer-events-none absolute z-10 min-w-32 px-2.5 py-2 text-xs", className), style: {
|
|
157
|
+
left: `${xPct}%`,
|
|
158
|
+
top: `${Math.min(yPct, 60)}%`,
|
|
159
|
+
transform: flip ? "translateX(calc(-100% - 12px))" : "translateX(12px)",
|
|
160
|
+
...style,
|
|
161
|
+
}, ...props, children: children }));
|
|
162
|
+
}
|
|
163
|
+
function SeriesSwatch({ width, strokeWidth, dashed, className, }) {
|
|
164
|
+
return (_jsx("svg", { width: width, height: "4", "aria-hidden": true, className: "shrink-0", children: _jsx("line", { x1: "0", y1: "2", x2: width, y2: "2", strokeWidth: strokeWidth, strokeDasharray: dashed ? "3 2" : undefined, className: className }) }));
|
|
165
|
+
}
|
|
166
|
+
/** Tooltip row: the value leads, the label follows, keyed by the series stroke. */
|
|
167
|
+
export function ChartTooltipRow({ label, value, swatchClassName, dashed, }) {
|
|
168
|
+
return (_jsxs("div", { "data-slot": "chart-tooltip-row", className: "flex items-center gap-2 py-0.5", children: [_jsx(SeriesSwatch, { width: 14, strokeWidth: 2.5, dashed: dashed, className: swatchClassName }), _jsx("span", { className: "font-semibold tabular-nums", children: value }), _jsx("span", { className: "text-muted-foreground", children: label })] }));
|
|
169
|
+
}
|
|
170
|
+
/** Row of legend keys under a chart. */
|
|
171
|
+
export function ChartLegend({ className, ...props }) {
|
|
172
|
+
return (_jsx("div", { "data-slot": "chart-legend", className: cn("flex flex-wrap gap-x-4 gap-y-1 text-xs text-muted-foreground", className), ...props }));
|
|
173
|
+
}
|
|
174
|
+
/** One legend entry: a solid or dashed line swatch and its label. */
|
|
175
|
+
export function ChartLegendKey({ label, swatchClassName, dashed, className, }) {
|
|
176
|
+
return (_jsxs("span", { "data-slot": "chart-legend-key", className: cn("inline-flex items-center gap-1.5", className), children: [_jsx(SeriesSwatch, { width: 16, strokeWidth: 2, dashed: dashed, className: swatchClassName }), label] }));
|
|
177
|
+
}
|