beez-ui 2.6.2 → 2.7.1
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 +29 -0
- package/README.md +47 -27
- package/dist/components/alert-dialog.d.ts +2 -2
- package/dist/components/alert-dialog.js +3 -3
- package/dist/components/dialog-surface.js +1 -1
- package/dist/components/dialog.d.ts +5 -4
- package/dist/components/dialog.js +7 -6
- package/dist/components/rich-link-editor.js +1 -1
- package/dist/components/sidebar.js +4 -9
- package/dist/motion/arc-tokens.d.ts +5 -1
- package/dist/motion/arc-tokens.js +19 -3
- package/dist/motion/glide-indicator.d.ts +4 -6
- package/dist/motion/glide-indicator.js +61 -23
- package/dist/motion/motion-slot.js +30 -61
- package/dist/motion/surface-keyframes.d.ts +12 -1
- package/dist/motion/surface-keyframes.js +48 -83
- package/dist/motion/tokens.d.ts +97 -61
- package/dist/motion/tokens.js +95 -99
- package/dist/styles.css +1 -1
- package/package.json +1 -1
|
@@ -6,8 +6,8 @@ import { Slot } from "radix-ui";
|
|
|
6
6
|
import { animate, hover, press, usePresence, frame } from "motion/react";
|
|
7
7
|
import { usePrefersReducedMotion } from "../hooks/use-prefers-reduced-motion.js";
|
|
8
8
|
import { HOVER_CAPABLE_QUERY, MILLISECONDS_PER_SECOND, MOTION_ENTERING_ATTRIBUTE } from "../constants/motion.js";
|
|
9
|
-
import { MOTION_EASE, MOTION_TIMING, MOTION_PRESS_SCALE, MOTION_SUBTLE_PRESS_SCALE, MOTION_TOGGLE_PRESS_SCALE, MOTION_HOVER_SCALE, MOTION_THUMB_SQUISH_SCALE, MOTION_CONTENT_DISTANCE,
|
|
10
|
-
import { surfaceEnter, surfaceExit } from "./surface-keyframes.js";
|
|
9
|
+
import { MOTION_EASE, MOTION_TIMING, MOTION_PRESS_SCALE, MOTION_SUBTLE_PRESS_SCALE, MOTION_TOGGLE_PRESS_SCALE, MOTION_HOVER_SCALE, MOTION_THUMB_SQUISH_SCALE, MOTION_CONTENT_DISTANCE, MOTION_ITEM_CHECK_SCALE, MOTION_ITEM_ENTER_SCALE, MOTION_ICON_SWAP_SCALE, MOTION_CLOSE_BUTTON_SCALE, MOTION_EASE_IN_OUT, MOTION_REVEAL_BLUR_PX, MOTION_REVEAL_SCALE, MOTION_SHIMMER_TRANSLATE, MOTION_CARD_SHADOW_LIFT, MOTION_CARD_SHADOW_REST, SPRING_CHEVRON, SPRING_PANEL, SPRING_POP, SPRING_PRESS, SPRING_THUMB } from "./tokens.js";
|
|
10
|
+
import { surfaceEnter, surfaceExit, withRestingStyle } from "./surface-keyframes.js";
|
|
11
11
|
import { useAttachedElement } from "./use-attached-element.js";
|
|
12
12
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
13
13
|
const EASE_OUT = [...MOTION_EASE];
|
|
@@ -33,8 +33,6 @@ const ANIMATED_PROPERTIES = [
|
|
|
33
33
|
"filter",
|
|
34
34
|
"clipPath"
|
|
35
35
|
];
|
|
36
|
-
/** Input groups draw the field frame; their controls are borderless. */
|
|
37
|
-
const INPUT_GROUP_SLOT = "input-group";
|
|
38
36
|
/** SVG attributes Motion writes to draw a stroke with `pathLength`. */
|
|
39
37
|
const STROKE_DRAW_ATTRIBUTES = [
|
|
40
38
|
"pathLength",
|
|
@@ -44,7 +42,6 @@ const STROKE_DRAW_ATTRIBUTES = [
|
|
|
44
42
|
/** Attributes whose changes re-announce a field's state with a subtle settle. */
|
|
45
43
|
const FIELD_STATE_ATTRIBUTES = [
|
|
46
44
|
"data-state",
|
|
47
|
-
"aria-invalid",
|
|
48
45
|
"aria-selected",
|
|
49
46
|
"aria-checked",
|
|
50
47
|
"data-loading",
|
|
@@ -87,14 +84,6 @@ function rotationOf(transform) {
|
|
|
87
84
|
const { a, b } = matrixOf(transform);
|
|
88
85
|
return Math.atan2(b, a) * 180 / Math.PI;
|
|
89
86
|
}
|
|
90
|
-
/** Position of an item among its siblings of the same slot inside the closest list or menu. */
|
|
91
|
-
function listIndexOf(element) {
|
|
92
|
-
const slot = element.getAttribute("data-slot");
|
|
93
|
-
const list = element.closest("[role=\"listbox\"],[role=\"menu\"],[role=\"list\"]");
|
|
94
|
-
if (!slot || !list) return 0;
|
|
95
|
-
const items = Array.from(list.querySelectorAll(`[data-slot="${slot}"]`));
|
|
96
|
-
return Math.max(items.indexOf(element), 0);
|
|
97
|
-
}
|
|
98
87
|
/** Checked and indeterminate indicators are visible; only unchecked ones are hidden. */
|
|
99
88
|
function isChecked(element) {
|
|
100
89
|
const state = element.getAttribute("data-state");
|
|
@@ -192,13 +181,18 @@ export function MotionSlot(t0) {
|
|
|
192
181
|
snapshot[property_1] = value;
|
|
193
182
|
}
|
|
194
183
|
});
|
|
195
|
-
interruptedRef.current =
|
|
184
|
+
interruptedRef.current = {
|
|
185
|
+
element,
|
|
186
|
+
values: snapshot
|
|
187
|
+
};
|
|
196
188
|
}
|
|
197
189
|
cleanups.forEach(_temp);
|
|
198
190
|
cancel();
|
|
199
191
|
restore();
|
|
200
192
|
element.removeAttribute(MOTION_ENTERING_ATTRIBUTE);
|
|
201
193
|
};
|
|
194
|
+
const interrupted = interruptedRef.current;
|
|
195
|
+
interruptedRef.current = null;
|
|
202
196
|
if (reduceMotion || !isPresent && element.hasAttribute("data-beez-theme-menu")) {
|
|
203
197
|
if (!isPresent) {
|
|
204
198
|
queueMicrotask(() => {
|
|
@@ -253,13 +247,15 @@ export function MotionSlot(t0) {
|
|
|
253
247
|
});
|
|
254
248
|
};
|
|
255
249
|
const tween = _temp2;
|
|
256
|
-
const interrupted = interruptedRef.current;
|
|
257
|
-
interruptedRef.current = null;
|
|
258
250
|
if (SURFACE_KINDS.includes(kind)) {
|
|
259
251
|
const step = isPresent ? surfaceEnter(element, kind) : surfaceExit(element, kind);
|
|
260
|
-
const
|
|
261
|
-
|
|
262
|
-
|
|
252
|
+
const computed_0 = getComputedStyle(element);
|
|
253
|
+
const keyframes_0 = withRestingStyle(step.keyframes, {
|
|
254
|
+
transform: computed_0.transform,
|
|
255
|
+
opacity: Number.parseFloat(computed_0.opacity)
|
|
256
|
+
});
|
|
257
|
+
if (interrupted?.element === element) {
|
|
258
|
+
Object.entries(interrupted.values).forEach((t10) => {
|
|
263
259
|
const [property_8, value_0] = t10;
|
|
264
260
|
const frames_0 = keyframes_0[property_8];
|
|
265
261
|
if (!Array.isArray(frames_0) || !value_0) {
|
|
@@ -298,11 +294,10 @@ export function MotionSlot(t0) {
|
|
|
298
294
|
if (kind === "icon-swap") {
|
|
299
295
|
play({
|
|
300
296
|
opacity: [0, 1],
|
|
301
|
-
transform: [`scale(${MOTION_ICON_SWAP_SCALE})`, "scale(1)"]
|
|
302
|
-
filter: ["blur(8px)", "blur(0px)"]
|
|
297
|
+
transform: [`scale(${MOTION_ICON_SWAP_SCALE})`, "scale(1)"]
|
|
303
298
|
}, {
|
|
304
|
-
|
|
305
|
-
|
|
299
|
+
...SPRING_POP,
|
|
300
|
+
opacity: tween(MOTION_TIMING.iconSwap)
|
|
306
301
|
});
|
|
307
302
|
} else {
|
|
308
303
|
if (kind === "item-check") {
|
|
@@ -315,16 +310,10 @@ export function MotionSlot(t0) {
|
|
|
315
310
|
});
|
|
316
311
|
} else {
|
|
317
312
|
if (kind === "list-item") {
|
|
318
|
-
|
|
319
|
-
const position = Math.min(index, MOTION_LIST_STAGGER_LIMIT);
|
|
320
|
-
play(index < MOTION_LIST_STAGGER_LIMIT ? {
|
|
313
|
+
play({
|
|
321
314
|
opacity: [0, 1],
|
|
322
|
-
transform: [`
|
|
323
|
-
|
|
324
|
-
} : { opacity: [0, 1] }, {
|
|
325
|
-
...tween(MOTION_TIMING.listItem),
|
|
326
|
-
delay: MOTION_TIMING.listDelay + position * MOTION_TIMING.listStagger
|
|
327
|
-
});
|
|
315
|
+
transform: [`scale(${MOTION_ITEM_ENTER_SCALE})`, "scale(1)"]
|
|
316
|
+
}, tween(MOTION_TIMING.listItem));
|
|
328
317
|
} else {
|
|
329
318
|
if (kind === "delayed-pop" && typeof element.animate === "function") {
|
|
330
319
|
const pop = element.animate({
|
|
@@ -356,8 +345,7 @@ export function MotionSlot(t0) {
|
|
|
356
345
|
if (kind === "message") {
|
|
357
346
|
play({
|
|
358
347
|
opacity: [0, 1],
|
|
359
|
-
transform: [`
|
|
360
|
-
filter: ["blur(4px)", "blur(0px)"]
|
|
348
|
+
transform: [`scale(${MOTION_ITEM_ENTER_SCALE})`, "scale(1)"]
|
|
361
349
|
}, tween(MOTION_TIMING.message));
|
|
362
350
|
} else {
|
|
363
351
|
if (kind === "fade" || kind === "card" || kind === "row") {
|
|
@@ -450,26 +438,10 @@ export function MotionSlot(t0) {
|
|
|
450
438
|
};
|
|
451
439
|
element.addEventListener("focusin", settleField);
|
|
452
440
|
cleanups.push(() => element.removeEventListener("focusin", settleField));
|
|
453
|
-
const
|
|
454
|
-
const isGroupControl = !isGroup && element.parentElement?.closest(`[data-slot="${INPUT_GROUP_SLOT}"]`) != null;
|
|
455
|
-
const readInvalid = () => isGroup ? element.querySelector("[data-slot][aria-invalid=\"true\"]") !== null : element.getAttribute("aria-invalid") === "true";
|
|
456
|
-
let wasInvalid = readInvalid();
|
|
457
|
-
const observer = new MutationObserver((records) => {
|
|
458
|
-
const isInvalid = readInvalid();
|
|
459
|
-
const becameInvalid = isInvalid && !wasInvalid;
|
|
460
|
-
wasInvalid = isInvalid;
|
|
461
|
-
if (becameInvalid && !isGroupControl && !element.hasAttribute("hidden")) {
|
|
462
|
-
play({ transform: MOTION_INVALID_SHAKE_PX.map(_temp3) }, { duration: MOTION_TIMING.shake });
|
|
463
|
-
} else {
|
|
464
|
-
if (records.some((record) => record.target === element && record.attributeName !== "aria-invalid")) {
|
|
465
|
-
settleField();
|
|
466
|
-
}
|
|
467
|
-
}
|
|
468
|
-
});
|
|
441
|
+
const observer = new MutationObserver(settleField);
|
|
469
442
|
observer.observe(element, {
|
|
470
443
|
attributes: true,
|
|
471
|
-
attributeFilter: FIELD_STATE_ATTRIBUTES
|
|
472
|
-
subtree: isGroup
|
|
444
|
+
attributeFilter: FIELD_STATE_ATTRIBUTES
|
|
473
445
|
});
|
|
474
446
|
cleanups.push(() => observer.disconnect());
|
|
475
447
|
}
|
|
@@ -477,9 +449,9 @@ export function MotionSlot(t0) {
|
|
|
477
449
|
let wasChecked = isChecked(element);
|
|
478
450
|
const strokeDraws = new Set();
|
|
479
451
|
const settleStrokes = () => {
|
|
480
|
-
strokeDraws.forEach(
|
|
452
|
+
strokeDraws.forEach(_temp3);
|
|
481
453
|
strokeDraws.clear();
|
|
482
|
-
element.querySelectorAll("path").forEach(
|
|
454
|
+
element.querySelectorAll("path").forEach(_temp4);
|
|
483
455
|
};
|
|
484
456
|
cleanups.push(settleStrokes);
|
|
485
457
|
const observer_0 = new MutationObserver(() => {
|
|
@@ -644,7 +616,7 @@ export function MotionSlot(t0) {
|
|
|
644
616
|
release.cancel();
|
|
645
617
|
squish = null;
|
|
646
618
|
}
|
|
647
|
-
}).catch(
|
|
619
|
+
}).catch(_temp5);
|
|
648
620
|
};
|
|
649
621
|
}));
|
|
650
622
|
}
|
|
@@ -697,16 +669,13 @@ export function MotionSlot(t0) {
|
|
|
697
669
|
}
|
|
698
670
|
return t6;
|
|
699
671
|
}
|
|
700
|
-
function
|
|
701
|
-
function
|
|
672
|
+
function _temp5() {}
|
|
673
|
+
function _temp4(path) {
|
|
702
674
|
STROKE_DRAW_ATTRIBUTES.forEach((attribute) => path.removeAttribute(attribute));
|
|
703
675
|
}
|
|
704
|
-
function
|
|
676
|
+
function _temp3(draw) {
|
|
705
677
|
return draw.cancel();
|
|
706
678
|
}
|
|
707
|
-
function _temp3(offset) {
|
|
708
|
-
return `translateX(${offset}px)`;
|
|
709
|
-
}
|
|
710
679
|
function _temp2(duration) {
|
|
711
680
|
return {
|
|
712
681
|
duration,
|
|
@@ -7,7 +7,18 @@ export interface MotionStep {
|
|
|
7
7
|
keyframes: DOMKeyframesDefinition;
|
|
8
8
|
transition: AnimationOptions;
|
|
9
9
|
}
|
|
10
|
-
/** Entrance playback
|
|
10
|
+
/** Entrance playback for each surface kind, on Arc's motion presets. */
|
|
11
11
|
export declare function surfaceEnter(element: Element, kind: SurfaceKind): MotionStep;
|
|
12
|
+
/** Transform and opacity a surface rests at, which its caller may set through a class or style. */
|
|
13
|
+
export interface RestingStyle {
|
|
14
|
+
transform: string;
|
|
15
|
+
opacity: number;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Composes surface playback with the surface's resting transform and opacity, so a caller-owned
|
|
19
|
+
* tilt, offset or translucency holds through the whole entrance and exit instead of being
|
|
20
|
+
* replaced by identity and full opacity, then snapping back once playback restores it.
|
|
21
|
+
*/
|
|
22
|
+
export declare function withRestingStyle(keyframes: DOMKeyframesDefinition, resting: RestingStyle): DOMKeyframesDefinition;
|
|
12
23
|
/** Exit playback: brief and eased so dismissal never blocks the next interaction. */
|
|
13
24
|
export declare function surfaceExit(element: Element, kind: SurfaceKind): MotionStep;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { MOTION_DIALOG_DISTANCE, MOTION_DIALOG_EXIT_DISTANCE, MOTION_EASE, MOTION_EASE_DRAWER, MOTION_EASE_STANDARD, MOTION_EXIT_SCALE, MOTION_SURFACE_CLIP_BLEED_PX, MOTION_SURFACE_CLIP_PERCENT, MOTION_SURFACE_SCALE, MOTION_TIMING, MOTION_TOOLTIP_DISTANCE, MOTION_TOOLTIP_SCALE, SPRING_PANEL } from "./tokens.js";
|
|
2
2
|
const EASE_OUT = [...MOTION_EASE];
|
|
3
3
|
const EASE_DRAWER = [...MOTION_EASE_DRAWER];
|
|
4
|
-
const
|
|
4
|
+
const EASE_STANDARD = [...MOTION_EASE_STANDARD];
|
|
5
5
|
const SIDES = [
|
|
6
6
|
"top",
|
|
7
7
|
"right",
|
|
@@ -70,45 +70,14 @@ function collapsedClip(side, align) {
|
|
|
70
70
|
const [top, bottom] = crossAxis(align);
|
|
71
71
|
return side === "right" ? `inset(${top} ${hidden} ${bottom} ${OPEN_EDGE})` : `inset(${top} ${OPEN_EDGE} ${bottom} ${hidden})`;
|
|
72
72
|
}
|
|
73
|
-
/** Computed radius of each corner, clockwise from the top left, as border-radius serializes it. */
|
|
74
|
-
const CORNER_RADIUS_PROPERTIES = [
|
|
75
|
-
"borderTopLeftRadius",
|
|
76
|
-
"borderTopRightRadius",
|
|
77
|
-
"borderBottomRightRadius",
|
|
78
|
-
"borderBottomLeftRadius"
|
|
79
|
-
];
|
|
80
|
-
/**
|
|
81
|
-
* Builds the `round` part of an inset clip that repeats the surface's own corners, each grown by
|
|
82
|
-
* the bleed so the clip edge stays parallel to the border. A corner serializes as one radius or
|
|
83
|
-
* as a horizontal and vertical pair (elliptical), in pixels or percentages of the box.
|
|
84
|
-
*/
|
|
85
|
-
function bleedingCornerRadii(element) {
|
|
86
|
-
const computed = getComputedStyle(element);
|
|
87
|
-
const grow = (radius) => `calc(${radius} + ${MOTION_SURFACE_CLIP_BLEED_PX}px)`;
|
|
88
|
-
const corners = CORNER_RADIUS_PROPERTIES.map((property) => {
|
|
89
|
-
const [horizontal = "0px", vertical = horizontal] = computed[property].trim().split(/\s+/);
|
|
90
|
-
return {
|
|
91
|
-
horizontal: grow(horizontal),
|
|
92
|
-
vertical: grow(vertical)
|
|
93
|
-
};
|
|
94
|
-
});
|
|
95
|
-
const horizontals = corners.map((corner) => corner.horizontal).join(" ");
|
|
96
|
-
const verticals = corners.map((corner) => corner.vertical).join(" ");
|
|
97
|
-
return `round ${horizontals} / ${verticals}`;
|
|
98
|
-
}
|
|
99
73
|
/**
|
|
100
|
-
*
|
|
101
|
-
*
|
|
102
|
-
* rounding corners.
|
|
74
|
+
* Arc's dialog leave: a short tween on the standard curve, so it never lingers and a reopen
|
|
75
|
+
* retargets from wherever the panel is.
|
|
103
76
|
*/
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
folded: `inset(${folded} ${folded} ${folded} ${folded} ${round})`,
|
|
109
|
-
unfolded: `inset(${OPEN_EDGE} ${OPEN_EDGE} ${OPEN_EDGE} ${OPEN_EDGE} ${round})`
|
|
110
|
-
};
|
|
111
|
-
}
|
|
77
|
+
const DIALOG_LEAVE = {
|
|
78
|
+
duration: MOTION_TIMING.fast,
|
|
79
|
+
ease: EASE_STANDARD
|
|
80
|
+
};
|
|
112
81
|
/** Edge-panel travel: fully off-screen on the side the sheet is attached to. */
|
|
113
82
|
function sheetOffset(element) {
|
|
114
83
|
const side = readPlacement(element).side ?? "right";
|
|
@@ -129,7 +98,7 @@ function sheetOffset(element) {
|
|
|
129
98
|
y: "0%"
|
|
130
99
|
};
|
|
131
100
|
}
|
|
132
|
-
/** Entrance playback
|
|
101
|
+
/** Entrance playback for each surface kind, on Arc's motion presets. */
|
|
133
102
|
export function surfaceEnter(element, kind) {
|
|
134
103
|
const { side, align } = readPlacement(element);
|
|
135
104
|
if (kind === "overlay") {
|
|
@@ -142,42 +111,33 @@ export function surfaceEnter(element, kind) {
|
|
|
142
111
|
};
|
|
143
112
|
}
|
|
144
113
|
if (kind === "tooltip") {
|
|
114
|
+
// Arc's tooltip: a short rise from its trigger and a fade, without blur.
|
|
145
115
|
const offset = offsetAwayFromTrigger(side, MOTION_TOOLTIP_DISTANCE);
|
|
146
116
|
return {
|
|
147
117
|
keyframes: {
|
|
148
118
|
opacity: [0, 1],
|
|
149
|
-
transform: [transform(offset.x, offset.y,
|
|
150
|
-
filter: ["blur(5px)", "blur(0px)"]
|
|
119
|
+
transform: [transform(offset.x, offset.y, MOTION_TOOLTIP_SCALE), transform(0, 0)]
|
|
151
120
|
},
|
|
152
121
|
transition: {
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
duration: MOTION_TIMING.fast,
|
|
156
|
-
ease: EASE_OUT
|
|
157
|
-
},
|
|
158
|
-
filter: {
|
|
159
|
-
duration: MOTION_TIMING.enter,
|
|
160
|
-
ease: EASE_OUT
|
|
161
|
-
}
|
|
122
|
+
duration: MOTION_TIMING.fast,
|
|
123
|
+
ease: EASE_OUT
|
|
162
124
|
}
|
|
163
125
|
};
|
|
164
126
|
}
|
|
165
|
-
|
|
127
|
+
// The deprecated center morph now plays Arc's dialog transition, like the library's own dialogs.
|
|
128
|
+
if (kind === "dialog" || kind === "center-morph") {
|
|
129
|
+
// Arc's dialog: the panel rises and scales in on the smooth spring while it fades in quickly.
|
|
166
130
|
return {
|
|
167
131
|
keyframes: {
|
|
168
132
|
opacity: [0, 1],
|
|
169
|
-
transform: [transform(0, MOTION_DIALOG_DISTANCE,
|
|
133
|
+
transform: [transform(0, MOTION_DIALOG_DISTANCE, MOTION_SURFACE_SCALE), transform(0, 0)]
|
|
170
134
|
},
|
|
171
|
-
transition: SPRING_PANEL
|
|
172
|
-
};
|
|
173
|
-
}
|
|
174
|
-
if (kind === "center-morph") {
|
|
175
|
-
const { folded, unfolded } = centerMorphClips(element);
|
|
176
|
-
return {
|
|
177
|
-
keyframes: { clipPath: [folded, unfolded] },
|
|
178
135
|
transition: {
|
|
179
|
-
|
|
180
|
-
|
|
136
|
+
...SPRING_PANEL,
|
|
137
|
+
opacity: {
|
|
138
|
+
duration: MOTION_TIMING.fast,
|
|
139
|
+
ease: EASE_OUT
|
|
140
|
+
}
|
|
181
141
|
}
|
|
182
142
|
};
|
|
183
143
|
}
|
|
@@ -211,6 +171,22 @@ export function surfaceEnter(element, kind) {
|
|
|
211
171
|
}
|
|
212
172
|
};
|
|
213
173
|
}
|
|
174
|
+
/**
|
|
175
|
+
* Composes surface playback with the surface's resting transform and opacity, so a caller-owned
|
|
176
|
+
* tilt, offset or translucency holds through the whole entrance and exit instead of being
|
|
177
|
+
* replaced by identity and full opacity, then snapping back once playback restores it.
|
|
178
|
+
*/
|
|
179
|
+
export function withRestingStyle(keyframes, resting) {
|
|
180
|
+
const composed = { ...keyframes };
|
|
181
|
+
const { transform, opacity } = composed;
|
|
182
|
+
if (Array.isArray(transform) && resting.transform && resting.transform !== "none") {
|
|
183
|
+
composed.transform = transform.map((value) => `${String(value)} ${resting.transform}`);
|
|
184
|
+
}
|
|
185
|
+
if (Array.isArray(opacity) && Number.isFinite(resting.opacity) && resting.opacity !== 1) {
|
|
186
|
+
composed.opacity = opacity.map((value) => Number(value) * resting.opacity);
|
|
187
|
+
}
|
|
188
|
+
return composed;
|
|
189
|
+
}
|
|
214
190
|
/** Exit playback: brief and eased so dismissal never blocks the next interaction. */
|
|
215
191
|
export function surfaceExit(element, kind) {
|
|
216
192
|
const exitTween = {
|
|
@@ -220,40 +196,29 @@ export function surfaceExit(element, kind) {
|
|
|
220
196
|
if (kind === "overlay") {
|
|
221
197
|
return {
|
|
222
198
|
keyframes: { opacity: [1, 0] },
|
|
223
|
-
transition:
|
|
199
|
+
transition: DIALOG_LEAVE
|
|
224
200
|
};
|
|
225
201
|
}
|
|
226
202
|
if (kind === "tooltip") {
|
|
227
|
-
|
|
203
|
+
// Arc's tooltip leaves in place, shrinking slightly while it fades, on the instant timing.
|
|
228
204
|
return {
|
|
229
205
|
keyframes: {
|
|
230
206
|
opacity: [1, 0],
|
|
231
|
-
transform: [transform(0, 0), transform(
|
|
232
|
-
filter: ["blur(0px)", "blur(3px)"]
|
|
207
|
+
transform: [transform(0, 0), transform(0, 0, MOTION_EXIT_SCALE)]
|
|
233
208
|
},
|
|
234
|
-
transition:
|
|
209
|
+
transition: {
|
|
210
|
+
duration: MOTION_TIMING.instant,
|
|
211
|
+
ease: EASE_STANDARD
|
|
212
|
+
}
|
|
235
213
|
};
|
|
236
214
|
}
|
|
237
|
-
if (kind === "dialog") {
|
|
215
|
+
if (kind === "dialog" || kind === "center-morph") {
|
|
238
216
|
return {
|
|
239
217
|
keyframes: {
|
|
240
218
|
opacity: [1, 0],
|
|
241
|
-
transform: [transform(0, 0), transform(0,
|
|
219
|
+
transform: [transform(0, 0), transform(0, MOTION_DIALOG_EXIT_DISTANCE, MOTION_EXIT_SCALE)]
|
|
242
220
|
},
|
|
243
|
-
transition:
|
|
244
|
-
duration: MOTION_TIMING.enter,
|
|
245
|
-
ease: EASE_OUT
|
|
246
|
-
}
|
|
247
|
-
};
|
|
248
|
-
}
|
|
249
|
-
if (kind === "center-morph") {
|
|
250
|
-
const { folded, unfolded } = centerMorphClips(element);
|
|
251
|
-
return {
|
|
252
|
-
keyframes: { clipPath: [unfolded, folded] },
|
|
253
|
-
transition: {
|
|
254
|
-
duration: MOTION_TIMING.centerMorphFold,
|
|
255
|
-
ease: EASE_UNFOLD
|
|
256
|
-
}
|
|
221
|
+
transition: DIALOG_LEAVE
|
|
257
222
|
};
|
|
258
223
|
}
|
|
259
224
|
if (kind === "sheet") {
|
package/dist/motion/tokens.d.ts
CHANGED
|
@@ -1,30 +1,49 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Defines the timing, curves and physics shared by Motion animations, aligned with beui.dev/components/motion.
|
|
3
|
-
* Public: consumers animate their own product surfaces with these tokens so they move like the library.
|
|
4
|
-
*/
|
|
5
1
|
export declare const MOTION_TIMING: {
|
|
6
|
-
|
|
7
|
-
readonly
|
|
8
|
-
|
|
9
|
-
readonly
|
|
10
|
-
|
|
11
|
-
readonly
|
|
2
|
+
/** Arc `duration.instant`: color answers and fades that must feel immediate. */
|
|
3
|
+
readonly instant: 0.12;
|
|
4
|
+
/** Arc `duration.fast`: small fades, the opacity of a spring entrance, and dialog exits. */
|
|
5
|
+
readonly fast: 0.16;
|
|
6
|
+
/** Arc `duration.standard`: entrance tweens. */
|
|
7
|
+
readonly enter: 0.24;
|
|
8
|
+
/** Arc `duration.standard`: tween length for a deliberate on-screen change. */
|
|
9
|
+
readonly standard: 0.24;
|
|
10
|
+
/** Arc `duration.considered`: the slowest tween, for large layout changes. */
|
|
11
|
+
readonly considered: 0.48;
|
|
12
|
+
/** Arc `duration.standard`: inline messages that appear next to the content they describe. */
|
|
13
|
+
readonly message: 0.24;
|
|
14
|
+
/** Arc `duration.standard`: opacity of panels whose size moves on a spring. */
|
|
15
|
+
readonly panel: 0.24;
|
|
16
|
+
/** Arc `duration.standard`: scrims fading in behind dialogs and sheets. */
|
|
17
|
+
readonly overlay: 0.24;
|
|
18
|
+
/** Arc `duration.exit`: leaving is faster than arriving. */
|
|
19
|
+
readonly exit: 0.18;
|
|
12
20
|
readonly surfaceClip: 0.32;
|
|
21
|
+
/**
|
|
22
|
+
* @deprecated Invalid fields no longer shake: the error is told by color and message. Kept for consumers that
|
|
23
|
+
* animate their own fields.
|
|
24
|
+
*/
|
|
13
25
|
readonly shake: 0.45;
|
|
14
26
|
readonly checkDraw: 0.3;
|
|
15
27
|
readonly checkDrawDelay: 0.04;
|
|
16
|
-
|
|
17
|
-
readonly
|
|
28
|
+
/** Arc `duration.fast`: the fade of an icon swapped in place; its scale rides `SPRING_POP`. */
|
|
29
|
+
readonly iconSwap: 0.16;
|
|
30
|
+
/** Arc `duration.fast`: options fading in with their list. */
|
|
31
|
+
readonly listItem: 0.16;
|
|
18
32
|
readonly listDelay: 0.05;
|
|
33
|
+
/** Arc `stagger.item`. */
|
|
19
34
|
readonly listStagger: 0.035;
|
|
20
35
|
readonly closeEnter: 0.2;
|
|
21
36
|
readonly closeDelay: 0.16;
|
|
37
|
+
/** Arc `duration.standard`: edge panels leaving the viewport. */
|
|
22
38
|
readonly sheetExit: 0.24;
|
|
23
|
-
/**
|
|
39
|
+
/**
|
|
40
|
+
* @deprecated Dialogs no longer unfold from their center; they rise and scale in on `SPRING_PANEL`. Kept for
|
|
41
|
+
* consumers that animate their own surfaces with it.
|
|
42
|
+
*/
|
|
24
43
|
readonly centerMorph: 0.43;
|
|
25
44
|
/**
|
|
26
|
-
*
|
|
27
|
-
*
|
|
45
|
+
* @deprecated Dialogs no longer fold back to their center; they leave in `MOTION_TIMING.fast`. Kept for consumers
|
|
46
|
+
* that animate their own surfaces with it.
|
|
28
47
|
*/
|
|
29
48
|
readonly centerMorphFold: 0.24;
|
|
30
49
|
readonly reveal: 0.4;
|
|
@@ -32,96 +51,95 @@ export declare const MOTION_TIMING: {
|
|
|
32
51
|
readonly pop: 0.32;
|
|
33
52
|
/** Height of expanding and collapsing regions. */
|
|
34
53
|
readonly collapse: 0.26;
|
|
54
|
+
/** Arc `duration.standard`. */
|
|
35
55
|
readonly monthSwap: 0.24;
|
|
36
56
|
readonly cursorBlink: 1;
|
|
37
57
|
readonly skeleton: 1.8;
|
|
38
58
|
};
|
|
39
59
|
/** Items beyond this position share the last stagger delay, so long lists never lag behind. */
|
|
40
60
|
export declare const MOTION_LIST_STAGGER_LIMIT = 10;
|
|
41
|
-
/**
|
|
61
|
+
/** Arc `ease.enter`: strong ease-out for entrances; weak defaults such as `ease-out` feel sluggish. */
|
|
42
62
|
export declare const MOTION_EASE: readonly [0.16, 1, 0.3, 1];
|
|
43
|
-
/**
|
|
44
|
-
export declare const
|
|
63
|
+
/** Arc `ease.exit`: accelerates away, for elements leaving the screen. */
|
|
64
|
+
export declare const MOTION_EASE_EXIT: readonly [0.7, 0, 0.84, 0];
|
|
65
|
+
/** Arc `ease.standard`: color and opacity changes, and quick exits that must not linger. */
|
|
66
|
+
export declare const MOTION_EASE_STANDARD: readonly [0.22, 1, 0.36, 1];
|
|
67
|
+
/** Arc `ease.inOut`: elements that move while already on screen, and looping effects. */
|
|
68
|
+
export declare const MOTION_EASE_IN_OUT: readonly [0.65, 0, 0.35, 1];
|
|
45
69
|
/** Drawer curve for edge panels leaving the viewport. */
|
|
46
70
|
export declare const MOTION_EASE_DRAWER: readonly [0.32, 0.72, 0, 1];
|
|
47
71
|
/**
|
|
48
72
|
* Near-linear curve for the center morph: a spring or strong ease-out would resolve the clip
|
|
49
73
|
* early and spend the last frames barely moving, so the whole duration reads as unfolding.
|
|
74
|
+
* @deprecated Dialogs no longer unfold from their center. Kept for consumers that animate their own surfaces with it.
|
|
50
75
|
*/
|
|
51
76
|
export declare const MOTION_EASE_UNFOLD: readonly [0.2, 0, 0.2, 1];
|
|
52
|
-
/**
|
|
77
|
+
/** Arc `spring.snappy`: press feedback on buttons and other tappable surfaces. */
|
|
53
78
|
export declare const SPRING_PRESS: {
|
|
54
79
|
readonly type: "spring";
|
|
55
|
-
readonly
|
|
56
|
-
readonly
|
|
57
|
-
readonly mass: 0.6;
|
|
80
|
+
readonly visualDuration: 0.26;
|
|
81
|
+
readonly bounce: 0.12;
|
|
58
82
|
};
|
|
59
|
-
/**
|
|
83
|
+
/** Arc `spring.smooth`: overlay panel entrances (dialogs, sheets and floating surfaces); never overshoots. */
|
|
60
84
|
export declare const SPRING_PANEL: {
|
|
61
85
|
readonly type: "spring";
|
|
62
|
-
readonly
|
|
63
|
-
readonly
|
|
64
|
-
readonly mass: 0.5;
|
|
86
|
+
readonly visualDuration: 0.4;
|
|
87
|
+
readonly bounce: 0;
|
|
65
88
|
};
|
|
66
|
-
/**
|
|
89
|
+
/** Arc `spring.morph`: shared-layout glides, indicators and highlights moving between items. */
|
|
67
90
|
export declare const SPRING_LAYOUT: {
|
|
68
91
|
readonly type: "spring";
|
|
69
|
-
readonly
|
|
70
|
-
readonly
|
|
71
|
-
readonly mass: 0.6;
|
|
92
|
+
readonly visualDuration: 0.42;
|
|
93
|
+
readonly bounce: 0.16;
|
|
72
94
|
};
|
|
73
|
-
/**
|
|
95
|
+
/** Arc `spring.snappy`: keyboard highlight in menus and lists, quick enough to keep up with a held arrow key. */
|
|
74
96
|
export declare const SPRING_HIGHLIGHT: {
|
|
75
97
|
readonly type: "spring";
|
|
76
|
-
readonly
|
|
77
|
-
readonly
|
|
78
|
-
readonly mass: 0.5;
|
|
98
|
+
readonly visualDuration: 0.26;
|
|
99
|
+
readonly bounce: 0.12;
|
|
79
100
|
};
|
|
80
|
-
/**
|
|
101
|
+
/** Arc `spring.morph`: the tab indicator gliding between tabs. */
|
|
81
102
|
export declare const SPRING_TABS: {
|
|
82
103
|
readonly type: "spring";
|
|
83
|
-
readonly
|
|
84
|
-
readonly
|
|
85
|
-
readonly mass: 1.2;
|
|
104
|
+
readonly visualDuration: 0.42;
|
|
105
|
+
readonly bounce: 0.16;
|
|
86
106
|
};
|
|
87
|
-
/**
|
|
107
|
+
/** Arc `spring.snappy`: small tooltip surfaces. */
|
|
88
108
|
export declare const SPRING_TOOLTIP: {
|
|
89
109
|
readonly type: "spring";
|
|
90
|
-
readonly
|
|
91
|
-
readonly
|
|
92
|
-
readonly mass: 0.7;
|
|
110
|
+
readonly visualDuration: 0.26;
|
|
111
|
+
readonly bounce: 0.12;
|
|
93
112
|
};
|
|
94
|
-
/**
|
|
113
|
+
/** Arc `spring.snappy`: disclosure chevrons turning toward their new state. */
|
|
95
114
|
export declare const SPRING_CHEVRON: {
|
|
96
115
|
readonly type: "spring";
|
|
97
|
-
readonly
|
|
98
|
-
readonly bounce: 0.
|
|
116
|
+
readonly visualDuration: 0.26;
|
|
117
|
+
readonly bounce: 0.12;
|
|
99
118
|
};
|
|
100
|
-
/**
|
|
119
|
+
/** Arc `spring.smooth`: the switch thumb reports state, so it lands without overshoot. */
|
|
101
120
|
export declare const SPRING_THUMB: {
|
|
102
121
|
readonly type: "spring";
|
|
103
|
-
readonly
|
|
104
|
-
readonly
|
|
105
|
-
readonly mass: 4;
|
|
122
|
+
readonly visualDuration: 0.4;
|
|
123
|
+
readonly bounce: 0;
|
|
106
124
|
};
|
|
107
|
-
/**
|
|
125
|
+
/** Arc `spring.snappy`: small, confident pops for counters, badges and swapped icons. */
|
|
108
126
|
export declare const SPRING_POP: {
|
|
109
127
|
readonly type: "spring";
|
|
110
|
-
readonly
|
|
111
|
-
readonly
|
|
112
|
-
readonly mass: 0.5;
|
|
128
|
+
readonly visualDuration: 0.26;
|
|
129
|
+
readonly bounce: 0.12;
|
|
113
130
|
};
|
|
114
|
-
/**
|
|
115
|
-
export declare const MOTION_PRESS_SCALE = 0.
|
|
131
|
+
/** Arc's press: about 0.97, so every pressable reads as the same push. */
|
|
132
|
+
export declare const MOTION_PRESS_SCALE = 0.97;
|
|
116
133
|
export declare const MOTION_SUBTLE_PRESS_SCALE = 0.98;
|
|
117
134
|
export declare const MOTION_TOGGLE_PRESS_SCALE = 0.92;
|
|
118
135
|
export declare const MOTION_HOVER_SCALE = 1.02;
|
|
119
136
|
export declare const MOTION_THUMB_SQUISH_SCALE = 0.9;
|
|
120
137
|
export declare const MOTION_ITEM_CHECK_SCALE = 0.75;
|
|
121
|
-
|
|
138
|
+
/** Arc's icon swap: the incoming icon grows from 0.6 on `SPRING_POP`. */
|
|
139
|
+
export declare const MOTION_ICON_SWAP_SCALE = 0.6;
|
|
122
140
|
export declare const MOTION_CLOSE_BUTTON_SCALE = 0.8;
|
|
123
141
|
export declare const MOTION_LIST_ITEM_DISTANCE = 6;
|
|
124
|
-
/** Scale a freshly inserted list item starts from, and a leaving one shrinks to. */
|
|
142
|
+
/** Scale a freshly inserted list item, option or inline message starts from, and a leaving one shrinks to. */
|
|
125
143
|
export declare const MOTION_ITEM_ENTER_SCALE = 0.98;
|
|
126
144
|
/** Loaded images settle from a slight zoom, so they develop in place instead of popping in. */
|
|
127
145
|
export declare const MOTION_REVEAL_SCALE = 1.06;
|
|
@@ -133,17 +151,35 @@ export declare const MOTION_SHIMMER_TRANSLATE: readonly ["-100% 0", "100% 0"];
|
|
|
133
151
|
/** Hovered interactive cards gain a soft drop shadow; the rest value has the same shape, invisible. */
|
|
134
152
|
export declare const MOTION_CARD_SHADOW_LIFT = "0px 4px 16px 0px rgba(0, 0, 0, 0.08)";
|
|
135
153
|
export declare const MOTION_CARD_SHADOW_REST = "0px 0px 0px 0px rgba(0, 0, 0, 0)";
|
|
154
|
+
/** Arc's dialog panel scale on entrance; floating surfaces start from it too. */
|
|
136
155
|
export declare const MOTION_SURFACE_SCALE = 0.96;
|
|
156
|
+
/** Scale a dialog or tooltip shrinks to while it leaves (Arc). */
|
|
157
|
+
export declare const MOTION_EXIT_SCALE = 0.98;
|
|
137
158
|
/** Fraction of the surface kept hidden by the clip at the start of its reveal, in percent. */
|
|
138
159
|
export declare const MOTION_SURFACE_CLIP_PERCENT = 92;
|
|
139
160
|
/** Room left outside a revealing surface for its ring and drop shadow (`shadow-lg` spans ~22px). */
|
|
140
161
|
export declare const MOTION_SURFACE_CLIP_BLEED_PX = 24;
|
|
141
|
-
|
|
142
|
-
export declare const
|
|
143
|
-
/**
|
|
162
|
+
/** Arc's tooltip rise from its trigger, in pixels. */
|
|
163
|
+
export declare const MOTION_TOOLTIP_DISTANCE = 3;
|
|
164
|
+
/** Arc's tooltip scale on entrance. */
|
|
165
|
+
export declare const MOTION_TOOLTIP_SCALE = 0.97;
|
|
166
|
+
/** Arc's dialog rise on entrance, in pixels. */
|
|
167
|
+
export declare const MOTION_DIALOG_DISTANCE = 8;
|
|
168
|
+
/** Arc's dialog drop while it leaves, in pixels. */
|
|
169
|
+
export declare const MOTION_DIALOG_EXIT_DISTANCE = 4;
|
|
170
|
+
/**
|
|
171
|
+
* Inset kept on every side of a center-morph dialog while folded, in percent: a sliver at its center.
|
|
172
|
+
* @deprecated Dialogs no longer unfold from their center. Kept for consumers that animate their own surfaces with it.
|
|
173
|
+
*/
|
|
144
174
|
export declare const MOTION_CENTER_MORPH_FOLDED_PERCENT = 48;
|
|
145
175
|
export declare const MOTION_CONTENT_DISTANCE = 4;
|
|
176
|
+
/**
|
|
177
|
+
* @deprecated Invalid fields no longer shake: the error is told by color and message. Kept for consumers that
|
|
178
|
+
* animate their own fields.
|
|
179
|
+
*/
|
|
146
180
|
export declare const MOTION_INVALID_SHAKE_PX: readonly [0, -6, 6, -4, 4, -2, 0];
|
|
147
181
|
/** Time after an item loses its active state during which a new active item still glides from it. */
|
|
148
182
|
export declare const GLIDE_CONTINUITY_MS = 300;
|
|
149
|
-
export type MotionKind = "press" | "subtle-press" | "toggle" | "fade" | "content" | "message" | "card" | "row" | "field" | "thumb" | "check" | "selection" | "item-check" | "rotate" | "list-item" | "icon-swap" | "delayed-pop" | "skeleton" | "reveal" | "link" | "icon" | "tooltip" | "surface" | "dialog"
|
|
183
|
+
export type MotionKind = "press" | "subtle-press" | "toggle" | "fade" | "content" | "message" | "card" | "row" | "field" | "thumb" | "check" | "selection" | "item-check" | "rotate" | "list-item" | "icon-swap" | "delayed-pop" | "skeleton" | "reveal" | "link" | "icon" | "tooltip" | "surface" | "dialog"
|
|
184
|
+
/** @deprecated Library dialogs use `dialog`. */
|
|
185
|
+
| "center-morph" | "sheet" | "overlay";
|