@sableui/react 0.15.0 → 0.17.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/CHANGELOG.md +99 -0
- package/bin/sable-lint.mjs +5 -1
- package/dist/{_contract-CkqXDVww.d.ts → _contract-CDa9-NdB.d.ts} +47 -1
- package/dist/blocks/approval-block.js +30 -48
- package/dist/blocks/approval-block.meta.js +7 -8
- package/dist/blocks/index.d.ts +1 -1
- package/dist/blocks/wallet-block.js +2 -2
- package/dist/components/app-shell/app-shell-frame.js +16 -0
- package/dist/components/app-shell/app-shell-main.js +12 -0
- package/dist/components/app-shell/app-shell.js +28 -4
- package/dist/components/app-shell/app-shell.meta.js +88 -4
- package/dist/components/area-chart/area-chart.meta.js +6 -1
- package/dist/components/attachment/attachment.meta.js +2 -1
- package/dist/components/attachment/attachment.variants.js +1 -1
- package/dist/components/badge/badge.meta.js +8 -1
- package/dist/components/badge/badge.variants.js +7 -7
- package/dist/components/banner/banner.meta.js +6 -1
- package/dist/components/banner/banner.variants.js +5 -5
- package/dist/components/bar-chart/bar-chart.meta.js +6 -1
- package/dist/components/caret-menu/caret-menu.js +5 -6
- package/dist/components/chat-composer/chat-composer.meta.js +2 -2
- package/dist/components/chat-composer/chat-composer.variants.js +1 -1
- package/dist/components/color-field/color-field.js +6 -8
- package/dist/components/combobox/combobox.js +27 -5
- package/dist/components/combobox/combobox.meta.js +10 -2
- package/dist/components/command-menu/command-menu.js +14 -3
- package/dist/components/command-menu/command-menu.meta.js +9 -1
- package/dist/components/date-picker/date-picker.meta.js +2 -2
- package/dist/components/feedback/feedback.js +1 -1
- package/dist/components/feedback/feedback.meta.js +2 -1
- package/dist/components/gradient-art/gradient-art.js +8 -5
- package/dist/components/gradient-art/gradient-art.meta.js +35 -2
- package/dist/components/input/input.meta.js +11 -3
- package/dist/components/input-group/input-group.js +1 -1
- package/dist/components/input-group/input-group.meta.js +1 -1
- package/dist/components/line-chart/line-chart.meta.js +6 -1
- package/dist/components/menu/menu.js +1 -1
- package/dist/components/meter/meter.js +53 -7
- package/dist/components/meter/meter.meta.js +51 -4
- package/dist/components/meter/meter.variants.js +43 -7
- package/dist/components/note/note.meta.js +5 -1
- package/dist/components/note/note.variants.js +4 -4
- package/dist/components/number-field/number-field.meta.js +2 -2
- package/dist/components/otp-field/otp-field.meta.js +2 -2
- package/dist/components/panel-group/panel-group.js +5 -8
- package/dist/components/pie-chart/pie-chart.meta.js +6 -1
- package/dist/components/radar-chart/radar-chart.meta.js +6 -1
- package/dist/components/radial-chart/radial-chart.meta.js +6 -1
- package/dist/components/search-input/search-input.meta.js +11 -3
- package/dist/components/section-rail/index.js +7 -0
- package/dist/components/section-rail/section-rail.js +153 -0
- package/dist/components/section-rail/section-rail.meta.js +189 -0
- package/dist/components/segmented-bar/segmented-bar.meta.js +8 -1
- package/dist/components/segmented-bar/segmented-bar.variants.js +7 -7
- package/dist/components/select/select.js +1 -1
- package/dist/components/select/select.meta.js +2 -2
- package/dist/components/sheet/sheet.js +5 -4
- package/dist/components/sidebar/sidebar.js +16 -1
- package/dist/components/sidebar/sidebar.meta.js +7 -2
- package/dist/components/sparkline/sparkline.meta.js +8 -1
- package/dist/components/status-dot/status-dot.meta.js +8 -1
- package/dist/components/status-dot/status-dot.variants.js +7 -7
- package/dist/components/stepper/stepper.meta.js +2 -1
- package/dist/components/stepper/stepper.variants.js +1 -1
- package/dist/components/table/table.meta.js +4 -4
- package/dist/components/textarea/textarea.js +5 -0
- package/dist/components/textarea/textarea.meta.js +3 -3
- package/dist/file-hashes.json +99 -92
- package/dist/foundations/accessibility.js +5 -0
- package/dist/index.d.ts +227 -25
- package/dist/index.js +12 -1
- package/dist/lib/chart/types.js +8 -8
- package/dist/lib/chart/use-arrival.js +5 -4
- package/dist/lib/field-shell.js +1 -1
- package/dist/lib/highlight-glide.js +6 -6
- package/dist/lib/scrub-gesture.js +6 -6
- package/dist/lib/taxonomy.js +1 -0
- package/dist/lib/use-active-section.js +59 -0
- package/dist/lib/use-caret-trigger.js +11 -18
- package/dist/lib/use-match-highlight.js +69 -0
- package/dist/lib/use-smooth-wheel.js +10 -8
- package/dist/presets/index.d.ts +13 -2
- package/dist/presets/index.js +17 -1
- package/dist/styles.css +100 -0
- package/dist/{theme-provider-CdSicAad.d.ts → theme-provider-BydfM5Z0.d.ts} +1 -1
- package/dist/themes/_contract.js +14 -0
- package/dist/themes/atelier/atelier.css +18 -0
- package/dist/themes/atelier/index.d.ts +1 -1
- package/dist/themes/contrast.js +48 -1
- package/dist/themes/ember/ember.css +18 -0
- package/dist/themes/ember/index.d.ts +1 -1
- package/dist/themes/index.d.ts +2 -2
- package/dist/themes/meridian/index.d.ts +1 -1
- package/dist/themes/meridian/meridian.css +18 -0
- package/dist/themes/poppy/index.d.ts +1 -1
- package/dist/themes/poppy/poppy.css +18 -0
- package/dist/themes/prism/index.d.ts +1 -1
- package/dist/themes/prism/prism.css +18 -0
- package/dist/themes/sable/index.d.ts +1 -1
- package/dist/themes/sable/sable.css +18 -0
- package/dist/themes/verdant/index.d.ts +1 -1
- package/dist/themes/verdant/verdant.css +18 -0
- package/llms/blocks/approval-block.md +40 -55
- package/llms/blocks/wallet-block.md +5 -4
- package/llms/components/app-shell.md +72 -3
- package/llms/components/area-chart.md +1 -1
- package/llms/components/attachment.md +1 -1
- package/llms/components/badge.md +1 -1
- package/llms/components/banner.md +1 -1
- package/llms/components/bar-chart.md +1 -1
- package/llms/components/chat-composer.md +1 -1
- package/llms/components/combobox.md +2 -1
- package/llms/components/command-menu.md +3 -2
- package/llms/components/date-picker.md +1 -1
- package/llms/components/feedback.md +1 -1
- package/llms/components/gradient-art.md +28 -1
- package/llms/components/input-group.md +1 -1
- package/llms/components/input.md +4 -2
- package/llms/components/line-chart.md +1 -1
- package/llms/components/meter.md +33 -4
- package/llms/components/note.md +1 -1
- package/llms/components/number-field.md +1 -1
- package/llms/components/otp-field.md +1 -1
- package/llms/components/pie-chart.md +1 -1
- package/llms/components/radar-chart.md +1 -1
- package/llms/components/radial-chart.md +1 -1
- package/llms/components/search-input.md +20 -2
- package/llms/components/section-rail.md +101 -0
- package/llms/components/segmented-bar.md +1 -1
- package/llms/components/select.md +1 -1
- package/llms/components/sidebar.md +3 -2
- package/llms/components/sparkline.md +1 -1
- package/llms/components/status-dot.md +1 -1
- package/llms/components/stepper.md +1 -1
- package/llms/components/table.md +2 -2
- package/llms/components/textarea.md +2 -2
- package/llms/foundations/accessibility.md +1 -0
- package/llms/reference/token-intent.md +22 -2
- package/llms/reference/utilities.md +5 -2
- package/llms-full.txt +367 -105
- package/llms.txt +6 -5
- package/package.json +1 -1
- package/registry.json +609 -67
- package/scripts/lint-rules.mjs +6 -3
- package/targets/ios.json +9 -0
|
@@ -1,22 +1,30 @@
|
|
|
1
1
|
// src/components/meter/meter.tsx
|
|
2
2
|
import { Meter as Base } from "@base-ui/react/meter";
|
|
3
|
-
import { createContext, useContext } from "react";
|
|
3
|
+
import { createContext, useContext, useState } from "react";
|
|
4
4
|
import { cn } from "../../lib/cn.js";
|
|
5
5
|
import {
|
|
6
6
|
meterIndicatorVariants,
|
|
7
7
|
meterRingValueVariants,
|
|
8
8
|
meterRingVariants,
|
|
9
|
+
meterSegmentedTrackVariants,
|
|
10
|
+
meterTickVariants,
|
|
9
11
|
meterTrackVariants
|
|
10
12
|
} from "./meter.variants.js";
|
|
11
13
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
12
14
|
var ToneContext = createContext("primary");
|
|
13
15
|
var SizeContext = createContext(void 0);
|
|
16
|
+
var SegmentsContext = createContext(null);
|
|
17
|
+
function filledTicks({ count, value, min, max }) {
|
|
18
|
+
if (value == null || value <= min) return 0;
|
|
19
|
+
const ratio = max === min ? 1 : Math.min(1, (value - min) / (max - min));
|
|
20
|
+
return Math.min(count, Math.max(1, Math.round(ratio * count)));
|
|
21
|
+
}
|
|
14
22
|
var RING_TONE_STROKE = {
|
|
15
23
|
primary: "var(--primary)",
|
|
16
|
-
"3": "var(--tone-3)",
|
|
17
|
-
success: "var(--success)",
|
|
18
|
-
warning: "var(--tone-warning)",
|
|
19
|
-
destructive: "var(--destructive)"
|
|
24
|
+
"3": "var(--tone-3-mark)",
|
|
25
|
+
success: "var(--success-mark)",
|
|
26
|
+
warning: "var(--tone-warning-mark)",
|
|
27
|
+
destructive: "var(--destructive-mark)"
|
|
20
28
|
};
|
|
21
29
|
function MeterRing({
|
|
22
30
|
value,
|
|
@@ -53,15 +61,30 @@ function MeterRing({
|
|
|
53
61
|
showValue ? /* @__PURE__ */ jsx(MeterValue, { className: meterRingValueVariants({ size }) }) : null
|
|
54
62
|
] });
|
|
55
63
|
}
|
|
56
|
-
function Meter({ className, children, tone = "primary", shape = "bar", size, strokeWidth = 6, showValue, ...props }) {
|
|
64
|
+
function Meter({ className, children, tone = "primary", shape = "bar", size, strokeWidth = 6, showValue, segments, ...props }) {
|
|
57
65
|
if (shape === "ring") {
|
|
58
66
|
return /* @__PURE__ */ jsx(ToneContext.Provider, { value: tone, children: /* @__PURE__ */ jsx(Base.Root, { className: cn(meterRingVariants({ size }), className), ...props, children: children ?? /* @__PURE__ */ jsx(MeterRing, { value: props.value ?? null, min: props.min, max: props.max, strokeWidth, showValue, tone, size }) }) });
|
|
59
67
|
}
|
|
60
|
-
|
|
68
|
+
const count = segments != null && Number.isFinite(segments) && segments >= 1 ? Math.floor(segments) : 0;
|
|
69
|
+
const segmented = count ? { count, value: props.value ?? null, min: props.min ?? 0, max: props.max ?? 100 } : null;
|
|
70
|
+
return /* @__PURE__ */ jsx(ToneContext.Provider, { value: tone, children: /* @__PURE__ */ jsx(SizeContext.Provider, { value: size, children: /* @__PURE__ */ jsx(SegmentsContext.Provider, { value: segmented, children: /* @__PURE__ */ jsx(Base.Root, { className: cn("w-full", className), ...props, children: children ?? /* @__PURE__ */ jsx(MeterTrack, {}) }) }) }) });
|
|
61
71
|
}
|
|
62
72
|
Meter.displayName = "Meter";
|
|
63
73
|
function MeterTrack({ className, children, size, ...props }) {
|
|
64
74
|
const inherited = useContext(SizeContext);
|
|
75
|
+
const tone = useContext(ToneContext);
|
|
76
|
+
const segments = useContext(SegmentsContext);
|
|
77
|
+
if (segments) {
|
|
78
|
+
return /* @__PURE__ */ jsx(
|
|
79
|
+
Base.Track,
|
|
80
|
+
{
|
|
81
|
+
"data-segmented": "",
|
|
82
|
+
className: cn(meterSegmentedTrackVariants({ size: size ?? inherited }), className),
|
|
83
|
+
...props,
|
|
84
|
+
children: /* @__PURE__ */ jsx(MeterTicks, { count: segments.count, filled: filledTicks(segments), tone })
|
|
85
|
+
}
|
|
86
|
+
);
|
|
87
|
+
}
|
|
65
88
|
return /* @__PURE__ */ jsx(
|
|
66
89
|
Base.Track,
|
|
67
90
|
{
|
|
@@ -72,6 +95,29 @@ function MeterTrack({ className, children, size, ...props }) {
|
|
|
72
95
|
);
|
|
73
96
|
}
|
|
74
97
|
MeterTrack.displayName = "MeterTrack";
|
|
98
|
+
function MeterTicks({ count, filled, tone }) {
|
|
99
|
+
const [shown, setShown] = useState(filled);
|
|
100
|
+
const [from, setFrom] = useState(filled);
|
|
101
|
+
if (filled !== shown) {
|
|
102
|
+
setFrom(shown);
|
|
103
|
+
setShown(filled);
|
|
104
|
+
}
|
|
105
|
+
return Array.from({ length: count }, (_, i) => {
|
|
106
|
+
const isFilled = i < filled;
|
|
107
|
+
const step = filled > from && i >= from && i < filled ? i - from : filled < from && i >= filled && i < from ? from - 1 - i : 0;
|
|
108
|
+
return /* @__PURE__ */ jsx(
|
|
109
|
+
"span",
|
|
110
|
+
{
|
|
111
|
+
"aria-hidden": "true",
|
|
112
|
+
"data-slot": "meter-tick",
|
|
113
|
+
"data-filled": isFilled ? "" : void 0,
|
|
114
|
+
className: meterTickVariants({ tone, filled: isFilled }),
|
|
115
|
+
style: step ? { transitionDelay: `calc(var(--motion-duration-slow) * ${Number((step / count).toFixed(4))} * var(--motion-expressive, 1))` } : void 0
|
|
116
|
+
},
|
|
117
|
+
i
|
|
118
|
+
);
|
|
119
|
+
});
|
|
120
|
+
}
|
|
75
121
|
function MeterIndicator({ className, tone, ...props }) {
|
|
76
122
|
const inherited = useContext(ToneContext);
|
|
77
123
|
return /* @__PURE__ */ jsx(
|
|
@@ -11,7 +11,12 @@ var meterMeta = {
|
|
|
11
11
|
"capacity",
|
|
12
12
|
"limit",
|
|
13
13
|
"password strength",
|
|
14
|
-
"level"
|
|
14
|
+
"level",
|
|
15
|
+
"segmented",
|
|
16
|
+
"tick bar",
|
|
17
|
+
"score",
|
|
18
|
+
"credits",
|
|
19
|
+
"spend limit"
|
|
15
20
|
],
|
|
16
21
|
usage: {
|
|
17
22
|
description: "Use Meter to show a static measurement against a known maximum \u2014 storage used, seats taken, API quota, password strength. It is announced as a meter, not a progress bar, so a screen-reader user hears a level rather than a task underway.",
|
|
@@ -36,6 +41,15 @@ var meterMeta = {
|
|
|
36
41
|
{
|
|
37
42
|
guidance: "do",
|
|
38
43
|
description: 'Use shape="ring" when the reading is a score that belongs in a small, glanceable spot on a card. It is the same measurement drawn as a circular gauge, not a different component.'
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
guidance: "do",
|
|
47
|
+
description: "Use `segments` (a tick bar, \u25AE\u25AE\u25AE\u25AE\u25AE\u25AE\u25AF\u25AF\u25AF\u25AF) for a discrete budget, score or quota read at a glance \u2014 a spend limit, credits left, an ICP lead score, months of data coverage. Pick a count that matches the unit when there is one (10 for a score out of 10, 12 for months) and otherwise size it to the track: a tick reads as a tick when it is taller than it is wide, about one per 8px of track (40 across a card row, 20 in a narrow table cell). Any reading above min shows at least one tick."
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
guidance: "dont",
|
|
51
|
+
description: "Do not segment a fine-grained continuous value where the exact proportion matters (disk at 73.4%) \u2014 ticks round it to the nearest mark; use the continuous bar. And do not use a tick bar for a task in flight or steps completed: that is Progress, or a Stepper for named steps.",
|
|
52
|
+
instead: ["Progress"]
|
|
39
53
|
}
|
|
40
54
|
],
|
|
41
55
|
anatomy: [
|
|
@@ -47,7 +61,7 @@ var meterMeta = {
|
|
|
47
61
|
{
|
|
48
62
|
name: "MeterIndicator",
|
|
49
63
|
required: true,
|
|
50
|
-
description: "The fill, sized from value and coloured by tone."
|
|
64
|
+
description: "The fill, sized from value and coloured by tone. Not used with `segments`: the track draws its ticks instead."
|
|
51
65
|
},
|
|
52
66
|
{
|
|
53
67
|
name: "MeterLabel",
|
|
@@ -105,6 +119,11 @@ var meterMeta = {
|
|
|
105
119
|
type: "boolean",
|
|
106
120
|
description: "Print the value in the ring's centre (formatted like MeterValue)."
|
|
107
121
|
},
|
|
122
|
+
{
|
|
123
|
+
name: "segments",
|
|
124
|
+
type: "number",
|
|
125
|
+
description: 'Draw the bar as this many separate ticks instead of a continuous fill (a quota, score or spend limit). Filled ticks = round(share \xD7 N), at least one for any reading above min, none at min. Ticks take the tone; empty ones sit on the rail colour. The segmented track has its own, taller sm / md / lg ladder. Bar only: ignored for shape="ring".'
|
|
126
|
+
},
|
|
108
127
|
{
|
|
109
128
|
name: "strokeWidth",
|
|
110
129
|
type: "number",
|
|
@@ -131,7 +150,7 @@ var meterMeta = {
|
|
|
131
150
|
{
|
|
132
151
|
name: "MeterTrack",
|
|
133
152
|
type: "Base UI Track props + className + size",
|
|
134
|
-
description: "The rail. Rendered automatically when Meter has no children; renders a MeterIndicator by default."
|
|
153
|
+
description: "The rail. Rendered automatically when Meter has no children; renders a MeterIndicator by default \u2014 or, when the Meter has `segments`, its row of ticks (children are not rendered then)."
|
|
135
154
|
},
|
|
136
155
|
{
|
|
137
156
|
name: "MeterIndicator",
|
|
@@ -157,11 +176,16 @@ var meterMeta = {
|
|
|
157
176
|
tokensUsed: [
|
|
158
177
|
"secondary",
|
|
159
178
|
"primary",
|
|
179
|
+
"tone-3",
|
|
160
180
|
"success",
|
|
161
181
|
"tone-warning",
|
|
162
182
|
"destructive",
|
|
163
183
|
"foreground",
|
|
164
|
-
"muted-foreground"
|
|
184
|
+
"muted-foreground",
|
|
185
|
+
"destructive-mark",
|
|
186
|
+
"success-mark",
|
|
187
|
+
"tone-warning-mark",
|
|
188
|
+
"tone-3-mark"
|
|
165
189
|
],
|
|
166
190
|
motion: [
|
|
167
191
|
{
|
|
@@ -182,6 +206,12 @@ var meterMeta = {
|
|
|
182
206
|
part: "MeterIndicator",
|
|
183
207
|
why: "Width and colour ease on the motion tokens (no recipe animates width) when the reading changes \u2014 freeing space after a delete visibly shrinks the bar, so the user sees the cause and effect rather than a different bar."
|
|
184
208
|
},
|
|
209
|
+
{
|
|
210
|
+
slot: "transition",
|
|
211
|
+
recipe: "custom",
|
|
212
|
+
part: "MeterTrack (segments)",
|
|
213
|
+
why: "With segments, the fill runs through the ticks from the edge it is leaving \u2014 left to right as it grows, right to left as it shrinks \u2014 each tick easing its colour on the fast token after a stagger that is a share of the slow token (a full sweep is one slow duration) \xD7 --motion-expressive. Every tick switching at once would read as a different bar, not the same budget moving; `subtle` lands them together and off is instant."
|
|
214
|
+
},
|
|
185
215
|
{
|
|
186
216
|
slot: "activity",
|
|
187
217
|
recipe: "none",
|
|
@@ -212,6 +242,23 @@ var meterMeta = {
|
|
|
212
242
|
</div>
|
|
213
243
|
<MeterTrack />
|
|
214
244
|
</Meter>`
|
|
245
|
+
},
|
|
246
|
+
{
|
|
247
|
+
label: "Segmented spend limit",
|
|
248
|
+
code: `<Meter value={1240} max={2000} segments={40} getAriaValueText={() => "\u20AC1,240 of \u20AC2,000 spent this month"}>
|
|
249
|
+
<div className="mb-2 flex flex-wrap items-center justify-between gap-x-3 gap-y-1">
|
|
250
|
+
<MeterLabel>Monthly spend limit</MeterLabel>
|
|
251
|
+
<MeterValue>{() => "\u20AC1,240 / \u20AC2,000"}</MeterValue>
|
|
252
|
+
</div>
|
|
253
|
+
<MeterTrack />
|
|
254
|
+
</Meter>`
|
|
255
|
+
},
|
|
256
|
+
{
|
|
257
|
+
label: "Segmented score cell",
|
|
258
|
+
code: `<div className="flex items-center gap-3">
|
|
259
|
+
<Meter value={8} max={10} segments={10} size="sm" tone="success" className="w-24" aria-label="ICP score" getAriaValueText={(_, v) => \`\${v} out of 10\`} />
|
|
260
|
+
<span className="text-mono-sm text-foreground">8/10</span>
|
|
261
|
+
</div>`
|
|
215
262
|
},
|
|
216
263
|
{
|
|
217
264
|
label: "Tones",
|
|
@@ -1,16 +1,17 @@
|
|
|
1
1
|
// src/components/meter/meter.variants.ts
|
|
2
2
|
import { cva } from "class-variance-authority";
|
|
3
|
+
var meterToneFill = {
|
|
4
|
+
primary: "bg-primary",
|
|
5
|
+
"3": "bg-tone-3-mark",
|
|
6
|
+
success: "bg-success-mark",
|
|
7
|
+
warning: "bg-tone-warning-mark",
|
|
8
|
+
destructive: "bg-destructive-mark"
|
|
9
|
+
};
|
|
3
10
|
var meterIndicatorVariants = cva(
|
|
4
11
|
"h-full rounded-full transition-[width,background-color] duration-(--motion-duration-slow) ease-decelerate",
|
|
5
12
|
{
|
|
6
13
|
variants: {
|
|
7
|
-
tone:
|
|
8
|
-
primary: "bg-primary",
|
|
9
|
-
"3": "bg-tone-3",
|
|
10
|
-
success: "bg-success",
|
|
11
|
-
warning: "bg-tone-warning",
|
|
12
|
-
destructive: "bg-destructive"
|
|
13
|
-
}
|
|
14
|
+
tone: meterToneFill
|
|
14
15
|
},
|
|
15
16
|
defaultVariants: {
|
|
16
17
|
tone: "primary"
|
|
@@ -29,6 +30,39 @@ var meterTrackVariants = cva("relative w-full overflow-hidden rounded-full bg-se
|
|
|
29
30
|
size: "md"
|
|
30
31
|
}
|
|
31
32
|
});
|
|
33
|
+
var meterSegmentedTrackVariants = cva("flex w-full gap-0.5", {
|
|
34
|
+
variants: {
|
|
35
|
+
size: {
|
|
36
|
+
sm: "h-3",
|
|
37
|
+
md: "h-4",
|
|
38
|
+
lg: "h-5"
|
|
39
|
+
}
|
|
40
|
+
},
|
|
41
|
+
defaultVariants: {
|
|
42
|
+
size: "md"
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
var meterTickVariants = cva(
|
|
46
|
+
// The corner follows the theme's smallest radius but stops at half a spacing
|
|
47
|
+
// step: a tick is a few px wide, and at the full radius-xs (4px in most
|
|
48
|
+
// themes) it rounded into a pill and the row read as a string of beads.
|
|
49
|
+
"h-full min-w-0 flex-1 rounded-[min(var(--radius-xs),--spacing(0.5))] transition-[background-color] duration-(--motion-duration-fast) ease-standard",
|
|
50
|
+
{
|
|
51
|
+
variants: {
|
|
52
|
+
tone: { primary: "", "3": "", success: "", warning: "", destructive: "" },
|
|
53
|
+
filled: { true: "", false: "bg-secondary" }
|
|
54
|
+
},
|
|
55
|
+
compoundVariants: Object.keys(meterToneFill).map((tone) => ({
|
|
56
|
+
tone,
|
|
57
|
+
filled: true,
|
|
58
|
+
class: meterToneFill[tone]
|
|
59
|
+
})),
|
|
60
|
+
defaultVariants: {
|
|
61
|
+
tone: "primary",
|
|
62
|
+
filled: false
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
);
|
|
32
66
|
var meterRingValueVariants = cva("absolute inset-0 flex items-center justify-center text-foreground", {
|
|
33
67
|
variants: {
|
|
34
68
|
size: {
|
|
@@ -57,5 +91,7 @@ export {
|
|
|
57
91
|
meterIndicatorVariants,
|
|
58
92
|
meterRingValueVariants,
|
|
59
93
|
meterRingVariants,
|
|
94
|
+
meterSegmentedTrackVariants,
|
|
95
|
+
meterTickVariants,
|
|
60
96
|
meterTrackVariants
|
|
61
97
|
};
|
|
@@ -5,10 +5,10 @@ var noteVariants = cva(
|
|
|
5
5
|
{
|
|
6
6
|
variants: {
|
|
7
7
|
tone: {
|
|
8
|
-
info: "border-tone-info/30 bg-tone-info/10",
|
|
9
|
-
warning: "border-tone-warning/30 bg-tone-warning/10",
|
|
10
|
-
success: "border-success/30 bg-success/10",
|
|
11
|
-
destructive: "border-destructive/30 bg-destructive/10",
|
|
8
|
+
info: "border-tone-info-mark/30 bg-tone-info-mark/10",
|
|
9
|
+
warning: "border-tone-warning-mark/30 bg-tone-warning-mark/10",
|
|
10
|
+
success: "border-success-mark/30 bg-success-mark/10",
|
|
11
|
+
destructive: "border-destructive-mark/30 bg-destructive-mark/10",
|
|
12
12
|
neutral: "border-border bg-muted/40"
|
|
13
13
|
}
|
|
14
14
|
},
|
|
@@ -107,14 +107,11 @@ function PanelResizer({
|
|
|
107
107
|
const delta = side === "left" ? ev.clientX - startX : startX - ev.clientX;
|
|
108
108
|
onWidthChange(startWidth + delta);
|
|
109
109
|
}
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
}
|
|
115
|
-
window.addEventListener("pointermove", onMove);
|
|
116
|
-
window.addEventListener("pointerup", onUp);
|
|
117
|
-
window.addEventListener("pointercancel", onUp);
|
|
110
|
+
const drag = new AbortController();
|
|
111
|
+
const { signal } = drag;
|
|
112
|
+
window.addEventListener("pointermove", onMove, { signal });
|
|
113
|
+
window.addEventListener("pointerup", () => drag.abort(), { signal });
|
|
114
|
+
window.addEventListener("pointercancel", () => drag.abort(), { signal });
|
|
118
115
|
}
|
|
119
116
|
function onKeyDown(e) {
|
|
120
117
|
const grow = side === "left" ? "ArrowRight" : "ArrowLeft";
|
|
@@ -168,7 +168,12 @@ var pieChartMeta = {
|
|
|
168
168
|
"popover",
|
|
169
169
|
"popover-foreground",
|
|
170
170
|
"foreground",
|
|
171
|
-
"background"
|
|
171
|
+
"background",
|
|
172
|
+
"tone-warning-mark",
|
|
173
|
+
"tone-info-mark",
|
|
174
|
+
"tone-1-mark",
|
|
175
|
+
"tone-2-mark",
|
|
176
|
+
"tone-3-mark"
|
|
172
177
|
],
|
|
173
178
|
motion: [
|
|
174
179
|
{
|
|
@@ -162,7 +162,12 @@ var radarChartMeta = {
|
|
|
162
162
|
"popover",
|
|
163
163
|
"popover-foreground",
|
|
164
164
|
"foreground",
|
|
165
|
-
"background"
|
|
165
|
+
"background",
|
|
166
|
+
"tone-warning-mark",
|
|
167
|
+
"tone-info-mark",
|
|
168
|
+
"tone-1-mark",
|
|
169
|
+
"tone-2-mark",
|
|
170
|
+
"tone-3-mark"
|
|
166
171
|
],
|
|
167
172
|
motion: [
|
|
168
173
|
{
|
|
@@ -174,7 +174,12 @@ var radialChartMeta = {
|
|
|
174
174
|
"popover",
|
|
175
175
|
"popover-foreground",
|
|
176
176
|
"foreground",
|
|
177
|
-
"background"
|
|
177
|
+
"background",
|
|
178
|
+
"tone-warning-mark",
|
|
179
|
+
"tone-info-mark",
|
|
180
|
+
"tone-1-mark",
|
|
181
|
+
"tone-2-mark",
|
|
182
|
+
"tone-3-mark"
|
|
178
183
|
],
|
|
179
184
|
motion: [
|
|
180
185
|
{
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// src/components/search-input/search-input.meta.ts
|
|
2
2
|
var searchInputMeta = {
|
|
3
3
|
name: "SearchInput",
|
|
4
|
-
summary: "Styled search field with a leading magnifier and a clearable X. Soft-square (rounded-sm),
|
|
4
|
+
summary: "Styled search field with a leading magnifier and a clearable X. Soft-square (rounded-sm), the theme's field surface, 2px focus ring; the clear button appears only when there is a value.",
|
|
5
5
|
status: "stable",
|
|
6
6
|
keywords: [
|
|
7
7
|
"search",
|
|
@@ -24,6 +24,10 @@ var searchInputMeta = {
|
|
|
24
24
|
guidance: "do",
|
|
25
25
|
description: "When a button stands in for search (a header trigger that opens CommandMenu with \u2318K), use CommandMenuTrigger. It wears this field's shell from the same source, so the two cannot drift; a hand-styled button already did once, into a borderless chip that matched neither the field nor a button."
|
|
26
26
|
},
|
|
27
|
+
{
|
|
28
|
+
guidance: "do",
|
|
29
|
+
description: "Light the query in the results it filters with `useMatchHighlight(query)` \u2014 a callback ref for the results container, with `selector` to keep it to titles. It paints on the `highlight` pair without wrapping matches in `<mark>`, the same paint Combobox and CommandMenu use for their rows."
|
|
30
|
+
},
|
|
27
31
|
{
|
|
28
32
|
guidance: "dont",
|
|
29
33
|
description: "Do not use SearchInput as a command palette; use CommandMenu for global commands, opened from the header by CommandMenuTrigger. A SearchInput that opens a dialog on focus is a text box you cannot type in."
|
|
@@ -91,8 +95,8 @@ var searchInputMeta = {
|
|
|
91
95
|
}
|
|
92
96
|
],
|
|
93
97
|
tokensUsed: [
|
|
94
|
-
"
|
|
95
|
-
"
|
|
98
|
+
"field-border",
|
|
99
|
+
"field",
|
|
96
100
|
"foreground",
|
|
97
101
|
"muted-foreground",
|
|
98
102
|
"ring",
|
|
@@ -144,6 +148,10 @@ var searchInputMeta = {
|
|
|
144
148
|
label: "Uncontrolled, sized as a whole",
|
|
145
149
|
code: '<Field label="Search posts"><SearchInput defaultValue="roadmap" className="max-w-80" /></Field>'
|
|
146
150
|
},
|
|
151
|
+
{
|
|
152
|
+
label: "Highlighting matches in the results",
|
|
153
|
+
code: 'const [q, setQ] = useState("")\nconst results = useMatchHighlight<HTMLUListElement>(q, { selector: "[data-title]" })\nconst shown = posts.filter((post) => post.title.toLowerCase().includes(q.trim().toLowerCase()))\n\n<SearchInput value={q} onValueChange={setQ} placeholder="Search posts" />\n<ul ref={results}>\n {shown.map((post) => (\n <li key={post.id}><span data-title>{post.title}</span></li>\n ))}\n</ul>'
|
|
154
|
+
},
|
|
147
155
|
{
|
|
148
156
|
label: "Disabled",
|
|
149
157
|
code: '<SearchInput disabled placeholder="Search" />'
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
// src/components/section-rail/section-rail.tsx
|
|
4
|
+
import {
|
|
5
|
+
useCallback,
|
|
6
|
+
useEffect,
|
|
7
|
+
useRef,
|
|
8
|
+
useState
|
|
9
|
+
} from "react";
|
|
10
|
+
import { cn } from "../../lib/cn.js";
|
|
11
|
+
import { HighlightGlide } from "../../lib/highlight-glide.js";
|
|
12
|
+
import { setRef } from "../../lib/refs.js";
|
|
13
|
+
import { useActiveSection } from "../../lib/use-active-section.js";
|
|
14
|
+
import { useMotionLevel } from "../../motion/MotionProvider.js";
|
|
15
|
+
import { SimpleTooltip, TooltipGroup } from "../tooltip/index.js";
|
|
16
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
17
|
+
var SCROLL_KEYS = /* @__PURE__ */ new Set(["ArrowUp", "ArrowDown", "PageUp", "PageDown", "Home", "End", " "]);
|
|
18
|
+
var GLIDE_HANDOFF = "[&[data-glide]_[data-active]>[data-section-rail-mark]]:opacity-0";
|
|
19
|
+
var GLIDE_MARK = "flex items-center justify-center motion-glide after:h-5 after:w-1 after:rounded-full after:bg-primary";
|
|
20
|
+
function SectionRail({
|
|
21
|
+
sections,
|
|
22
|
+
value,
|
|
23
|
+
onValueChange,
|
|
24
|
+
offset,
|
|
25
|
+
root,
|
|
26
|
+
side = "left",
|
|
27
|
+
motion,
|
|
28
|
+
className,
|
|
29
|
+
"aria-label": ariaLabel = "Sections",
|
|
30
|
+
ref,
|
|
31
|
+
...props
|
|
32
|
+
}) {
|
|
33
|
+
const rail = useRef(null);
|
|
34
|
+
const setRail = useCallback(
|
|
35
|
+
(node) => {
|
|
36
|
+
rail.current = node;
|
|
37
|
+
setRef(ref, node);
|
|
38
|
+
},
|
|
39
|
+
[ref]
|
|
40
|
+
);
|
|
41
|
+
const { enabled, attribute } = useMotionLevel(motion);
|
|
42
|
+
const spied = useActiveSection(
|
|
43
|
+
sections.map((section) => section.id),
|
|
44
|
+
{ offset, root }
|
|
45
|
+
);
|
|
46
|
+
const [picked, setPicked] = useState(null);
|
|
47
|
+
const internal = picked ?? spied;
|
|
48
|
+
const active = value ?? internal;
|
|
49
|
+
const report = useRef(onValueChange);
|
|
50
|
+
report.current = onValueChange;
|
|
51
|
+
const reported = useRef(internal);
|
|
52
|
+
useEffect(() => {
|
|
53
|
+
if (internal === null || internal === reported.current) return;
|
|
54
|
+
reported.current = internal;
|
|
55
|
+
report.current?.(internal);
|
|
56
|
+
}, [internal]);
|
|
57
|
+
useEffect(() => {
|
|
58
|
+
if (picked === null) return;
|
|
59
|
+
const release = () => setPicked(null);
|
|
60
|
+
const releaseOnKey = (event) => {
|
|
61
|
+
if (SCROLL_KEYS.has(event.key)) release();
|
|
62
|
+
};
|
|
63
|
+
const listeners = new AbortController();
|
|
64
|
+
const options = { passive: true, signal: listeners.signal };
|
|
65
|
+
window.addEventListener("wheel", release, options);
|
|
66
|
+
window.addEventListener("touchstart", release, options);
|
|
67
|
+
window.addEventListener("keydown", releaseOnKey, options);
|
|
68
|
+
window.addEventListener(
|
|
69
|
+
"pointerdown",
|
|
70
|
+
(event) => {
|
|
71
|
+
if (!(event.target instanceof Node && rail.current?.contains(event.target))) release();
|
|
72
|
+
},
|
|
73
|
+
options
|
|
74
|
+
);
|
|
75
|
+
return () => listeners.abort();
|
|
76
|
+
}, [picked]);
|
|
77
|
+
const jump = (event, id) => {
|
|
78
|
+
if (event.defaultPrevented || event.button !== 0) return;
|
|
79
|
+
if (event.metaKey || event.ctrlKey || event.shiftKey || event.altKey) return;
|
|
80
|
+
const section = document.getElementById(id);
|
|
81
|
+
if (!section) return;
|
|
82
|
+
event.preventDefault();
|
|
83
|
+
setPicked(id);
|
|
84
|
+
const behavior = enabled ? "auto" : "instant";
|
|
85
|
+
const scroller = root?.current;
|
|
86
|
+
if (scroller) {
|
|
87
|
+
const margin = Number.parseFloat(getComputedStyle(section).scrollMarginTop) || 0;
|
|
88
|
+
const top = section.getBoundingClientRect().top - scroller.getBoundingClientRect().top - scroller.clientTop;
|
|
89
|
+
scroller.scrollTo({ top: scroller.scrollTop + top - margin, behavior });
|
|
90
|
+
} else {
|
|
91
|
+
section.scrollIntoView({ behavior, block: "start" });
|
|
92
|
+
window.history.replaceState(window.history.state, "", `#${id}`);
|
|
93
|
+
}
|
|
94
|
+
if (event.detail === 0) {
|
|
95
|
+
if (!section.hasAttribute("tabindex")) {
|
|
96
|
+
section.setAttribute("tabindex", "-1");
|
|
97
|
+
section.addEventListener("blur", () => section.removeAttribute("tabindex"), { once: true });
|
|
98
|
+
}
|
|
99
|
+
section.focus({ preventScroll: true });
|
|
100
|
+
}
|
|
101
|
+
};
|
|
102
|
+
return /* @__PURE__ */ jsxs(
|
|
103
|
+
"nav",
|
|
104
|
+
{
|
|
105
|
+
ref: setRail,
|
|
106
|
+
"aria-label": ariaLabel,
|
|
107
|
+
"data-motion": attribute,
|
|
108
|
+
className: cn(
|
|
109
|
+
"relative flex w-fit flex-col rounded-full bg-muted py-2",
|
|
110
|
+
GLIDE_HANDOFF,
|
|
111
|
+
className
|
|
112
|
+
),
|
|
113
|
+
...props,
|
|
114
|
+
children: [
|
|
115
|
+
/* @__PURE__ */ jsx(HighlightGlide, { follow: "data-active", timing: "selection", className: GLIDE_MARK }),
|
|
116
|
+
/* @__PURE__ */ jsx(TooltipGroup, { motion, children: sections.map((section) => {
|
|
117
|
+
const current = section.id === active;
|
|
118
|
+
return /* @__PURE__ */ jsx(
|
|
119
|
+
SimpleTooltip,
|
|
120
|
+
{
|
|
121
|
+
content: section.label,
|
|
122
|
+
side: side === "left" ? "right" : "left",
|
|
123
|
+
children: /* @__PURE__ */ jsx(
|
|
124
|
+
"a",
|
|
125
|
+
{
|
|
126
|
+
href: `#${section.id}`,
|
|
127
|
+
"aria-label": section.label,
|
|
128
|
+
"aria-current": current ? "location" : void 0,
|
|
129
|
+
"data-active": current ? "" : void 0,
|
|
130
|
+
onClick: (event) => jump(event, section.id),
|
|
131
|
+
className: "group/rail-link relative flex h-(--control-sm) w-11 items-center justify-center rounded-full text-muted-foreground outline-none motion-interactive hover:text-foreground focus-visible:ring-2 focus-visible:ring-ring",
|
|
132
|
+
children: /* @__PURE__ */ jsx(
|
|
133
|
+
"span",
|
|
134
|
+
{
|
|
135
|
+
"aria-hidden": true,
|
|
136
|
+
"data-section-rail-mark": "",
|
|
137
|
+
className: "size-1 rounded-full bg-current motion-interactive group-hover/rail-link:scale-150 group-focus-visible/rail-link:scale-150 group-data-[active]/rail-link:h-5 group-data-[active]/rail-link:bg-primary"
|
|
138
|
+
}
|
|
139
|
+
)
|
|
140
|
+
}
|
|
141
|
+
)
|
|
142
|
+
},
|
|
143
|
+
section.id
|
|
144
|
+
);
|
|
145
|
+
}) })
|
|
146
|
+
]
|
|
147
|
+
}
|
|
148
|
+
);
|
|
149
|
+
}
|
|
150
|
+
SectionRail.displayName = "SectionRail";
|
|
151
|
+
export {
|
|
152
|
+
SectionRail
|
|
153
|
+
};
|