@sableui/react 0.13.2 → 0.14.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/AGENTS.md +2 -2
- package/CHANGELOG.md +114 -0
- package/CONTRIBUTING.md +6 -4
- package/DESIGN.md +8 -3
- package/dist/blocks/approval-block.js +93 -0
- package/dist/blocks/approval-block.meta.js +23 -0
- package/dist/blocks/index.d.ts +16 -1
- package/dist/blocks/index.js +4 -0
- package/dist/components/accordion/accordion.meta.js +8 -2
- package/dist/components/area-chart/area-chart.meta.js +10 -3
- package/dist/components/attachment/attachment.meta.js +26 -0
- package/dist/components/bar-chart/bar-chart.meta.js +11 -4
- package/dist/components/bottom-nav/bottom-nav.js +3 -1
- package/dist/components/bottom-nav/bottom-nav.meta.js +32 -0
- package/dist/components/breadcrumbs/breadcrumbs.meta.js +26 -0
- package/dist/components/button/button.js +21 -4
- package/dist/components/button/button.meta.js +7 -7
- package/dist/components/calendar/calendar.js +18 -1
- package/dist/components/calendar/calendar.meta.js +38 -0
- package/dist/components/carousel/carousel.js +6 -4
- package/dist/components/carousel/carousel.meta.js +42 -0
- package/dist/components/chat-composer/chat-composer.js +21 -21
- package/dist/components/chat-composer/chat-composer.meta.js +38 -0
- package/dist/components/chat-composer/use-eased-growth.js +70 -0
- package/dist/components/checkbox/checkbox-mark.js +8 -1
- package/dist/components/checkbox/checkbox.js +3 -1
- package/dist/components/checkbox/checkbox.meta.js +32 -0
- package/dist/components/checkbox-card/checkbox-card.js +3 -0
- package/dist/components/checkbox-card/checkbox-card.meta.js +32 -0
- package/dist/components/code-block/code-block.js +14 -3
- package/dist/components/code-block/code-block.meta.js +28 -1
- package/dist/components/collapsible/collapsible.meta.js +32 -0
- package/dist/components/color-field/color-field.meta.js +26 -0
- package/dist/components/combobox/combobox.meta.js +44 -0
- package/dist/components/command-menu/command-menu.js +8 -3
- package/dist/components/command-menu/command-menu.meta.js +38 -0
- package/dist/components/confirm-button/confirm-button.js +179 -0
- package/dist/components/confirm-button/confirm-button.meta.js +143 -0
- package/dist/components/confirm-button/index.js +7 -0
- package/dist/components/data-table/data-table.js +6 -2
- package/dist/components/data-table/data-table.meta.js +32 -0
- package/dist/components/entity/entity.meta.js +26 -0
- package/dist/components/feedback/feedback.js +113 -47
- package/dist/components/feedback/feedback.meta.js +49 -4
- package/dist/components/input-group/input-group.meta.js +27 -1
- package/dist/components/line-chart/line-chart.meta.js +10 -3
- package/dist/components/middle-truncate/middle-truncate.meta.js +1 -1
- package/dist/components/note/note.meta.js +32 -0
- package/dist/components/notification-badge/notification-badge.js +17 -5
- package/dist/components/notification-badge/notification-badge.meta.js +34 -2
- package/dist/components/number-ticker/index.js +7 -0
- package/dist/components/number-ticker/number-ticker.js +246 -0
- package/dist/components/number-ticker/number-ticker.meta.js +159 -0
- package/dist/components/otp-field/otp-field.js +23 -3
- package/dist/components/otp-field/otp-field.meta.js +23 -2
- package/dist/components/panel-group/panel-group.meta.js +26 -0
- package/dist/components/panel-section/panel-section.js +37 -10
- package/dist/components/panel-section/panel-section.meta.js +32 -0
- package/dist/components/pie-chart/pie-chart.js +20 -11
- package/dist/components/pie-chart/pie-chart.meta.js +17 -4
- package/dist/components/pill/pill.meta.js +32 -0
- package/dist/components/radar-chart/radar-chart.js +4 -3
- package/dist/components/radar-chart/radar-chart.meta.js +10 -3
- package/dist/components/radial-chart/radial-chart.js +22 -22
- package/dist/components/radial-chart/radial-chart.meta.js +17 -4
- package/dist/components/radio/radio.meta.js +26 -0
- package/dist/components/radio-card/radio-card.meta.js +38 -0
- package/dist/components/relative-time/relative-time.meta.js +32 -0
- package/dist/components/scroller/scroller.meta.js +32 -0
- package/dist/components/scrub-field/scrub-field.meta.js +32 -0
- package/dist/components/search-input/search-input.js +9 -7
- package/dist/components/search-input/search-input.meta.js +26 -0
- package/dist/components/show-more/show-more.js +1 -4
- package/dist/components/show-more/show-more.meta.js +38 -0
- package/dist/components/side-nav/side-nav.js +21 -1
- package/dist/components/side-nav/side-nav.meta.js +32 -0
- package/dist/components/side-nav/side-nav.variants.js +3 -1
- package/dist/components/sidebar/sidebar.js +31 -8
- package/dist/components/sidebar/sidebar.meta.js +38 -0
- package/dist/components/slider/slider.meta.js +26 -0
- package/dist/components/sparkline/sparkline.js +7 -4
- package/dist/components/sparkline/sparkline.meta.js +9 -2
- package/dist/components/table/table.meta.js +26 -0
- package/dist/components/tabs/tabs.js +27 -8
- package/dist/components/tabs/tabs.meta.js +46 -3
- package/dist/components/theme-selector/theme-selector.meta.js +32 -0
- package/dist/components/theme-switcher/theme-switcher.js +4 -1
- package/dist/components/theme-switcher/theme-switcher.meta.js +42 -0
- package/dist/components/toggle/toggle.js +28 -5
- package/dist/components/toggle/toggle.meta.js +44 -2
- package/dist/components/tool-call/index.js +8 -0
- package/dist/components/tool-call/tool-call.js +117 -0
- package/dist/components/tool-call/tool-call.meta.js +147 -0
- package/dist/components/tool-call/tool-call.variants.js +33 -0
- package/dist/components/tooltip/index.js +3 -1
- package/dist/components/tooltip/tooltip.js +84 -0
- package/dist/components/tooltip/tooltip.meta.js +16 -1
- package/dist/components/tree/tree.js +71 -6
- package/dist/components/tree/tree.meta.js +32 -0
- package/dist/components/video/video.meta.js +32 -0
- package/dist/file-hashes.json +114 -97
- package/dist/foundations/motion.js +49 -3
- package/dist/index.d.ts +614 -44
- package/dist/index.js +4 -1
- package/dist/lib/chart/cartesian.js +9 -10
- package/dist/lib/chart/chart-center.js +30 -0
- package/dist/lib/chart/chart-frame.js +15 -8
- package/dist/lib/chart/dim.js +13 -0
- package/dist/lib/chart/format.js +7 -1
- package/dist/lib/chart/motion.js +6 -4
- package/dist/lib/highlight-glide.js +68 -10
- package/dist/lib/taxonomy.js +3 -0
- package/dist/motion/MotionProvider.js +45 -3
- package/dist/motion/Reveal.js +1 -1
- package/dist/motion/TextReveal.js +1 -1
- package/dist/motion/index.js +15 -2
- package/dist/motion/useAutoSize.js +42 -0
- package/dist/motion/useShake.js +25 -0
- package/dist/styles.css +298 -5
- package/llms/blocks/approval-block.md +128 -0
- package/llms/components/accordion.md +3 -2
- package/llms/components/area-chart.md +4 -3
- package/llms/components/attachment.md +7 -0
- package/llms/components/bar-chart.md +5 -4
- package/llms/components/bottom-nav.md +8 -0
- package/llms/components/breadcrumbs.md +7 -0
- package/llms/components/button.md +6 -6
- package/llms/components/calendar.md +9 -0
- package/llms/components/carousel.md +17 -0
- package/llms/components/chat-composer.md +9 -0
- package/llms/components/checkbox-card.md +8 -0
- package/llms/components/checkbox.md +8 -0
- package/llms/components/code-block.md +8 -0
- package/llms/components/collapsible.md +8 -0
- package/llms/components/color-field.md +7 -0
- package/llms/components/combobox.md +10 -0
- package/llms/components/command-menu.md +9 -0
- package/llms/components/confirm-button.md +92 -0
- package/llms/components/data-table.md +8 -0
- package/llms/components/entity.md +7 -0
- package/llms/components/feedback.md +22 -3
- package/llms/components/input-group.md +8 -1
- package/llms/components/line-chart.md +4 -3
- package/llms/components/middle-truncate.md +1 -1
- package/llms/components/note.md +8 -0
- package/llms/components/notification-badge.md +10 -2
- package/llms/components/number-ticker.md +86 -0
- package/llms/components/otp-field.md +15 -1
- package/llms/components/panel-group.md +7 -0
- package/llms/components/panel-section.md +8 -0
- package/llms/components/pie-chart.md +6 -4
- package/llms/components/pill.md +8 -0
- package/llms/components/radar-chart.md +4 -3
- package/llms/components/radial-chart.md +6 -4
- package/llms/components/radio-card.md +9 -0
- package/llms/components/radio.md +7 -0
- package/llms/components/relative-time.md +8 -0
- package/llms/components/scroller.md +8 -0
- package/llms/components/scrub-field.md +8 -0
- package/llms/components/search-input.md +7 -0
- package/llms/components/show-more.md +9 -0
- package/llms/components/side-nav.md +8 -0
- package/llms/components/sidebar.md +9 -0
- package/llms/components/slider.md +7 -0
- package/llms/components/sparkline.md +3 -2
- package/llms/components/table.md +7 -0
- package/llms/components/tabs.md +20 -2
- package/llms/components/theme-selector.md +8 -0
- package/llms/components/theme-switcher.md +17 -0
- package/llms/components/toggle.md +19 -2
- package/llms/components/tool-call.md +115 -0
- package/llms/components/tooltip.md +11 -1
- package/llms/components/tree.md +8 -0
- package/llms/components/video.md +8 -0
- package/llms/foundations/motion.md +48 -3
- package/llms/reference/utilities.md +23 -1
- package/llms/tasks/reviewing-motion.md +14 -0
- package/llms-full.txt +955 -49
- package/llms.txt +15 -10
- package/package.json +1 -1
- package/registry.json +2565 -210
- package/targets/ios.json +27 -0
|
@@ -122,6 +122,44 @@ var radioCardMeta = {
|
|
|
122
122
|
"tone-info",
|
|
123
123
|
"tone-warning"
|
|
124
124
|
],
|
|
125
|
+
motion: [
|
|
126
|
+
{
|
|
127
|
+
slot: "arrival",
|
|
128
|
+
recipe: "none",
|
|
129
|
+
part: "Root",
|
|
130
|
+
why: "Cards arrive with the form. A plan picker that animated each card in would delay the choice it exists to make."
|
|
131
|
+
},
|
|
132
|
+
{
|
|
133
|
+
slot: "response",
|
|
134
|
+
recipe: "motion-interactive",
|
|
135
|
+
part: "Root",
|
|
136
|
+
why: "The edge steps toward the ring colour on hover, eased. On a card-sized target a hard snap across the whole border reads as the layout flickering."
|
|
137
|
+
},
|
|
138
|
+
{
|
|
139
|
+
slot: "response",
|
|
140
|
+
recipe: "motion-press",
|
|
141
|
+
part: "Root",
|
|
142
|
+
why: "The settle on press confirms a click anywhere in the card \u2014 the radio is small, and without it a click in the card's body can feel like it missed the control."
|
|
143
|
+
},
|
|
144
|
+
{
|
|
145
|
+
slot: "transition",
|
|
146
|
+
recipe: "motion-interactive",
|
|
147
|
+
part: "Root",
|
|
148
|
+
why: "Checking eases the tone ring and wash onto the new card as the old one releases, so the choice reads as moving between cards rather than two cards repainting."
|
|
149
|
+
},
|
|
150
|
+
{
|
|
151
|
+
slot: "transition",
|
|
152
|
+
recipe: "motion-interactive",
|
|
153
|
+
part: "Indicator",
|
|
154
|
+
why: "The radio dot grows in on a spring, the same mark as Radio, so a card and a plain radio confirm a choice the same way."
|
|
155
|
+
},
|
|
156
|
+
{
|
|
157
|
+
slot: "activity",
|
|
158
|
+
recipe: "none",
|
|
159
|
+
part: "Root",
|
|
160
|
+
why: "A choice commits instantly. A slow write belongs to the surrounding form's pending state."
|
|
161
|
+
}
|
|
162
|
+
],
|
|
125
163
|
a11y: [
|
|
126
164
|
"Base UI provides the radiogroup/radio roles, aria-checked, hidden native inputs, and roving tabindex.",
|
|
127
165
|
"Arrow keys move and select between cards; Space selects the focused card; Tab enters/leaves the group.",
|
|
@@ -54,6 +54,38 @@ var relativeTimeMeta = {
|
|
|
54
54
|
}
|
|
55
55
|
],
|
|
56
56
|
tokensUsed: ["popover", "popover-foreground", "border", "ring"],
|
|
57
|
+
motion: [
|
|
58
|
+
{
|
|
59
|
+
slot: "arrival",
|
|
60
|
+
recipe: "none",
|
|
61
|
+
part: "Root",
|
|
62
|
+
why: "It is a word in a line of text and arrives with that line."
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
slot: "response",
|
|
66
|
+
recipe: "none",
|
|
67
|
+
part: "Root",
|
|
68
|
+
why: "The only hover change on the timestamp is a dotted underline, and text-decoration-line is discrete: CSS cannot tween it, so a transition here would animate nothing and imply the snap had been handled."
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
slot: "response",
|
|
72
|
+
recipe: "motion-pop",
|
|
73
|
+
part: "Tooltip",
|
|
74
|
+
why: "The pointer's real answer is the absolute time, which arrives in a Tooltip on its own fast pop. That short entrance is what makes the full date read as belonging to the word under the cursor."
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
slot: "transition",
|
|
78
|
+
recipe: "none",
|
|
79
|
+
part: "Root",
|
|
80
|
+
why: "The label does not tick: it is computed when the component renders, so it never changes in front of the reader. A timestamp rolling from one minute to two would pull the eye off the content it dates."
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
slot: "activity",
|
|
84
|
+
recipe: "none",
|
|
85
|
+
part: "Root",
|
|
86
|
+
why: "A timestamp has nothing to wait for."
|
|
87
|
+
}
|
|
88
|
+
],
|
|
57
89
|
a11y: [
|
|
58
90
|
"Renders a semantic <time> element with a machine-readable dateTime (ISO) attribute.",
|
|
59
91
|
"The <time> carries tabIndex=0 (it is not focusable natively), so the absolute time \u2014 exposed via Base UI's Tooltip, which opens on keyboard focus as well as hover and wires aria-describedby \u2014 is reachable from the keyboard.",
|
|
@@ -92,6 +92,38 @@ var scrollerMeta = {
|
|
|
92
92
|
wheelScroll: ["true", "false"]
|
|
93
93
|
},
|
|
94
94
|
tokensUsed: ["ring"],
|
|
95
|
+
motion: [
|
|
96
|
+
{
|
|
97
|
+
slot: "arrival",
|
|
98
|
+
recipe: "none",
|
|
99
|
+
part: "Root",
|
|
100
|
+
why: "It is a region, not an event; its content arrives with whatever fills it."
|
|
101
|
+
},
|
|
102
|
+
{
|
|
103
|
+
slot: "response",
|
|
104
|
+
recipe: "motion-interactive",
|
|
105
|
+
part: "Viewport",
|
|
106
|
+
why: "The region is focusable so the keyboard can scroll it, and its focus ring eases in like any control's. It is the one sign the region, not the page, now takes the arrow keys."
|
|
107
|
+
},
|
|
108
|
+
{
|
|
109
|
+
slot: "response",
|
|
110
|
+
recipe: "custom",
|
|
111
|
+
part: "Scrollbar",
|
|
112
|
+
why: "The vertical bar (`scrollbar-quiet`) stays hidden until the region is hovered, focused or scrolled, then fades its thumb in on the motion tokens. A bar that appeared in one frame would flash every time the pointer crossed the list."
|
|
113
|
+
},
|
|
114
|
+
{
|
|
115
|
+
slot: "transition",
|
|
116
|
+
recipe: "none",
|
|
117
|
+
part: "Edge fade",
|
|
118
|
+
why: "The edge fades switch on and off as you scroll, in the frame the content is already moving, so the change is carried by the scroll itself. They are a mask gradient, which CSS cannot interpolate without registered custom properties."
|
|
119
|
+
},
|
|
120
|
+
{
|
|
121
|
+
slot: "activity",
|
|
122
|
+
recipe: "none",
|
|
123
|
+
part: "Root",
|
|
124
|
+
why: "A scroll region does no work of its own. Content still loading inside it brings its own Skeleton or Spinner."
|
|
125
|
+
}
|
|
126
|
+
],
|
|
95
127
|
a11y: [
|
|
96
128
|
"The edge fades are a CSS mask-image on the scrolling element, not overlay elements \u2014 there is nothing in the DOM to intercept a pointer or be read by assistive tech, and the content under a fade stays fully interactive.",
|
|
97
129
|
'Two boxes: a chrome box carrying `className` (border, background, radius, sizing) and, inside it, the element that actually scrolls and carries the fade mask. They are split because a `mask-image` applies to its element\'s own border \u2014 so with both on one box, a bordered Scroller watched its frame dissolve at the scrolling edge. The axis is published as `data-orientation`, not `aria-orientation`, which is invalid on `role="group"`.',
|
|
@@ -99,6 +99,38 @@ var scrubFieldMeta = {
|
|
|
99
99
|
{ name: "disabled", type: "boolean", description: "Disables typing and scrubbing." },
|
|
100
100
|
{ name: "id", type: "string", description: "Input id, for a PropertyRow's htmlFor." }
|
|
101
101
|
],
|
|
102
|
+
motion: [
|
|
103
|
+
{
|
|
104
|
+
slot: "arrival",
|
|
105
|
+
recipe: "none",
|
|
106
|
+
part: "Root",
|
|
107
|
+
why: "It arrives with the inspector row it sits in."
|
|
108
|
+
},
|
|
109
|
+
{
|
|
110
|
+
slot: "response",
|
|
111
|
+
recipe: "motion-interactive",
|
|
112
|
+
part: "Root",
|
|
113
|
+
why: "The shell eases its focus ring like every field, so tabbing through a column of numbers shows where the keyboard is without flicker."
|
|
114
|
+
},
|
|
115
|
+
{
|
|
116
|
+
slot: "response",
|
|
117
|
+
recipe: "motion-interactive",
|
|
118
|
+
part: "Handle",
|
|
119
|
+
why: "The handle's ink brightens while the field is hovered. It is the only sign that the icon or prefix can be dragged, which nobody would guess from a label."
|
|
120
|
+
},
|
|
121
|
+
{
|
|
122
|
+
slot: "transition",
|
|
123
|
+
recipe: "none",
|
|
124
|
+
part: "Value",
|
|
125
|
+
why: "The number follows the drag pixel for pixel. Easing it \u2014 or rolling it with a NumberTicker \u2014 would show a value the pointer has already left, in a control whose whole point is precision."
|
|
126
|
+
},
|
|
127
|
+
{
|
|
128
|
+
slot: "activity",
|
|
129
|
+
recipe: "none",
|
|
130
|
+
part: "Root",
|
|
131
|
+
why: "Scrubbing is synchronous; there is nothing to wait for."
|
|
132
|
+
}
|
|
133
|
+
],
|
|
102
134
|
a11y: [
|
|
103
135
|
"The input carries the accessible name via `label`; the glyph is aria-hidden because it is decorative.",
|
|
104
136
|
"Every scrub gesture is reachable from the keyboard: ArrowUp/ArrowDown step by `step`, Shift+Arrow by ten steps.",
|
|
@@ -66,21 +66,23 @@ function SearchInput({
|
|
|
66
66
|
}
|
|
67
67
|
),
|
|
68
68
|
hasValue && !props.disabled ? (
|
|
69
|
-
//
|
|
70
|
-
//
|
|
71
|
-
//
|
|
72
|
-
//
|
|
73
|
-
|
|
69
|
+
// The wrapper carries the arrival, the button keeps its own response.
|
|
70
|
+
// motion-appear declares a transition list of its own, and on the
|
|
71
|
+
// Button it would fight motion-interactive for the one
|
|
72
|
+
// transition-property slot — whichever sorted last would win, and
|
|
73
|
+
// either the hover ease or the arrival would silently go. Removal
|
|
74
|
+
// stays instant: an emptied field should not wait on its X.
|
|
75
|
+
/* @__PURE__ */ jsx("span", { className: "absolute end-2 flex motion-appear", children: /* @__PURE__ */ jsx(
|
|
74
76
|
Button,
|
|
75
77
|
{
|
|
76
78
|
variant: "ghost",
|
|
77
79
|
size: "icon-xs",
|
|
78
80
|
onClick: handleClear,
|
|
79
81
|
"aria-label": "Clear search",
|
|
80
|
-
className: "
|
|
82
|
+
className: "text-muted-foreground hover:text-foreground",
|
|
81
83
|
children: /* @__PURE__ */ jsx(Icon, { name: "xmark" })
|
|
82
84
|
}
|
|
83
|
-
)
|
|
85
|
+
) })
|
|
84
86
|
) : null
|
|
85
87
|
] })
|
|
86
88
|
);
|
|
@@ -98,6 +98,32 @@ var searchInputMeta = {
|
|
|
98
98
|
"ring",
|
|
99
99
|
"destructive"
|
|
100
100
|
],
|
|
101
|
+
motion: [
|
|
102
|
+
{
|
|
103
|
+
slot: "arrival",
|
|
104
|
+
recipe: "motion-appear",
|
|
105
|
+
part: "Clear button",
|
|
106
|
+
why: "The X mounts as the first character lands. Fading and settling in from a smaller scale keeps it from popping into the corner of the eye on every new search; removal is instant so an emptied field does not wait on it."
|
|
107
|
+
},
|
|
108
|
+
{
|
|
109
|
+
slot: "response",
|
|
110
|
+
recipe: "motion-interactive",
|
|
111
|
+
part: "Input",
|
|
112
|
+
why: "The focus ring and invalid border ease in, so focus reads as the field answering rather than a repaint."
|
|
113
|
+
},
|
|
114
|
+
{
|
|
115
|
+
slot: "transition",
|
|
116
|
+
recipe: "none",
|
|
117
|
+
part: "Root",
|
|
118
|
+
why: "The field has no states of its own to move between \u2014 the clear button arriving is its only change, and arrival covers it."
|
|
119
|
+
},
|
|
120
|
+
{
|
|
121
|
+
slot: "activity",
|
|
122
|
+
recipe: "none",
|
|
123
|
+
part: "Root",
|
|
124
|
+
why: "Searching is the results' job: a list skeleton or a Spinner beside them, not a field that animates while you type."
|
|
125
|
+
}
|
|
126
|
+
],
|
|
101
127
|
a11y: [
|
|
102
128
|
'Renders a native <input type="search"> \u2014 keyboard and form semantics come for free.',
|
|
103
129
|
"Associate a <label htmlFor> (or wrap) \u2014 the magnifier glyph is decorative, not a label.",
|
|
@@ -70,10 +70,7 @@ function ShowMore({
|
|
|
70
70
|
Icon,
|
|
71
71
|
{
|
|
72
72
|
name: "chevron-down",
|
|
73
|
-
className: cn(
|
|
74
|
-
"transition-transform duration-(--motion-duration-fast) ease-standard",
|
|
75
|
-
expanded && "rotate-180"
|
|
76
|
-
)
|
|
73
|
+
className: cn("motion-interactive", expanded && "rotate-180")
|
|
77
74
|
}
|
|
78
75
|
),
|
|
79
76
|
children: expanded ? "Show less" : "Show more"
|
|
@@ -58,6 +58,44 @@ var showMoreMeta = {
|
|
|
58
58
|
}
|
|
59
59
|
],
|
|
60
60
|
tokensUsed: ["background", "foreground", "ring"],
|
|
61
|
+
motion: [
|
|
62
|
+
{
|
|
63
|
+
slot: "arrival",
|
|
64
|
+
recipe: "none",
|
|
65
|
+
part: "Root",
|
|
66
|
+
why: "The preview arrives with the content it clips. Its fade and toggle appear only once a measurement proves the content overflows."
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
slot: "response",
|
|
70
|
+
recipe: "motion-interactive",
|
|
71
|
+
part: "Toggle",
|
|
72
|
+
why: "The toggle is a link Button, so its hover and focus ease on Button's recipe."
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
slot: "transition",
|
|
76
|
+
recipe: "custom",
|
|
77
|
+
part: "Content",
|
|
78
|
+
why: "The region eases its max-height between the collapsed and the measured full height on the slow motion token. motion-disclosure keys on Base UI's starting and ending styles, which this self-contained toggle does not have. The text unrolling in place keeps the reader's line; a snap drops them at a different scroll position."
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
slot: "transition",
|
|
82
|
+
recipe: "motion-interactive",
|
|
83
|
+
part: "Fade",
|
|
84
|
+
why: "The bottom fade dissolves as the region opens and returns as it closes, so the cut edge and the reveal read as one movement."
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
slot: "transition",
|
|
88
|
+
recipe: "motion-interactive",
|
|
89
|
+
part: "Chevron",
|
|
90
|
+
why: "The chevron turns 180\xB0 in step with the region (motion-interactive lists `rotate`), so the toggle visibly points at what it will do next."
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
slot: "activity",
|
|
94
|
+
recipe: "none",
|
|
95
|
+
part: "Root",
|
|
96
|
+
why: "Expanding is local and instant; nothing loads."
|
|
97
|
+
}
|
|
98
|
+
],
|
|
61
99
|
a11y: [
|
|
62
100
|
"The toggle is a real <button> with aria-expanded reflecting the open state.",
|
|
63
101
|
"The fade overlay is pointer-events-none and decorative; it never traps clicks.",
|
|
@@ -4,19 +4,39 @@
|
|
|
4
4
|
import { useRender } from "@base-ui/react/use-render";
|
|
5
5
|
import { createContext, useContext } from "react";
|
|
6
6
|
import { cn } from "../../lib/cn.js";
|
|
7
|
+
import { HighlightGlide } from "../../lib/highlight-glide.js";
|
|
8
|
+
import { motionAttribute } from "../../motion/MotionProvider.js";
|
|
7
9
|
import { sideNavItemIconVariants, sideNavItemVariants } from "./side-nav.variants.js";
|
|
8
10
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
9
11
|
var SideNavContext = createContext({
|
|
10
12
|
size: "md",
|
|
11
13
|
activeVariant: "content"
|
|
12
14
|
});
|
|
15
|
+
var GLIDE_HANDOFF = "[&[data-glide]_[data-active]]:bg-transparent";
|
|
16
|
+
var GLIDE_FILL = {
|
|
17
|
+
content: "rounded-sm bg-accent motion-glide",
|
|
18
|
+
sidebar: "rounded-sm bg-sidebar-accent motion-glide"
|
|
19
|
+
};
|
|
13
20
|
function SideNav({
|
|
14
21
|
className,
|
|
15
22
|
size = "md",
|
|
16
23
|
activeVariant = "content",
|
|
24
|
+
motion,
|
|
25
|
+
children,
|
|
17
26
|
...props
|
|
18
27
|
}) {
|
|
19
|
-
return /* @__PURE__ */ jsx(SideNavContext.Provider, { value: { size, activeVariant }, children: /* @__PURE__ */
|
|
28
|
+
return /* @__PURE__ */ jsx(SideNavContext.Provider, { value: { size, activeVariant }, children: /* @__PURE__ */ jsxs(
|
|
29
|
+
"nav",
|
|
30
|
+
{
|
|
31
|
+
"data-motion": motionAttribute(motion),
|
|
32
|
+
className: cn("flex flex-col gap-0.5", GLIDE_HANDOFF, className),
|
|
33
|
+
...props,
|
|
34
|
+
children: [
|
|
35
|
+
/* @__PURE__ */ jsx(HighlightGlide, { follow: "data-active", timing: "selection", className: GLIDE_FILL[activeVariant] }),
|
|
36
|
+
children
|
|
37
|
+
]
|
|
38
|
+
}
|
|
39
|
+
) });
|
|
20
40
|
}
|
|
21
41
|
SideNav.displayName = "SideNav";
|
|
22
42
|
function SideNavGroup({
|
|
@@ -80,6 +80,12 @@ var sideNavMeta = {
|
|
|
80
80
|
type: '"content" | "sidebar"',
|
|
81
81
|
default: '"content"',
|
|
82
82
|
description: 'Active-state token vocabulary shared by all items. "content" uses bg-accent/text-accent-foreground for local rails inside the content area; "sidebar" uses bg-sidebar-accent/text-sidebar-accent-foreground for SideNavs rendered inside app chrome.'
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
name: "motion",
|
|
86
|
+
type: '"full" | "subtle" | "off"',
|
|
87
|
+
default: '"full"',
|
|
88
|
+
description: "How much motion this component uses: `full` inherits the app's policy, `subtle` keeps feedback and drops the current-page glide, `off` stills it. It can only turn motion down \u2014 it never overrides a reader's reduce-motion setting."
|
|
83
89
|
}
|
|
84
90
|
],
|
|
85
91
|
subcomponents: [
|
|
@@ -94,6 +100,32 @@ var sideNavMeta = {
|
|
|
94
100
|
description: "A labelled section within a SideNav - groups settings into areas. label renders an optional sentence-case caption heading above the group's items."
|
|
95
101
|
}
|
|
96
102
|
],
|
|
103
|
+
motion: [
|
|
104
|
+
{
|
|
105
|
+
slot: "arrival",
|
|
106
|
+
recipe: "none",
|
|
107
|
+
part: "Root",
|
|
108
|
+
why: "In-page navigation arrives with the page it navigates."
|
|
109
|
+
},
|
|
110
|
+
{
|
|
111
|
+
slot: "response",
|
|
112
|
+
recipe: "motion-interactive",
|
|
113
|
+
part: "SideNavItem",
|
|
114
|
+
why: "Rows ease their hover fill and focus ring, so moving down a long nav reads as one pointer travelling rather than rows flashing in turn."
|
|
115
|
+
},
|
|
116
|
+
{
|
|
117
|
+
slot: "transition",
|
|
118
|
+
recipe: "motion-glide",
|
|
119
|
+
part: "SideNav",
|
|
120
|
+
why: "The current-item fill is one element that travels to the new row when the section changes inside a persistent layout, so the marker reads as moving rather than one row dimming while another lights. It lands in place on first paint, each row keeps its own fill as the server-render fallback, and the active row's heavier weight lands at once \u2014 font weight does not ease. Expressive: `subtle` moves it at once."
|
|
121
|
+
},
|
|
122
|
+
{
|
|
123
|
+
slot: "activity",
|
|
124
|
+
recipe: "none",
|
|
125
|
+
part: "Root",
|
|
126
|
+
why: "Navigation chrome has no working state; a loading section shows it in the content."
|
|
127
|
+
}
|
|
128
|
+
],
|
|
97
129
|
a11y: [
|
|
98
130
|
"SideNav renders a <nav> landmark; pair multiple navs with aria-label to distinguish them.",
|
|
99
131
|
'The active item carries aria-current="page".',
|
|
@@ -2,7 +2,9 @@
|
|
|
2
2
|
import { cva } from "class-variance-authority";
|
|
3
3
|
var RAIL_ROW_INLINE = "gap-3 px-3";
|
|
4
4
|
var sideNavItemVariants = cva(
|
|
5
|
-
|
|
5
|
+
// `relative`: a row paints above SideNav's gliding current-page fill, which is
|
|
6
|
+
// absolutely positioned and earlier in the DOM.
|
|
7
|
+
"relative flex w-full items-center text-start motion-interactive focus-visible:outline-none focus-visible:ring-2 data-[active]:font-semibold",
|
|
6
8
|
{
|
|
7
9
|
variants: {
|
|
8
10
|
size: {
|
|
@@ -4,9 +4,12 @@
|
|
|
4
4
|
import { useRender } from "@base-ui/react/use-render";
|
|
5
5
|
import {
|
|
6
6
|
createContext,
|
|
7
|
-
useContext
|
|
7
|
+
useContext,
|
|
8
|
+
useState
|
|
8
9
|
} from "react";
|
|
9
10
|
import { cn } from "../../lib/cn.js";
|
|
11
|
+
import { HighlightGlide } from "../../lib/highlight-glide.js";
|
|
12
|
+
import { motionAttribute } from "../../motion/MotionProvider.js";
|
|
10
13
|
import { RAIL_ROW_INLINE, sideNavItemIconVariants, sideNavItemVariants } from "../side-nav/side-nav.variants.js";
|
|
11
14
|
import { SimpleTooltip } from "../tooltip/index.js";
|
|
12
15
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
@@ -14,6 +17,7 @@ var SidebarContext = createContext({
|
|
|
14
17
|
collapsed: false,
|
|
15
18
|
icons: true
|
|
16
19
|
});
|
|
20
|
+
var LABEL_FADE = "starting:opacity-0 transition-opacity duration-(--motion-duration-base) ease-decelerate";
|
|
17
21
|
function useSidebar() {
|
|
18
22
|
return useContext(SidebarContext);
|
|
19
23
|
}
|
|
@@ -43,13 +47,17 @@ function Sidebar({
|
|
|
43
47
|
icons = true,
|
|
44
48
|
variant = "app",
|
|
45
49
|
surface = "rail",
|
|
50
|
+
motion,
|
|
46
51
|
className,
|
|
47
52
|
children,
|
|
48
53
|
...props
|
|
49
54
|
}) {
|
|
50
|
-
|
|
55
|
+
const [hasCollapsed, setHasCollapsed] = useState(collapsed);
|
|
56
|
+
if (collapsed && !hasCollapsed) setHasCollapsed(true);
|
|
57
|
+
return /* @__PURE__ */ jsx(SidebarContext.Provider, { value: { collapsed, icons, reopened: hasCollapsed && !collapsed }, children: /* @__PURE__ */ jsx(
|
|
51
58
|
"aside",
|
|
52
59
|
{
|
|
60
|
+
"data-motion": motionAttribute(motion),
|
|
53
61
|
"data-collapsed": collapsed || void 0,
|
|
54
62
|
"data-variant": variant,
|
|
55
63
|
"data-surface": surface,
|
|
@@ -66,6 +74,15 @@ function Sidebar({
|
|
|
66
74
|
surface === "rail" ? cn("border-r border-sidebar-border", SIDEBAR_INK) : PAGE_INK,
|
|
67
75
|
SIDEBAR_VARIANT[variant],
|
|
68
76
|
"w-[var(--sidebar-width,16rem)] data-[collapsed]:w-[var(--sidebar-width-collapsed,4rem)]",
|
|
77
|
+
// Collapsing eases the width (`motion-size`, expressive) rather than
|
|
78
|
+
// jumping 192px in one frame. It reflows the page beside the rail for
|
|
79
|
+
// the length of the ease, which is the point: the content visibly
|
|
80
|
+
// takes the room the rail gave up. Nothing inside jumps, because a
|
|
81
|
+
// collapsed row's centred icon and an expanded row's inset icon sit
|
|
82
|
+
// on the same column (measured: 21px from the rail edge in both).
|
|
83
|
+
// Labels leave at once and fade back in as the rail reopens; the
|
|
84
|
+
// clip keeps them from painting past the rail while it is narrow.
|
|
85
|
+
"overflow-x-clip motion-size",
|
|
69
86
|
className
|
|
70
87
|
),
|
|
71
88
|
...props,
|
|
@@ -94,7 +111,7 @@ function SidebarHeader({
|
|
|
94
111
|
}
|
|
95
112
|
SidebarHeader.displayName = "SidebarHeader";
|
|
96
113
|
function SidebarBrand({ mark, label, render, className, "aria-label": ariaLabel, ...props }) {
|
|
97
|
-
const { collapsed } = useSidebar();
|
|
114
|
+
const { collapsed, reopened } = useSidebar();
|
|
98
115
|
return useRender({
|
|
99
116
|
render: render ?? /* @__PURE__ */ jsx("div", {}),
|
|
100
117
|
props: {
|
|
@@ -111,7 +128,7 @@ function SidebarBrand({ mark, label, render, className, "aria-label": ariaLabel,
|
|
|
111
128
|
),
|
|
112
129
|
children: /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
113
130
|
/* @__PURE__ */ jsx("span", { className: sideNavItemIconVariants({ size: "rail" }), children: mark }),
|
|
114
|
-
collapsed ? null : /* @__PURE__ */ jsx("span", { className: "min-w-0 truncate text-title-sm", children: label })
|
|
131
|
+
collapsed ? null : /* @__PURE__ */ jsx("span", { className: cn("min-w-0 truncate text-title-sm", reopened && LABEL_FADE), children: label })
|
|
115
132
|
] })
|
|
116
133
|
},
|
|
117
134
|
defaultTagName: "div"
|
|
@@ -120,9 +137,10 @@ function SidebarBrand({ mark, label, render, className, "aria-label": ariaLabel,
|
|
|
120
137
|
SidebarBrand.displayName = "SidebarBrand";
|
|
121
138
|
function SidebarNav({
|
|
122
139
|
className,
|
|
140
|
+
children,
|
|
123
141
|
...props
|
|
124
142
|
}) {
|
|
125
|
-
return /* @__PURE__ */
|
|
143
|
+
return /* @__PURE__ */ jsxs(
|
|
126
144
|
"nav",
|
|
127
145
|
{
|
|
128
146
|
className: cn(
|
|
@@ -130,9 +148,14 @@ function SidebarNav({
|
|
|
130
148
|
// narrow rail, and contained overscroll so a wheel gesture that runs
|
|
131
149
|
// out of nav stops there instead of scrolling the page behind it.
|
|
132
150
|
"flex flex-1 flex-col gap-1 overflow-y-auto overscroll-contain px-3 py-4 scrollbar-quiet",
|
|
151
|
+
"[&[data-glide]_[data-active]]:bg-transparent",
|
|
133
152
|
className
|
|
134
153
|
),
|
|
135
|
-
...props
|
|
154
|
+
...props,
|
|
155
|
+
children: [
|
|
156
|
+
/* @__PURE__ */ jsx(HighlightGlide, { follow: "data-active", timing: "selection", className: "rounded-sm bg-sidebar-accent motion-glide" }),
|
|
157
|
+
children
|
|
158
|
+
]
|
|
136
159
|
}
|
|
137
160
|
);
|
|
138
161
|
}
|
|
@@ -163,7 +186,7 @@ function SidebarItem({
|
|
|
163
186
|
"aria-label": ariaLabel,
|
|
164
187
|
...props
|
|
165
188
|
}) {
|
|
166
|
-
const { collapsed, icons } = useSidebar();
|
|
189
|
+
const { collapsed, icons, reopened } = useSidebar();
|
|
167
190
|
const showIcon = icon != null && (icons || collapsed);
|
|
168
191
|
const element = useRender({
|
|
169
192
|
render: render ?? /* @__PURE__ */ jsx("button", { type: "button" }),
|
|
@@ -183,7 +206,7 @@ function SidebarItem({
|
|
|
183
206
|
children: /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
184
207
|
showIcon ? /* @__PURE__ */ jsx("span", { className: sideNavItemIconVariants({ size: "rail" }), children: icon }) : null,
|
|
185
208
|
collapsed ? null : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
186
|
-
/* @__PURE__ */ jsx("span", { className: "min-w-0 flex-1 truncate", children: label }),
|
|
209
|
+
/* @__PURE__ */ jsx("span", { className: cn("min-w-0 flex-1 truncate", reopened && LABEL_FADE), children: label }),
|
|
187
210
|
badge ? /* @__PURE__ */ jsx("span", { className: "ms-auto shrink-0", children: badge }) : null
|
|
188
211
|
] })
|
|
189
212
|
] })
|
|
@@ -77,6 +77,12 @@ var sidebarMeta = {
|
|
|
77
77
|
type: '"rail" | "page"',
|
|
78
78
|
default: '"rail"',
|
|
79
79
|
description: `Which surface the rail is painted on. "rail" is its own --sidebar-* surface with a border \u2014 right for a full-height rail that frames the app, which a theme may tint or paint dark. "page" uses the page's surface and ink with no border \u2014 use it when a full-width header sits above the rail, where a tinted column starting under a page-coloured bar reads as pasted on. Use it instead of overriding bg-/text- classes, which the rail's re-pointed tokens defeat.`
|
|
80
|
+
},
|
|
81
|
+
{
|
|
82
|
+
name: "motion",
|
|
83
|
+
type: '"full" | "subtle" | "off"',
|
|
84
|
+
default: '"full"',
|
|
85
|
+
description: "How much motion this component uses: `full` inherits the app's policy, `subtle` keeps feedback and drops the collapse ease and the current-page glide, `off` stills it. It can only turn motion down \u2014 it never overrides a reader's reduce-motion setting."
|
|
80
86
|
}
|
|
81
87
|
],
|
|
82
88
|
subcomponents: [
|
|
@@ -96,6 +102,38 @@ var sidebarMeta = {
|
|
|
96
102
|
description: 'A single nav row. label is the row text (inline when expanded, in a hover tooltip when collapsed); icon is the leading icon node (the consumer swaps fill weight for the active state); active marks the current route (accent fill + aria-current="page"); badge is a trailing node (e.g. a count) pushed to the row end and hidden when collapsed; render composes another element, e.g. render={<Link to="/x" />}.'
|
|
97
103
|
}
|
|
98
104
|
],
|
|
105
|
+
motion: [
|
|
106
|
+
{
|
|
107
|
+
slot: "arrival",
|
|
108
|
+
recipe: "none",
|
|
109
|
+
part: "Root",
|
|
110
|
+
why: "The rail is the app's frame, present from first paint. It is not summoned, so it has no entrance."
|
|
111
|
+
},
|
|
112
|
+
{
|
|
113
|
+
slot: "response",
|
|
114
|
+
recipe: "motion-interactive",
|
|
115
|
+
part: "SidebarItem",
|
|
116
|
+
why: "Rows ease their hover fill (SideNav's row recipe on the rail rung), so moving down the nav reads as the pointer travelling rather than rows flashing."
|
|
117
|
+
},
|
|
118
|
+
{
|
|
119
|
+
slot: "transition",
|
|
120
|
+
recipe: "motion-glide",
|
|
121
|
+
part: "SidebarNav",
|
|
122
|
+
why: "The current-page fill is one element that travels to the new item when the page changes inside a persistent layout, so the marker reads as moving rather than one row dimming while another lights. It lands in place on first paint, and each item keeps its own fill as the server-render fallback. Expressive: `subtle` moves it at once."
|
|
123
|
+
},
|
|
124
|
+
{
|
|
125
|
+
slot: "transition",
|
|
126
|
+
recipe: "motion-size",
|
|
127
|
+
part: "Root",
|
|
128
|
+
why: "Collapsing to the icon rail eases the width instead of jumping 192px in one frame, so the content beside it visibly takes the room the rail gave up. Nothing inside jumps \u2014 a collapsed row's centred icon and an expanded row's inset icon sit on the same column \u2014 and labels leave at once and fade back in on reopen, clipped to the rail meanwhile. It reflows the page for the length of the ease, which is acceptable for a deliberate user toggle and never happens on load. Expressive: `subtle` snaps."
|
|
129
|
+
},
|
|
130
|
+
{
|
|
131
|
+
slot: "activity",
|
|
132
|
+
recipe: "none",
|
|
133
|
+
part: "Root",
|
|
134
|
+
why: "Navigation chrome has no working state; a loading route shows it in the page."
|
|
135
|
+
}
|
|
136
|
+
],
|
|
99
137
|
a11y: [
|
|
100
138
|
"SidebarNav renders a <nav> landmark.",
|
|
101
139
|
'Active item carries aria-current="page".',
|
|
@@ -173,6 +173,32 @@ var sliderMeta = {
|
|
|
173
173
|
}
|
|
174
174
|
],
|
|
175
175
|
tokensUsed: ["secondary", "primary", "background", "ring"],
|
|
176
|
+
motion: [
|
|
177
|
+
{
|
|
178
|
+
slot: "arrival",
|
|
179
|
+
recipe: "none",
|
|
180
|
+
part: "Root",
|
|
181
|
+
why: "It arrives with the form or panel it sits in."
|
|
182
|
+
},
|
|
183
|
+
{
|
|
184
|
+
slot: "response",
|
|
185
|
+
recipe: "motion-interactive",
|
|
186
|
+
part: "Thumb",
|
|
187
|
+
why: "The thumb's dot grows from 12px to full size on hover and keyboard focus, and a little past it while dragging, on a spring. Only the paint resizes, never the hit target, so the growth says the thumb is live without moving where you have to aim."
|
|
188
|
+
},
|
|
189
|
+
{
|
|
190
|
+
slot: "transition",
|
|
191
|
+
recipe: "none",
|
|
192
|
+
part: "Thumb",
|
|
193
|
+
why: "The thumb and the filled range follow the pointer one-to-one, and keyboard steps land at once. An eased position would trail the drag and show a value the user has already moved past."
|
|
194
|
+
},
|
|
195
|
+
{
|
|
196
|
+
slot: "activity",
|
|
197
|
+
recipe: "none",
|
|
198
|
+
part: "Root",
|
|
199
|
+
why: "A slider commits as it moves; there is nothing to wait for."
|
|
200
|
+
}
|
|
201
|
+
],
|
|
176
202
|
a11y: [
|
|
177
203
|
"Base UI provides role=slider, aria-valuemin/max/now, and arrow-key / Page Up-Down / Home-End control.",
|
|
178
204
|
"Each thumb is a focusable input[type=range] nested in the thumb element; keyboard focus raises a 2px ring on the thumb via has-[:focus-visible] (a bare focus-visible: on the thumb never fires, since the div itself is not focusable).",
|
|
@@ -5,15 +5,17 @@ import { areaPath, linePath } from "../../lib/chart/geometry.js";
|
|
|
5
5
|
import { drawTransition, riseTransition, stagger } from "../../lib/chart/motion.js";
|
|
6
6
|
import { toneColor } from "../../lib/chart/types.js";
|
|
7
7
|
import { cn } from "../../lib/cn.js";
|
|
8
|
-
import {
|
|
8
|
+
import { motionAttribute, useMotionLevel } from "../../motion/MotionProvider.js";
|
|
9
|
+
import { chartMotion } from "../../lib/chart/chart-frame.js";
|
|
9
10
|
import { useArrival } from "../../lib/chart/use-arrival.js";
|
|
10
11
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
11
12
|
var W = 100;
|
|
12
13
|
var H = 32;
|
|
13
|
-
function Sparkline({ data, label, variant = "line", tone = "primary", animate
|
|
14
|
-
const
|
|
14
|
+
function Sparkline({ data, label, variant = "line", tone = "primary", motion, animate, locale, className, ...props }) {
|
|
15
|
+
const level = chartMotion(motion, animate);
|
|
16
|
+
const { expressive } = useMotionLevel(level);
|
|
15
17
|
const [ref, inView] = useArrival();
|
|
16
|
-
const shown = !
|
|
18
|
+
const shown = !expressive || inView;
|
|
17
19
|
const color = toneColor(tone);
|
|
18
20
|
const min = data.length ? Math.min(...data) : 0;
|
|
19
21
|
const max = data.length ? Math.max(...data) : 1;
|
|
@@ -72,6 +74,7 @@ function Sparkline({ data, label, variant = "line", tone = "primary", animate =
|
|
|
72
74
|
viewBox: `0 0 ${W} ${H}`,
|
|
73
75
|
preserveAspectRatio: "none",
|
|
74
76
|
"data-reveal": shown ? "in" : "out",
|
|
77
|
+
"data-motion": motionAttribute(level),
|
|
75
78
|
className: cn("inline-block h-8 w-24 shrink-0 overflow-visible align-middle", className),
|
|
76
79
|
...props,
|
|
77
80
|
children: marks
|
|
@@ -70,11 +70,18 @@ var sparklineMeta = {
|
|
|
70
70
|
default: '"h-8 w-24"',
|
|
71
71
|
description: "Sizes it; the drawing stretches to fit."
|
|
72
72
|
},
|
|
73
|
+
{
|
|
74
|
+
name: "motion",
|
|
75
|
+
type: '"full" | "subtle" | "off"',
|
|
76
|
+
default: '"full"',
|
|
77
|
+
description: "How much motion this component uses: `full` (default) inherits the app's policy, `subtle` keeps feedback and drops the flourish, `off` stills it. It can only turn motion down \u2014 it never overrides a reader's reduce-motion setting. Drawing on (or rising) is the flourish, so `subtle` and `off` show the finished line."
|
|
78
|
+
},
|
|
73
79
|
{
|
|
74
80
|
name: "animate",
|
|
75
81
|
type: "boolean",
|
|
76
82
|
default: "true",
|
|
77
|
-
description:
|
|
83
|
+
description: 'Draw on (or rise) when it scrolls into view. `animate={false}` is `motion="subtle"`.',
|
|
84
|
+
deprecated: 'motion="subtle" (what animate={false} now maps to) or motion="off"'
|
|
78
85
|
},
|
|
79
86
|
{
|
|
80
87
|
name: "locale",
|
|
@@ -106,7 +113,7 @@ var sparklineMeta = {
|
|
|
106
113
|
slot: "arrival",
|
|
107
114
|
recipe: "custom",
|
|
108
115
|
part: "Line / bars",
|
|
109
|
-
why:
|
|
116
|
+
why: 'Once on screen the line draws on (or the bars rise, in sequence). On the motion tokens; reduced motion shows the finished drawing. The arrival is expressive: it plays at the full motion level only, so `motion="subtle"` (or a subtle app policy) shows the finished line.'
|
|
110
117
|
},
|
|
111
118
|
{
|
|
112
119
|
slot: "response",
|