@sableui/react 0.13.1 → 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 +132 -0
- package/CONTRIBUTING.md +7 -5
- package/DESIGN.md +8 -3
- package/bin/sable.mjs +17 -1
- package/dist/blocks/approval-block.js +93 -0
- package/dist/blocks/approval-block.meta.js +23 -0
- package/dist/blocks/index.d.ts +17 -2
- package/dist/blocks/index.js +4 -0
- package/dist/components/accordion/accordion.meta.js +10 -3
- package/dist/components/area-chart/area-chart.meta.js +10 -3
- package/dist/components/attachment/attachment.meta.js +26 -0
- package/dist/components/badge/badge.meta.js +2 -1
- package/dist/components/banner/banner.meta.js +4 -2
- 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/button-group/button-group.meta.js +2 -1
- 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 +34 -1
- 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 +34 -1
- package/dist/components/color-field/color-field.meta.js +26 -0
- package/dist/components/combobox/combobox.meta.js +48 -2
- package/dist/components/command-menu/command-menu.js +8 -3
- package/dist/components/command-menu/command-menu.meta.js +44 -3
- 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/context-card/context-card.meta.js +2 -1
- package/dist/components/data-table/data-table.js +6 -2
- package/dist/components/data-table/data-table.meta.js +32 -0
- package/dist/components/dialog/dialog.meta.js +6 -3
- package/dist/components/disclosure-picker/disclosure-picker.meta.js +2 -1
- 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/inline-status/inline-status.meta.js +2 -1
- 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/menu/menu.meta.js +2 -1
- package/dist/components/menubar/menubar.meta.js +2 -1
- package/dist/components/meter/meter.meta.js +2 -1
- package/dist/components/middle-truncate/middle-truncate.meta.js +1 -1
- package/dist/components/note/note.meta.js +34 -1
- 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/page-loader/page-loader.meta.js +2 -1
- 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 +34 -1
- package/dist/components/popover/popover.meta.js +4 -2
- package/dist/components/progress/progress.meta.js +2 -1
- 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 +28 -1
- 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/segmented-bar/segmented-bar.meta.js +2 -1
- package/dist/components/select/select.meta.js +4 -2
- package/dist/components/sheet/sheet.meta.js +4 -2
- 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 +34 -1
- 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/skeleton/skeleton.meta.js +2 -1
- 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/spinner/spinner.meta.js +2 -1
- package/dist/components/stepper/stepper.meta.js +4 -2
- package/dist/components/switch/switch.meta.js +2 -1
- package/dist/components/table/table.meta.js +28 -1
- package/dist/components/tabs/tabs.js +27 -8
- package/dist/components/tabs/tabs.meta.js +50 -5
- 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/thinking-orb/thinking-orb.meta.js +4 -2
- package/dist/components/toast/toast.meta.js +2 -1
- package/dist/components/toggle/toggle.js +28 -5
- package/dist/components/toggle/toggle.meta.js +46 -3
- 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/toolbar/toolbar.meta.js +2 -1
- package/dist/components/tooltip/index.js +3 -1
- package/dist/components/tooltip/tooltip.js +84 -0
- package/dist/components/tooltip/tooltip.meta.js +18 -2
- 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 +139 -122
- package/dist/foundations/motion.js +49 -3
- package/dist/index.d.ts +616 -46
- 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/{meta-DVG8ceze.d.ts → meta-DX5r5wQL.d.ts} +8 -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/patterns/index.d.ts +1 -1
- package/dist/styles.css +298 -5
- package/llms/blocks/approval-block.md +128 -0
- package/llms/components/accordion.md +4 -3
- package/llms/components/area-chart.md +4 -3
- package/llms/components/attachment.md +7 -0
- package/llms/components/badge.md +1 -1
- package/llms/components/banner.md +2 -2
- 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-group.md +1 -1
- 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 +9 -1
- package/llms/components/code-block.md +8 -0
- package/llms/components/collapsible.md +9 -1
- package/llms/components/color-field.md +7 -0
- package/llms/components/combobox.md +12 -2
- package/llms/components/command-menu.md +12 -3
- package/llms/components/confirm-button.md +92 -0
- package/llms/components/context-card.md +1 -1
- package/llms/components/data-table.md +8 -0
- package/llms/components/dialog.md +3 -3
- package/llms/components/disclosure-picker.md +1 -1
- package/llms/components/entity.md +7 -0
- package/llms/components/feedback.md +22 -3
- package/llms/components/inline-status.md +1 -1
- package/llms/components/input-group.md +8 -1
- package/llms/components/line-chart.md +4 -3
- package/llms/components/menu.md +1 -1
- package/llms/components/menubar.md +1 -1
- package/llms/components/meter.md +1 -1
- package/llms/components/middle-truncate.md +1 -1
- package/llms/components/note.md +9 -1
- 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/page-loader.md +1 -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 +9 -1
- package/llms/components/popover.md +2 -2
- package/llms/components/progress.md +1 -1
- 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 +8 -1
- 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/segmented-bar.md +1 -1
- package/llms/components/select.md +2 -2
- package/llms/components/sheet.md +2 -2
- package/llms/components/show-more.md +9 -0
- package/llms/components/side-nav.md +9 -1
- package/llms/components/sidebar.md +9 -0
- package/llms/components/skeleton.md +1 -1
- package/llms/components/slider.md +7 -0
- package/llms/components/sparkline.md +3 -2
- package/llms/components/spinner.md +1 -1
- package/llms/components/stepper.md +2 -2
- package/llms/components/switch.md +1 -1
- package/llms/components/table.md +8 -1
- package/llms/components/tabs.md +22 -4
- package/llms/components/theme-selector.md +8 -0
- package/llms/components/theme-switcher.md +17 -0
- package/llms/components/thinking-orb.md +2 -2
- package/llms/components/toast.md +1 -1
- package/llms/components/toggle.md +20 -3
- package/llms/components/tool-call.md +115 -0
- package/llms/components/toolbar.md +1 -1
- package/llms/components/tooltip.md +12 -2
- 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 +1004 -98
- package/llms.txt +16 -11
- package/package.json +1 -1
- package/registry.json +2708 -187
- package/targets/ios.json +27 -0
package/dist/index.js
CHANGED
|
@@ -43,6 +43,7 @@ export * from "./icon/index.js";
|
|
|
43
43
|
export * from "./motion/index.js";
|
|
44
44
|
export * from "./components/button/index.js";
|
|
45
45
|
export * from "./components/button-group/index.js";
|
|
46
|
+
export * from "./components/confirm-button/index.js";
|
|
46
47
|
export * from "./components/card/index.js";
|
|
47
48
|
export * from "./components/caret-menu/index.js";
|
|
48
49
|
export * from "./components/badge/index.js";
|
|
@@ -63,10 +64,12 @@ export * from "./components/avatar/index.js";
|
|
|
63
64
|
export * from "./components/skeleton/index.js";
|
|
64
65
|
export * from "./components/spinner/index.js";
|
|
65
66
|
export * from "./components/thinking-orb/index.js";
|
|
67
|
+
export * from "./components/tool-call/index.js";
|
|
66
68
|
export * from "./components/streaming-text/index.js";
|
|
67
69
|
export * from "./components/gliding-highlight/index.js";
|
|
68
70
|
export * from "./components/progress/index.js";
|
|
69
71
|
export * from "./components/meter/index.js";
|
|
72
|
+
export * from "./components/number-ticker/index.js";
|
|
70
73
|
export * from "./components/segmented-bar/index.js";
|
|
71
74
|
export * from "./components/area-chart/index.js";
|
|
72
75
|
export * from "./components/bar-chart/index.js";
|
|
@@ -172,7 +175,7 @@ import {
|
|
|
172
175
|
} from "./lib/use-caret-trigger.js";
|
|
173
176
|
import { STATUS_TONES, STATUS_TONE_ICON } from "./lib/status-tone.js";
|
|
174
177
|
import { PALETTE_COLORS, PALETTE_FAMILIES, PALETTE_SCALES, PALETTE_STEPS, PALETTE_TONES, isPaletteColor } from "./lib/palette.js";
|
|
175
|
-
var SABLE_UI_VERSION = "0.
|
|
178
|
+
var SABLE_UI_VERSION = "0.14.0";
|
|
176
179
|
export {
|
|
177
180
|
ART_TOKENS,
|
|
178
181
|
CHART_SERIES_COUNT,
|
|
@@ -3,10 +3,11 @@
|
|
|
3
3
|
// src/lib/chart/cartesian.tsx
|
|
4
4
|
import { useId } from "react";
|
|
5
5
|
import { cn } from "../cn.js";
|
|
6
|
-
import { ChartFrame } from "./chart-frame.js";
|
|
6
|
+
import { ChartFrame, chartMotion } from "./chart-frame.js";
|
|
7
7
|
import { ChartLegend } from "./chart-legend.js";
|
|
8
8
|
import { ChartTooltipContent } from "./chart-tooltip.js";
|
|
9
9
|
import { approxTextWidth, categoryText, chartFormatters } from "./format.js";
|
|
10
|
+
import { FILL_HOVER, dimmed } from "./dim.js";
|
|
10
11
|
import { areaPath, linePath, rectPath } from "./geometry.js";
|
|
11
12
|
import { GLIDE, barTransition, drawTransition, morphTransition, pathStyle, riseTransition, stagger } from "./motion.js";
|
|
12
13
|
import { bandScale, extent, linearScale, niceTicks, pointScale, stack } from "./scale.js";
|
|
@@ -34,7 +35,8 @@ function CartesianChart(props) {
|
|
|
34
35
|
locale,
|
|
35
36
|
tooltip = true,
|
|
36
37
|
legend,
|
|
37
|
-
|
|
38
|
+
motion,
|
|
39
|
+
animate,
|
|
38
40
|
activeIndex,
|
|
39
41
|
defaultActiveIndex,
|
|
40
42
|
onActiveIndexChange,
|
|
@@ -150,7 +152,7 @@ function CartesianChart(props) {
|
|
|
150
152
|
className,
|
|
151
153
|
count: n,
|
|
152
154
|
interactive: tooltip !== false,
|
|
153
|
-
animate,
|
|
155
|
+
motion: chartMotion(motion, animate),
|
|
154
156
|
activeIndex,
|
|
155
157
|
defaultActiveIndex,
|
|
156
158
|
onActiveIndexChange,
|
|
@@ -232,15 +234,12 @@ function CartesianChart(props) {
|
|
|
232
234
|
"path",
|
|
233
235
|
{
|
|
234
236
|
d,
|
|
235
|
-
className:
|
|
236
|
-
horizontal ? "[[data-reveal=out]_&]:scale-x-0" : "[[data-reveal=out]_&]:scale-y-0",
|
|
237
|
-
dim && "opacity-40"
|
|
238
|
-
),
|
|
237
|
+
className: horizontal ? "[[data-reveal=out]_&]:scale-x-0" : "[[data-reveal=out]_&]:scale-y-0",
|
|
239
238
|
style: {
|
|
240
239
|
...pathStyle(d),
|
|
241
|
-
fill: colorAt(k, i),
|
|
240
|
+
fill: dim ? dimmed(colorAt(k, i), 38) : colorAt(k, i),
|
|
242
241
|
transformOrigin: origin,
|
|
243
|
-
transition: barTransition(stagger(i))
|
|
242
|
+
transition: `${barTransition(stagger(i))}, ${FILL_HOVER}`
|
|
244
243
|
}
|
|
245
244
|
},
|
|
246
245
|
`b${k}-${i}`
|
|
@@ -329,7 +328,7 @@ function CartesianChart(props) {
|
|
|
329
328
|
r: 4,
|
|
330
329
|
strokeWidth: 2,
|
|
331
330
|
className: cn(SURFACE, "[[data-reveal=out]_&]:opacity-0"),
|
|
332
|
-
style: { fill: colorBy === "index" ? colorAt(k, i) : s.colorValue, transition: morphTransition(), transitionDelay: `calc(${300 + i * 25}ms * var(--motion-scale, 1))` }
|
|
331
|
+
style: { fill: colorBy === "index" ? colorAt(k, i) : s.colorValue, transition: morphTransition(), transitionDelay: `calc(${300 + i * 25}ms * var(--motion-scale, 1) * var(--motion-expressive, 1))` }
|
|
333
332
|
},
|
|
334
333
|
`p${k}-${i}`
|
|
335
334
|
)
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
// src/lib/chart/chart-center.tsx
|
|
4
|
+
import { NumberTicker } from "../../components/number-ticker/number-ticker.js";
|
|
5
|
+
import { cn } from "../cn.js";
|
|
6
|
+
import { centerValueFormat } from "./format.js";
|
|
7
|
+
import { morphTransition } from "./motion.js";
|
|
8
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
9
|
+
function ChartCenter({ center, x, y, placement = "middle", locale, motion }) {
|
|
10
|
+
const value = typeof center.value === "number" ? /* @__PURE__ */ jsx(NumberTicker, { value: center.value, format: centerValueFormat(center.value), locales: locale, motion, className: "text-heading-2 text-foreground" }) : /* @__PURE__ */ jsx("span", { className: "text-heading-2 text-foreground tabular-nums", children: center.value });
|
|
11
|
+
const middle = placement === "middle";
|
|
12
|
+
return /* @__PURE__ */ jsx(
|
|
13
|
+
"div",
|
|
14
|
+
{
|
|
15
|
+
className: "absolute whitespace-nowrap [[data-reveal=out]_&]:opacity-0",
|
|
16
|
+
style: { left: x, top: y, transform: middle ? "translate(-50%, -50%)" : "translate(-50%, -100%)", transition: morphTransition() },
|
|
17
|
+
children: /* @__PURE__ */ jsxs("div", { className: cn("relative flex flex-col items-center", !middle && center.label && "pb-4"), children: [
|
|
18
|
+
value,
|
|
19
|
+
center.label ? middle ? (
|
|
20
|
+
// Out of flow, so the number alone is centred; centred by flex so a
|
|
21
|
+
// caption wider than the number overhangs it evenly on both sides.
|
|
22
|
+
/* @__PURE__ */ jsx("span", { className: "absolute inset-x-0 top-full mt-1 flex justify-center", children: /* @__PURE__ */ jsx("span", { className: "text-caption text-muted-foreground", children: center.label }) })
|
|
23
|
+
) : /* @__PURE__ */ jsx("span", { className: "text-caption text-muted-foreground", children: center.label }) : null
|
|
24
|
+
] })
|
|
25
|
+
}
|
|
26
|
+
);
|
|
27
|
+
}
|
|
28
|
+
export {
|
|
29
|
+
ChartCenter
|
|
30
|
+
};
|
|
@@ -2,16 +2,19 @@
|
|
|
2
2
|
|
|
3
3
|
// src/lib/chart/chart-frame.tsx
|
|
4
4
|
import { useEffect, useId, useRef, useState } from "react";
|
|
5
|
-
import {
|
|
5
|
+
import { motionAttribute, useMotionLevel } from "../../motion/MotionProvider.js";
|
|
6
6
|
import { useArrival } from "./use-arrival.js";
|
|
7
7
|
import { cn } from "../cn.js";
|
|
8
8
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
9
|
+
function chartMotion(motion, animate) {
|
|
10
|
+
return motion ?? (animate === false ? "subtle" : void 0);
|
|
11
|
+
}
|
|
9
12
|
function ChartFrame({
|
|
10
13
|
label,
|
|
11
14
|
className,
|
|
12
15
|
count,
|
|
13
16
|
interactive,
|
|
14
|
-
|
|
17
|
+
motion,
|
|
15
18
|
activeIndex: controlled,
|
|
16
19
|
defaultActiveIndex = null,
|
|
17
20
|
onActiveIndexChange,
|
|
@@ -20,13 +23,14 @@ function ChartFrame({
|
|
|
20
23
|
describe,
|
|
21
24
|
legend,
|
|
22
25
|
table,
|
|
23
|
-
children
|
|
26
|
+
children,
|
|
27
|
+
overlay
|
|
24
28
|
}) {
|
|
25
|
-
const {
|
|
29
|
+
const { expressive } = useMotionLevel(motion);
|
|
26
30
|
const [viewRef, inView] = useArrival();
|
|
27
31
|
const plotRef = useRef(null);
|
|
28
32
|
const [size, setSize] = useState(null);
|
|
29
|
-
const [shown, setShown] = useState(
|
|
33
|
+
const [shown, setShown] = useState(motion === "off" || motion === "subtle");
|
|
30
34
|
const [uncontrolled, setUncontrolled] = useState(defaultActiveIndex);
|
|
31
35
|
const [viaKeyboard, setViaKeyboard] = useState(false);
|
|
32
36
|
const appeared = useRef(true);
|
|
@@ -54,7 +58,7 @@ function ChartFrame({
|
|
|
54
58
|
}, []);
|
|
55
59
|
useEffect(() => {
|
|
56
60
|
if (shown) return;
|
|
57
|
-
if (!
|
|
61
|
+
if (!expressive) {
|
|
58
62
|
setShown(true);
|
|
59
63
|
return;
|
|
60
64
|
}
|
|
@@ -67,7 +71,7 @@ function ChartFrame({
|
|
|
67
71
|
cancelAnimationFrame(outer);
|
|
68
72
|
cancelAnimationFrame(inner);
|
|
69
73
|
};
|
|
70
|
-
}, [shown,
|
|
74
|
+
}, [shown, expressive, size, inView]);
|
|
71
75
|
const local = (event) => {
|
|
72
76
|
const r = event.currentTarget.getBoundingClientRect();
|
|
73
77
|
return { x: event.clientX - r.left, y: event.clientY - r.top };
|
|
@@ -98,6 +102,7 @@ function ChartFrame({
|
|
|
98
102
|
{
|
|
99
103
|
ref: viewRef,
|
|
100
104
|
"data-reveal": shown ? "in" : "out",
|
|
105
|
+
"data-motion": motionAttribute(motion),
|
|
101
106
|
className: cn("m-0 flex h-60 w-full min-w-0 flex-col gap-3", className),
|
|
102
107
|
children: [
|
|
103
108
|
/* @__PURE__ */ jsxs(
|
|
@@ -129,6 +134,7 @@ function ChartFrame({
|
|
|
129
134
|
children: children({ size, activeIndex: interactive ? active : null })
|
|
130
135
|
}
|
|
131
136
|
) : null,
|
|
137
|
+
overlay && size && size.width > 0 && size.height > 0 ? /* @__PURE__ */ jsx("div", { "aria-hidden": true, className: "pointer-events-none absolute inset-0", children: overlay({ size, activeIndex: interactive ? active : null }) }) : null,
|
|
132
138
|
placement ? /* @__PURE__ */ jsx(
|
|
133
139
|
"div",
|
|
134
140
|
{
|
|
@@ -159,5 +165,6 @@ function ChartFrame({
|
|
|
159
165
|
);
|
|
160
166
|
}
|
|
161
167
|
export {
|
|
162
|
-
ChartFrame
|
|
168
|
+
ChartFrame,
|
|
169
|
+
chartMotion
|
|
163
170
|
};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
// src/lib/chart/dim.ts
|
|
2
|
+
import { HOVER } from "./motion.js";
|
|
3
|
+
var SURFACE = "var(--sable-surface-bg, var(--background))";
|
|
4
|
+
function dimmed(color, keep) {
|
|
5
|
+
return `color-mix(in oklab, ${color} ${keep}%, ${SURFACE})`;
|
|
6
|
+
}
|
|
7
|
+
var FILL_HOVER = `fill ${HOVER}`;
|
|
8
|
+
var STROKE_HOVER = `stroke ${HOVER}`;
|
|
9
|
+
export {
|
|
10
|
+
FILL_HOVER,
|
|
11
|
+
STROKE_HOVER,
|
|
12
|
+
dimmed
|
|
13
|
+
};
|
package/dist/lib/chart/format.js
CHANGED
|
@@ -14,11 +14,17 @@ function approxTextWidth(text) {
|
|
|
14
14
|
}
|
|
15
15
|
function centerValueText(value, locale) {
|
|
16
16
|
if (typeof value === "string") return value;
|
|
17
|
-
return
|
|
17
|
+
return new Intl.NumberFormat(locale, centerValueFormat(value)).format(value);
|
|
18
18
|
}
|
|
19
|
+
function centerValueFormat(value) {
|
|
20
|
+
return Math.abs(value) < 1e4 ? FULL_CENTER : COMPACT_CENTER;
|
|
21
|
+
}
|
|
22
|
+
var FULL_CENTER = { maximumFractionDigits: 1 };
|
|
23
|
+
var COMPACT_CENTER = { notation: "compact", maximumFractionDigits: 1 };
|
|
19
24
|
export {
|
|
20
25
|
approxTextWidth,
|
|
21
26
|
categoryText,
|
|
27
|
+
centerValueFormat,
|
|
22
28
|
centerValueText,
|
|
23
29
|
chartFormatters
|
|
24
30
|
};
|
package/dist/lib/chart/motion.js
CHANGED
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
// src/lib/chart/motion.ts
|
|
2
|
-
var
|
|
3
|
-
var
|
|
4
|
-
var
|
|
2
|
+
var expressive = (token, factor = 1) => `calc(var(${token}) * ${factor} * var(--motion-expressive, 1))`;
|
|
3
|
+
var DRAW_DURATION = expressive("--motion-duration-slower", 1.8);
|
|
4
|
+
var RISE = `${expressive("--motion-duration-slower")} var(--motion-ease-emphasized)`;
|
|
5
|
+
var MORPH = `${expressive("--motion-duration-slow")} var(--motion-ease-standard)`;
|
|
5
6
|
var HOVER = "var(--motion-duration-fast) var(--motion-ease-standard)";
|
|
6
7
|
var GLIDE = `transform ${HOVER}, opacity ${HOVER}, cx ${HOVER}, cy ${HOVER}`;
|
|
7
8
|
function barTransition(delay = "0ms") {
|
|
8
9
|
return `scale ${RISE} ${delay}, opacity ${HOVER}, d ${MORPH}`;
|
|
9
10
|
}
|
|
10
11
|
function stagger(index, stepMs = 35) {
|
|
11
|
-
return `calc(${Math.min(index, 24) * stepMs}ms * var(--motion-scale, 1))`;
|
|
12
|
+
return `calc(${Math.min(index, 24) * stepMs}ms * var(--motion-scale, 1) * var(--motion-expressive, 1))`;
|
|
12
13
|
}
|
|
13
14
|
function drawTransition(delay = "0ms") {
|
|
14
15
|
return `stroke-dashoffset ${DRAW_DURATION} var(--motion-ease-decelerate) ${delay}, d ${MORPH}, opacity ${HOVER}`;
|
|
@@ -25,6 +26,7 @@ function pathStyle(d) {
|
|
|
25
26
|
export {
|
|
26
27
|
DRAW_DURATION,
|
|
27
28
|
GLIDE,
|
|
29
|
+
HOVER,
|
|
28
30
|
barTransition,
|
|
29
31
|
drawTransition,
|
|
30
32
|
morphTransition,
|
|
@@ -3,17 +3,26 @@
|
|
|
3
3
|
// src/lib/highlight-glide.tsx
|
|
4
4
|
import { useLayoutEffect, useRef } from "react";
|
|
5
5
|
import { jsx } from "react/jsx-runtime";
|
|
6
|
-
|
|
6
|
+
var expressive = (token) => `calc(var(${token}) * var(--motion-expressive, 1))`;
|
|
7
|
+
function HighlightGlide({
|
|
8
|
+
className = "rounded-popup-item bg-accent",
|
|
9
|
+
follow = "data-highlighted",
|
|
10
|
+
timing = "highlight"
|
|
11
|
+
}) {
|
|
7
12
|
const ref = useRef(null);
|
|
8
13
|
useLayoutEffect(() => {
|
|
9
14
|
const glide = ref.current;
|
|
10
15
|
const host = glide?.parentElement;
|
|
11
16
|
if (!glide || !host) return;
|
|
12
17
|
host.setAttribute("data-glide", "");
|
|
18
|
+
const selection = timing === "selection";
|
|
13
19
|
let shown = false;
|
|
20
|
+
let placed = false;
|
|
21
|
+
let lastRow = null;
|
|
22
|
+
let lastTarget = "";
|
|
14
23
|
let frame = 0;
|
|
15
24
|
const place = () => {
|
|
16
|
-
const row = host.querySelector(
|
|
25
|
+
const row = host.querySelector(`[${follow}]:not([data-highlight-glide])`);
|
|
17
26
|
if (!row) {
|
|
18
27
|
shown = false;
|
|
19
28
|
glide.style.opacity = "0";
|
|
@@ -21,20 +30,59 @@ function HighlightGlide({ className = "rounded-popup-item bg-accent" }) {
|
|
|
21
30
|
}
|
|
22
31
|
const hostBox = host.getBoundingClientRect();
|
|
23
32
|
const rowBox = row.getBoundingClientRect();
|
|
24
|
-
const
|
|
33
|
+
const layoutWidth = selection ? Number.parseFloat(getComputedStyle(host).width) || host.offsetWidth : host.offsetWidth;
|
|
34
|
+
const scale = layoutWidth > 0 ? hostBox.width / layoutWidth || 1 : 1;
|
|
25
35
|
const x = (rowBox.left - hostBox.left) / scale + host.scrollLeft - host.clientLeft;
|
|
26
36
|
const y = (rowBox.top - hostBox.top) / scale + host.scrollTop - host.clientTop;
|
|
27
37
|
const geometry = (s) => {
|
|
28
38
|
s.width = `${rowBox.width / scale}px`;
|
|
29
39
|
s.height = `${rowBox.height / scale}px`;
|
|
30
|
-
s.
|
|
40
|
+
if (selection) s.translate = `${x}px ${y}px`;
|
|
41
|
+
else s.transform = `translate(${x}px, ${y}px)`;
|
|
31
42
|
};
|
|
43
|
+
if (selection) {
|
|
44
|
+
const target = `${x},${y},${rowBox.width / scale},${rowBox.height / scale}`;
|
|
45
|
+
const sameRow = placed && shown && row === lastRow;
|
|
46
|
+
lastRow = row;
|
|
47
|
+
const moved = target !== lastTarget;
|
|
48
|
+
lastTarget = target;
|
|
49
|
+
if (sameRow) {
|
|
50
|
+
if (moved) {
|
|
51
|
+
glide.style.transitionProperty = "none";
|
|
52
|
+
geometry(glide.style);
|
|
53
|
+
void glide.offsetWidth;
|
|
54
|
+
glide.style.transitionProperty = "";
|
|
55
|
+
}
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
if (!placed) {
|
|
59
|
+
glide.style.transitionProperty = "none";
|
|
60
|
+
geometry(glide.style);
|
|
61
|
+
glide.style.opacity = "1";
|
|
62
|
+
void glide.offsetWidth;
|
|
63
|
+
glide.style.transitionProperty = "";
|
|
64
|
+
placed = true;
|
|
65
|
+
shown = true;
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
if (!shown) {
|
|
69
|
+
glide.style.transitionProperty = "opacity";
|
|
70
|
+
geometry(glide.style);
|
|
71
|
+
void glide.offsetWidth;
|
|
72
|
+
glide.style.transitionProperty = "";
|
|
73
|
+
shown = true;
|
|
74
|
+
} else {
|
|
75
|
+
geometry(glide.style);
|
|
76
|
+
}
|
|
77
|
+
glide.style.opacity = "1";
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
32
80
|
if (!shown) {
|
|
33
81
|
glide.style.transitionProperty = "opacity";
|
|
34
82
|
geometry(glide.style);
|
|
35
83
|
cancelAnimationFrame(frame);
|
|
36
84
|
frame = requestAnimationFrame(() => {
|
|
37
|
-
glide.style.transitionProperty = "transform, width, height
|
|
85
|
+
glide.style.transitionProperty = "opacity, transform, width, height";
|
|
38
86
|
});
|
|
39
87
|
shown = true;
|
|
40
88
|
} else {
|
|
@@ -44,7 +92,7 @@ function HighlightGlide({ className = "rounded-popup-item bg-accent" }) {
|
|
|
44
92
|
};
|
|
45
93
|
place();
|
|
46
94
|
const mutations = new MutationObserver(place);
|
|
47
|
-
mutations.observe(host, { attributes: true, attributeFilter: [
|
|
95
|
+
mutations.observe(host, { attributes: true, attributeFilter: [follow], subtree: true, childList: true });
|
|
48
96
|
const resize = new ResizeObserver(place);
|
|
49
97
|
resize.observe(host);
|
|
50
98
|
host.addEventListener("scroll", place, true);
|
|
@@ -59,7 +107,7 @@ function HighlightGlide({ className = "rounded-popup-item bg-accent" }) {
|
|
|
59
107
|
host.removeEventListener("animationend", place);
|
|
60
108
|
host.removeAttribute("data-glide");
|
|
61
109
|
};
|
|
62
|
-
}, []);
|
|
110
|
+
}, [follow, timing]);
|
|
63
111
|
return /* @__PURE__ */ jsx(
|
|
64
112
|
"span",
|
|
65
113
|
{
|
|
@@ -72,9 +120,19 @@ function HighlightGlide({ className = "rounded-popup-item bg-accent" }) {
|
|
|
72
120
|
// getBoundingClientRect, which is physical in RTL too.
|
|
73
121
|
left: 0,
|
|
74
122
|
opacity: 0,
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
123
|
+
...timing === "highlight" && {
|
|
124
|
+
transitionProperty: "opacity",
|
|
125
|
+
// One duration per property in "opacity, transform, width, height"
|
|
126
|
+
// order. The fade that shows or hides the highlight is essential;
|
|
127
|
+
// the travel is expressive.
|
|
128
|
+
transitionDuration: [
|
|
129
|
+
"var(--motion-duration-instant)",
|
|
130
|
+
expressive("--motion-duration-instant"),
|
|
131
|
+
expressive("--motion-duration-instant"),
|
|
132
|
+
expressive("--motion-duration-instant")
|
|
133
|
+
].join(", "),
|
|
134
|
+
transitionTimingFunction: "var(--motion-ease-standard)"
|
|
135
|
+
}
|
|
78
136
|
}
|
|
79
137
|
}
|
|
80
138
|
);
|
package/dist/lib/taxonomy.js
CHANGED
|
@@ -25,6 +25,7 @@ var COMPONENT_TAXONOMY = {
|
|
|
25
25
|
what it is. Copying is `useCopy` on a Button, not a component. */
|
|
26
26
|
Button: ["Actions", "either"],
|
|
27
27
|
ButtonGroup: ["Actions", "either"],
|
|
28
|
+
ConfirmButton: ["Actions", "either"],
|
|
28
29
|
Menubar: ["Actions", "either"],
|
|
29
30
|
OverflowMenu: ["Actions", "either"],
|
|
30
31
|
Toolbar: ["Actions", "either"],
|
|
@@ -136,6 +137,7 @@ var COMPONENT_TAXONOMY = {
|
|
|
136
137
|
Entity: ["Data display", "either"],
|
|
137
138
|
GlidingHighlight: ["Data display", "either"],
|
|
138
139
|
Meter: ["Data display", "either"],
|
|
140
|
+
NumberTicker: ["Data display", "either"],
|
|
139
141
|
RelativeTime: ["Data display", "either"],
|
|
140
142
|
SegmentedBar: ["Data display", "either"],
|
|
141
143
|
Table: ["Data display", "either"],
|
|
@@ -184,6 +186,7 @@ var COMPONENT_TAXONOMY = {
|
|
|
184
186
|
MessageScroller: ["Chat & AI", "either"],
|
|
185
187
|
StreamingText: ["Chat & AI", "either"],
|
|
186
188
|
ThinkingOrb: ["Chat & AI", "either"],
|
|
189
|
+
ToolCall: ["Chat & AI", "either"],
|
|
187
190
|
/* ── Media & mockups ──────────────────────────────────────────────────────
|
|
188
191
|
Presenting images and video: players, galleries, scrolling strips, and the
|
|
189
192
|
device frames a product image sits in. `Video` was filed as a mockup; it is
|
|
@@ -26,6 +26,14 @@ interface BestPracticeMeta {
|
|
|
26
26
|
guidance: "do" | "dont";
|
|
27
27
|
/** Short design guidance focused on when and how to use the component. */
|
|
28
28
|
description: string;
|
|
29
|
+
/**
|
|
30
|
+
* On a "dont" only: what to reach for instead — component or subcomponent
|
|
31
|
+
* names, checked at build. Prose that names an alternative goes stale silently
|
|
32
|
+
* when that component is renamed; this does not, and it lets the CLI print a
|
|
33
|
+
* short "not for" list at compact detail. Leave it off when the remedy is a
|
|
34
|
+
* way of using this same component.
|
|
35
|
+
*/
|
|
36
|
+
instead?: string[];
|
|
29
37
|
}
|
|
30
38
|
interface AnatomyMeta {
|
|
31
39
|
/** Visible or structural part name, e.g. "Label", "Trigger", "Content". */
|
|
@@ -17,6 +17,9 @@ function resolveEnabled(policy, prefersReducedMotion) {
|
|
|
17
17
|
if (policy === "full") return true;
|
|
18
18
|
return !prefersReducedMotion;
|
|
19
19
|
}
|
|
20
|
+
function resolveExpressive(policy, prefersReducedMotion) {
|
|
21
|
+
return policy !== "subtle" && resolveEnabled(policy, prefersReducedMotion);
|
|
22
|
+
}
|
|
20
23
|
function MotionProvider({
|
|
21
24
|
children,
|
|
22
25
|
defaultPolicy = "system",
|
|
@@ -51,12 +54,47 @@ function MotionProvider({
|
|
|
51
54
|
policy,
|
|
52
55
|
setPolicy,
|
|
53
56
|
prefersReducedMotion,
|
|
54
|
-
enabled: resolveEnabled(policy, prefersReducedMotion)
|
|
57
|
+
enabled: resolveEnabled(policy, prefersReducedMotion),
|
|
58
|
+
expressive: resolveExpressive(policy, prefersReducedMotion)
|
|
55
59
|
}),
|
|
56
60
|
[policy, setPolicy, prefersReducedMotion]
|
|
57
61
|
);
|
|
58
62
|
return /* @__PURE__ */ jsx(MotionContext.Provider, { value, children });
|
|
59
63
|
}
|
|
64
|
+
var LEVEL_RANK = { off: 0, subtle: 1, full: 2 };
|
|
65
|
+
function lowerLevel(a, b) {
|
|
66
|
+
return LEVEL_RANK[a] <= LEVEL_RANK[b] ? a : b;
|
|
67
|
+
}
|
|
68
|
+
function contextLevel(ctx) {
|
|
69
|
+
if (!ctx.enabled) return "off";
|
|
70
|
+
return ctx.expressive ? "full" : "subtle";
|
|
71
|
+
}
|
|
72
|
+
function motionAttribute(level) {
|
|
73
|
+
return level === void 0 || level === "full" ? void 0 : level;
|
|
74
|
+
}
|
|
75
|
+
function useMotionLevel(level) {
|
|
76
|
+
const ctx = useMotion();
|
|
77
|
+
const resolved = lowerLevel(contextLevel(ctx), level ?? "full");
|
|
78
|
+
return {
|
|
79
|
+
level: resolved,
|
|
80
|
+
enabled: resolved !== "off",
|
|
81
|
+
expressive: resolved === "full",
|
|
82
|
+
attribute: motionAttribute(level)
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
function MotionScope({ children, level }) {
|
|
86
|
+
const parent = useMotion();
|
|
87
|
+
const resolved = lowerLevel(contextLevel(parent), level);
|
|
88
|
+
const value = useMemo(
|
|
89
|
+
() => ({
|
|
90
|
+
...parent,
|
|
91
|
+
enabled: resolved !== "off",
|
|
92
|
+
expressive: resolved === "full"
|
|
93
|
+
}),
|
|
94
|
+
[parent, resolved]
|
|
95
|
+
);
|
|
96
|
+
return /* @__PURE__ */ jsx(MotionContext.Provider, { value, children: /* @__PURE__ */ jsx("div", { "data-motion": level, style: { display: "contents" }, children }) });
|
|
97
|
+
}
|
|
60
98
|
function useMotion() {
|
|
61
99
|
const ctx = useContext(MotionContext);
|
|
62
100
|
const prefersReducedMotion = useReducedMotion();
|
|
@@ -66,10 +104,14 @@ function useMotion() {
|
|
|
66
104
|
setPolicy: () => {
|
|
67
105
|
},
|
|
68
106
|
prefersReducedMotion,
|
|
69
|
-
enabled: resolveEnabled("system", prefersReducedMotion)
|
|
107
|
+
enabled: resolveEnabled("system", prefersReducedMotion),
|
|
108
|
+
expressive: resolveExpressive("system", prefersReducedMotion)
|
|
70
109
|
};
|
|
71
110
|
}
|
|
72
111
|
export {
|
|
73
112
|
MotionProvider,
|
|
74
|
-
|
|
113
|
+
MotionScope,
|
|
114
|
+
motionAttribute,
|
|
115
|
+
useMotion,
|
|
116
|
+
useMotionLevel
|
|
75
117
|
};
|
package/dist/motion/Reveal.js
CHANGED
|
@@ -18,7 +18,7 @@ function Reveal({
|
|
|
18
18
|
...props
|
|
19
19
|
}) {
|
|
20
20
|
const Comp = as ?? "div";
|
|
21
|
-
const { enabled } = useMotion();
|
|
21
|
+
const { expressive: enabled } = useMotion();
|
|
22
22
|
const { ref, inView } = useInView({ once, amount, rootMargin });
|
|
23
23
|
const shown = inView && when || !enabled;
|
|
24
24
|
return /* @__PURE__ */ jsx(
|
|
@@ -18,7 +18,7 @@ function TextReveal({
|
|
|
18
18
|
...props
|
|
19
19
|
}) {
|
|
20
20
|
const Comp = as ?? "span";
|
|
21
|
-
const { enabled } = useMotion();
|
|
21
|
+
const { expressive: enabled } = useMotion();
|
|
22
22
|
const { ref, inView } = useInView({ once, amount, rootMargin });
|
|
23
23
|
const shown = inView && when || !enabled;
|
|
24
24
|
const units = split === "char" ? typeof Intl.Segmenter === "function" ? Array.from(new Intl.Segmenter(void 0, { granularity: "grapheme" }).segment(text), ({ segment }) => segment) : Array.from(text) : text.split(/(\s+)/).filter(Boolean);
|
package/dist/motion/index.js
CHANGED
|
@@ -1,6 +1,14 @@
|
|
|
1
1
|
// src/motion/index.ts
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
MotionProvider,
|
|
4
|
+
MotionScope,
|
|
5
|
+
motionAttribute,
|
|
6
|
+
useMotion,
|
|
7
|
+
useMotionLevel
|
|
8
|
+
} from "./MotionProvider.js";
|
|
3
9
|
import { useReducedMotion } from "./useReducedMotion.js";
|
|
10
|
+
import { useShake } from "./useShake.js";
|
|
11
|
+
import { useAutoSize } from "./useAutoSize.js";
|
|
4
12
|
import { useInView } from "./useInView.js";
|
|
5
13
|
import { Reveal } from "./Reveal.js";
|
|
6
14
|
import { TextReveal } from "./TextReveal.js";
|
|
@@ -12,13 +20,18 @@ import {
|
|
|
12
20
|
} from "./viewTransition.js";
|
|
13
21
|
export {
|
|
14
22
|
MotionProvider,
|
|
23
|
+
MotionScope,
|
|
15
24
|
Presence,
|
|
16
25
|
Reveal,
|
|
17
26
|
TextReveal,
|
|
18
27
|
ViewTransitionName,
|
|
28
|
+
motionAttribute,
|
|
19
29
|
startViewTransition,
|
|
20
30
|
supportsViewTransitions,
|
|
31
|
+
useAutoSize,
|
|
21
32
|
useInView,
|
|
22
33
|
useMotion,
|
|
23
|
-
|
|
34
|
+
useMotionLevel,
|
|
35
|
+
useReducedMotion,
|
|
36
|
+
useShake
|
|
24
37
|
};
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
// src/motion/useAutoSize.ts
|
|
4
|
+
import { useLayoutEffect, useRef } from "react";
|
|
5
|
+
function useAutoSize({ axis = "width", enabled = true } = {}) {
|
|
6
|
+
const outerRef = useRef(null);
|
|
7
|
+
const innerRef = useRef(null);
|
|
8
|
+
useLayoutEffect(() => {
|
|
9
|
+
const outer = outerRef.current;
|
|
10
|
+
const inner = innerRef.current;
|
|
11
|
+
if (!enabled || !outer || !inner || typeof ResizeObserver === "undefined") return;
|
|
12
|
+
let first = true;
|
|
13
|
+
const apply = () => {
|
|
14
|
+
const style = getComputedStyle(outer);
|
|
15
|
+
const px = (value) => Number.parseFloat(value) || 0;
|
|
16
|
+
const extraX = px(style.paddingLeft) + px(style.paddingRight) + px(style.borderLeftWidth) + px(style.borderRightWidth);
|
|
17
|
+
const extraY = px(style.paddingTop) + px(style.paddingBottom) + px(style.borderTopWidth) + px(style.borderBottomWidth);
|
|
18
|
+
const width = inner.offsetWidth || inner.getBoundingClientRect().width;
|
|
19
|
+
const height = inner.offsetHeight || inner.getBoundingClientRect().height;
|
|
20
|
+
if (first) outer.style.transition = "none";
|
|
21
|
+
if (axis !== "height") outer.style.width = `${width + extraX}px`;
|
|
22
|
+
if (axis !== "width") outer.style.height = `${height + extraY}px`;
|
|
23
|
+
if (first) {
|
|
24
|
+
void outer.offsetWidth;
|
|
25
|
+
outer.style.transition = "";
|
|
26
|
+
first = false;
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
apply();
|
|
30
|
+
const observer = new ResizeObserver(apply);
|
|
31
|
+
observer.observe(inner);
|
|
32
|
+
return () => {
|
|
33
|
+
observer.disconnect();
|
|
34
|
+
outer.style.width = "";
|
|
35
|
+
outer.style.height = "";
|
|
36
|
+
};
|
|
37
|
+
}, [axis, enabled]);
|
|
38
|
+
return { outerRef, innerRef };
|
|
39
|
+
}
|
|
40
|
+
export {
|
|
41
|
+
useAutoSize
|
|
42
|
+
};
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
// src/motion/useShake.ts
|
|
4
|
+
import { useCallback, useEffect, useRef, useState } from "react";
|
|
5
|
+
function useShake() {
|
|
6
|
+
const [shaking, setShaking] = useState(false);
|
|
7
|
+
const frame = useRef(0);
|
|
8
|
+
useEffect(() => () => cancelAnimationFrame(frame.current), []);
|
|
9
|
+
const shake = useCallback(() => {
|
|
10
|
+
cancelAnimationFrame(frame.current);
|
|
11
|
+
setShaking(false);
|
|
12
|
+
frame.current = requestAnimationFrame(() => {
|
|
13
|
+
frame.current = requestAnimationFrame(() => setShaking(true));
|
|
14
|
+
});
|
|
15
|
+
}, []);
|
|
16
|
+
const onAnimationEnd = useCallback((event) => {
|
|
17
|
+
if (event.target === event.currentTarget && event.animationName === "sable-shake") {
|
|
18
|
+
setShaking(false);
|
|
19
|
+
}
|
|
20
|
+
}, []);
|
|
21
|
+
return { shake, shakeProps: { "data-shake": shaking ? "" : void 0, onAnimationEnd } };
|
|
22
|
+
}
|
|
23
|
+
export {
|
|
24
|
+
useShake
|
|
25
|
+
};
|
package/dist/patterns/index.d.ts
CHANGED