beez-ui 0.5.6 → 0.6.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.
@@ -1,36 +1,107 @@
1
1
  "use client";
2
2
  import { c as _c } from "react/compiler-runtime";
3
3
  /** Animates the original primitive node with Motion, without adding DOM wrappers. */
4
- import { useLayoutEffect, useState } from "react";
4
+ import { useLayoutEffect, useRef } from "react";
5
5
  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
- import { MOTION_EASE, MOTION_TIMING, MOTION_PRESS_SCALE, MOTION_SURFACE_DISTANCE, MOTION_PANEL_DISTANCE } from "./tokens.js";
8
+ import { HOVER_CAPABLE_QUERY, MILLISECONDS_PER_SECOND } 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, MOTION_INVALID_SHAKE_PX, MOTION_ITEM_CHECK_SCALE, MOTION_ICON_SWAP_SCALE, MOTION_CLOSE_BUTTON_SCALE, MOTION_LIST_ITEM_DISTANCE, MOTION_LIST_STAGGER_LIMIT, MOTION_EASE_IN_OUT, SPRING_CHEVRON, SPRING_PANEL, SPRING_PRESS, SPRING_THUMB } from "./tokens.js";
10
+ import { surfaceEnter, surfaceExit } from "./surface-keyframes.js";
11
+ import { useAttachedElement } from "./use-attached-element.js";
9
12
  import { jsx as _jsx } from "react/jsx-runtime";
13
+ const EASE_OUT = [...MOTION_EASE];
14
+ const SURFACE_KINDS = [
15
+ "tooltip",
16
+ "surface",
17
+ "dialog",
18
+ "sheet",
19
+ "overlay"
20
+ ];
21
+ const PRESS_SCALES = {
22
+ press: MOTION_PRESS_SCALE,
23
+ "subtle-press": MOTION_SUBTLE_PRESS_SCALE,
24
+ toggle: MOTION_TOGGLE_PRESS_SCALE
25
+ };
26
+ const FIELD_KINDS = ["field", "toggle"];
27
+ /** Inline style properties Motion playback may write; they are restored after playback. */
28
+ const ANIMATED_PROPERTIES = [
29
+ "opacity",
30
+ "transform",
31
+ "boxShadow",
32
+ "filter",
33
+ "clipPath"
34
+ ];
35
+ /** Input groups draw the field frame; their controls are borderless. */
36
+ const INPUT_GROUP_SLOT = "input-group";
37
+ /** SVG attributes Motion writes to draw a stroke with `pathLength`. */
38
+ const STROKE_DRAW_ATTRIBUTES = [
39
+ "pathLength",
40
+ "stroke-dasharray",
41
+ "stroke-dashoffset"
42
+ ];
43
+ /** Attributes whose changes re-announce a field's state with a subtle settle. */
44
+ const FIELD_STATE_ATTRIBUTES = [
45
+ "data-state",
46
+ "aria-invalid",
47
+ "aria-selected",
48
+ "aria-checked",
49
+ "data-loading",
50
+ "data-error"
51
+ ];
10
52
  /** Checks the actual DOM state rather than intercepting the component's event handlers. */
11
53
  function isEnabled(element) {
12
54
  return !element.hasAttribute("disabled") && element.getAttribute("aria-disabled") !== "true" && !element.hasAttribute("data-disabled");
13
55
  }
14
- /** Computes a small offset toward the surface's originating edge. */
15
- function entryTransform(element, kind) {
16
- if (kind === "dialog") return "scale(0.985)";
17
- const distance = kind === "sheet" ? MOTION_PANEL_DISTANCE : MOTION_SURFACE_DISTANCE;
18
- const direction = element.getAttribute("data-side");
19
- const offset = kind === "sheet" ? distance : -distance;
20
- if (direction === "left") return `translateX(${-offset}px)`;
21
- if (direction === "right") return `translateX(${offset}px)`;
22
- if (direction === "top") return `translateY(${-offset}px)`;
23
- return `translateY(${offset}px)`;
56
+ /** Touch devices keep phantom hovers after a tap, so hover lifts need a real hover pointer. */
57
+ function canHover() {
58
+ return typeof window.matchMedia === "function" && window.matchMedia(HOVER_CAPABLE_QUERY).matches;
24
59
  }
25
- /** Uses explicit identity transforms so Motion never interprets a missing scale as zero. */
26
- function restingTransform(element, kind) {
27
- if (kind === "dialog") return "scale(1)";
28
- const direction = element.getAttribute("data-side");
29
- return direction === "left" || direction === "right" ? "translateX(0px)" : "translateY(0px)";
60
+ /** Reads the currently rendered transform, including a running animation, as a start keyframe. */
61
+ function currentTransform(element) {
62
+ const value = getComputedStyle(element).transform;
63
+ return value && value !== "none" ? value : "scale(1)";
64
+ }
65
+ /** Reads a computed `matrix()` or `matrix3d()` transform as its 2D components. */
66
+ function matrixOf(transform) {
67
+ const values = /^matrix(3d)?\(([^)]+)\)$/.exec(transform);
68
+ if (!values) return {
69
+ a: 1,
70
+ b: 0,
71
+ translateX: 0
72
+ };
73
+ const parts = values[2].split(",").map(parseFloat);
74
+ return {
75
+ a: parts[0] ?? 1,
76
+ b: parts[1] ?? 0,
77
+ translateX: (values[1] ? parts[12] : parts[4]) ?? 0
78
+ };
79
+ }
80
+ /** Extracts the horizontal translation of a computed transform. */
81
+ function translateXOf(transform) {
82
+ return matrixOf(transform).translateX;
83
+ }
84
+ /** Extracts the rotation, in degrees, of a computed transform. */
85
+ function rotationOf(transform) {
86
+ const { a, b } = matrixOf(transform);
87
+ return Math.atan2(b, a) * 180 / Math.PI;
88
+ }
89
+ /** Position of an item among its siblings of the same slot inside the closest list or menu. */
90
+ function listIndexOf(element) {
91
+ const slot = element.getAttribute("data-slot");
92
+ const list = element.closest("[role=\"listbox\"],[role=\"menu\"],[role=\"list\"]");
93
+ if (!slot || !list) return 0;
94
+ const items = Array.from(list.querySelectorAll(`[data-slot="${slot}"]`));
95
+ return Math.max(items.indexOf(element), 0);
96
+ }
97
+ /** Checked and indeterminate indicators are visible; only unchecked ones are hidden. */
98
+ function isChecked(element) {
99
+ const state = element.getAttribute("data-state");
100
+ return state === "checked" || state === "indeterminate";
30
101
  }
31
102
  /** Composes refs through Radix Slot and delegates gesture, entry and exit playback to Motion. */
32
103
  export function MotionSlot(t0) {
33
- const $ = _c(21);
104
+ const $ = _c(20);
34
105
  let children;
35
106
  let props;
36
107
  let ref;
@@ -49,48 +120,33 @@ export function MotionSlot(t0) {
49
120
  t1 = $[4];
50
121
  }
51
122
  const kind = t1 === undefined ? "fade" : t1;
52
- const [element, setElement] = useState(null);
123
+ const [element, attach] = useAttachedElement(ref);
124
+ const reduceMotion = usePrefersReducedMotion();
125
+ const [isPresent, safeToRemove] = usePresence();
126
+ const safeToRemoveRef = useRef(safeToRemove);
53
127
  let t2;
54
- if ($[5] !== ref) {
55
- t2 = (node) => {
56
- setElement(node);
57
- const cleanup = typeof ref === "function" ? ref(node) : undefined;
58
- if (ref && typeof ref !== "function") {
59
- ref.current = node;
60
- }
61
- return () => {
62
- setElement(null);
63
- if (typeof cleanup === "function") {
64
- cleanup();
65
- } else {
66
- if (typeof ref === "function") {
67
- ref(null);
68
- } else {
69
- if (ref) {
70
- ref.current = null;
71
- }
72
- }
73
- }
74
- };
128
+ if ($[5] !== safeToRemove) {
129
+ t2 = () => {
130
+ safeToRemoveRef.current = safeToRemove;
75
131
  };
76
- $[5] = ref;
132
+ $[5] = safeToRemove;
77
133
  $[6] = t2;
78
134
  } else {
79
135
  t2 = $[6];
80
136
  }
81
- const attach = t2;
82
- const reduceMotion = usePrefersReducedMotion();
83
- const [isPresent, safeToRemove] = usePresence();
137
+ useLayoutEffect(t2);
138
+ const interruptedRef = useRef(null);
84
139
  let t3;
85
140
  let t4;
86
- if ($[7] !== element || $[8] !== isPresent || $[9] !== kind || $[10] !== reduceMotion || $[11] !== safeToRemove) {
141
+ if ($[7] !== element || $[8] !== isPresent || $[9] !== kind || $[10] !== reduceMotion) {
87
142
  t3 = () => {
88
143
  let active = true;
89
- if (!element) {
144
+ const safeToRemove_0 = () => safeToRemoveRef.current?.();
145
+ if (!element || !element.isConnected) {
90
146
  if (!isPresent) {
91
147
  queueMicrotask(() => {
92
148
  if (active) {
93
- safeToRemove?.();
149
+ safeToRemove_0();
94
150
  }
95
151
  });
96
152
  }
@@ -98,189 +154,357 @@ export function MotionSlot(t0) {
98
154
  active = false;
99
155
  };
100
156
  }
101
- const animations = new Set();
157
+ const animations = new Map();
158
+ const held = new Set();
102
159
  const cleanups = [];
103
160
  const original = {
104
161
  opacity: element.style.opacity,
105
162
  transform: element.style.transform,
106
- translate: element.style.translate,
107
- boxShadow: element.style.boxShadow
163
+ boxShadow: element.style.boxShadow,
164
+ filter: element.style.filter,
165
+ clipPath: element.style.clipPath
108
166
  };
109
- const restore = function restore() {
110
- element.style.opacity = original.opacity;
111
- element.style.transform = original.transform;
112
- element.style.translate = original.translate;
113
- element.style.boxShadow = original.boxShadow;
167
+ const restore = function restore(t5) {
168
+ const properties = t5 === undefined ? ANIMATED_PROPERTIES : t5;
169
+ properties.forEach((property) => {
170
+ element.style[property] = original[property];
171
+ });
172
+ };
173
+ const cancel = function cancel(t6) {
174
+ const properties_0 = t6 === undefined ? ANIMATED_PROPERTIES : t6;
175
+ animations.forEach((driven, animation) => {
176
+ if (!driven.some((property_0) => properties_0.includes(property_0))) {
177
+ return;
178
+ }
179
+ animation.cancel();
180
+ animations.delete(animation);
181
+ });
114
182
  };
115
183
  const dispose = function dispose() {
116
184
  active = false;
185
+ if (animations.size) {
186
+ const computed = getComputedStyle(element);
187
+ const snapshot = {};
188
+ new Set([...animations.values()].flat()).forEach((property_1) => {
189
+ const value = computed[property_1];
190
+ if (value && value !== "none") {
191
+ snapshot[property_1] = value;
192
+ }
193
+ });
194
+ interruptedRef.current = snapshot;
195
+ }
117
196
  cleanups.forEach(_temp);
118
- animations.forEach(_temp2);
197
+ cancel();
119
198
  restore();
120
199
  };
121
200
  if (reduceMotion || !isPresent && element.hasAttribute("data-beez-theme-menu")) {
122
201
  if (!isPresent) {
123
202
  queueMicrotask(() => {
124
203
  if (active) {
125
- safeToRemove?.();
204
+ safeToRemove_0();
126
205
  }
127
206
  });
128
207
  }
129
208
  return dispose;
130
209
  }
131
- const play = function play(keyframes, duration, t5, t6) {
132
- const remove = t5 === undefined ? false : t5;
133
- const restoreOnComplete = t6 === undefined ? true : t6;
134
- animations.forEach(_temp3);
135
- animations.clear();
136
- const animation_1 = animate(element, keyframes, {
137
- duration,
138
- ease: [...MOTION_EASE]
210
+ const play = function play(keyframes, transition, t7) {
211
+ const { remove: t8, restoreOnComplete: t9 } = t7 === undefined ? {} : t7;
212
+ const remove = t8 === undefined ? false : t8;
213
+ const restoreOnComplete = t9 === undefined ? true : t9;
214
+ const properties_1 = ANIMATED_PROPERTIES.filter((property_2) => property_2 in keyframes);
215
+ const busy = new Set([...animations.values()].flat());
216
+ properties_1.forEach((property_3) => {
217
+ if (!busy.has(property_3) && !held.has(property_3)) {
218
+ original[property_3] = element.style[property_3];
219
+ }
139
220
  });
140
- animations.add(animation_1);
141
- animation_1.then(() => {
142
- animations.delete(animation_1);
143
- if (!active) {
221
+ cancel(properties_1);
222
+ properties_1.forEach((property_4) => held.delete(property_4));
223
+ const frames = keyframes;
224
+ properties_1.forEach((property_5) => {
225
+ const values = frames[property_5];
226
+ if (Array.isArray(values) && values.length > 1) {
227
+ element.style[property_5] = String(values[0]);
228
+ }
229
+ });
230
+ const animation_0 = animate(element, keyframes, transition);
231
+ animations.set(animation_0, properties_1);
232
+ animation_0.then(() => {
233
+ if (!animations.delete(animation_0) || !active) {
144
234
  return;
145
235
  }
146
236
  if (remove) {
147
- safeToRemove?.();
148
- } else {
149
- if (restoreOnComplete) {
150
- frame.postRender(() => {
151
- if (active && !animations.size) {
152
- restore();
153
- }
154
- });
155
- }
237
+ safeToRemove_0();
238
+ return;
239
+ }
240
+ if (!restoreOnComplete) {
241
+ properties_1.forEach((property_6) => held.add(property_6));
156
242
  }
243
+ frame.postRender(() => {
244
+ if (!active) {
245
+ return;
246
+ }
247
+ const busy_0 = new Set([...animations.values()].flat());
248
+ restore(ANIMATED_PROPERTIES.filter((property_7) => !busy_0.has(property_7) && !held.has(property_7)));
249
+ });
157
250
  });
158
251
  };
159
- const isSurface = [
160
- "surface",
161
- "dialog",
162
- "sheet",
163
- "overlay"
164
- ].includes(kind);
165
- if (!isPresent) {
166
- if (!isSurface) {
167
- queueMicrotask(() => {
168
- if (active) {
169
- safeToRemove?.();
252
+ const tween = _temp2;
253
+ const interrupted = interruptedRef.current;
254
+ interruptedRef.current = null;
255
+ if (SURFACE_KINDS.includes(kind)) {
256
+ const step = isPresent ? surfaceEnter(element, kind) : surfaceExit(element, kind);
257
+ const keyframes_0 = { ...step.keyframes };
258
+ if (!isPresent && interrupted) {
259
+ Object.entries(interrupted).forEach((t10) => {
260
+ const [property_8, value_0] = t10;
261
+ const frames_0 = keyframes_0[property_8];
262
+ if (!Array.isArray(frames_0) || !value_0) {
263
+ return;
170
264
  }
265
+ const start = property_8 === "opacity" ? Number.parseFloat(value_0) : value_0;
266
+ keyframes_0[property_8] = [start, ...frames_0.slice(1)];
171
267
  });
172
- return dispose;
173
268
  }
174
- const keyframes_0 = kind === "overlay" ? { opacity: [1, 0] } : {
175
- opacity: [1, 0],
176
- transform: [restingTransform(element, kind), entryTransform(element, kind)]
177
- };
178
- play(keyframes_0, MOTION_TIMING.exit, true);
269
+ play(keyframes_0, step.transition, { remove: !isPresent });
179
270
  return dispose;
180
271
  }
181
- if (isSurface) {
182
- const keyframes_1 = kind === "overlay" ? { opacity: [0, 1] } : {
183
- opacity: [0, 1],
184
- transform: [entryTransform(element, kind), restingTransform(element, kind)]
185
- };
186
- play(keyframes_1, kind === "dialog" || kind === "sheet" ? MOTION_TIMING.panel : MOTION_TIMING.enter);
272
+ if (!isPresent) {
273
+ queueMicrotask(() => {
274
+ if (active) {
275
+ safeToRemove_0();
276
+ }
277
+ });
278
+ return dispose;
279
+ }
280
+ if (kind === "skeleton") {
281
+ animations.set(animate(element, { opacity: [
282
+ 1,
283
+ .6,
284
+ 1
285
+ ] }, {
286
+ duration: MOTION_TIMING.skeleton,
287
+ repeat: Infinity,
288
+ ease: "easeInOut"
289
+ }), ["opacity"]);
187
290
  } else {
188
- if (kind === "skeleton") {
189
- animations.add(animate(element, { opacity: [
190
- 1,
191
- .6,
192
- 1
193
- ] }, {
194
- duration: MOTION_TIMING.skeleton,
195
- repeat: Infinity,
196
- ease: "easeInOut"
197
- }));
291
+ if (kind === "icon-swap") {
292
+ play({
293
+ opacity: [0, 1],
294
+ transform: [`scale(${MOTION_ICON_SWAP_SCALE})`, "scale(1)"],
295
+ filter: ["blur(8px)", "blur(0px)"]
296
+ }, {
297
+ duration: MOTION_TIMING.iconSwap,
298
+ ease: [...MOTION_EASE_IN_OUT]
299
+ });
198
300
  } else {
199
- if (kind === "selection") {
301
+ if (kind === "item-check") {
200
302
  play({
201
303
  opacity: [0, 1],
202
- transform: ["scale(0.85)", "scale(1)"]
203
- }, MOTION_TIMING.fast);
304
+ transform: [`scale(${MOTION_ITEM_CHECK_SCALE})`, "scale(1)"]
305
+ }, {
306
+ ...SPRING_PANEL,
307
+ opacity: tween(MOTION_TIMING.fast)
308
+ });
204
309
  } else {
205
- if (kind === "fade" || kind === "card" || kind === "row") {
206
- play({ opacity: [0, 1] }, MOTION_TIMING.enter);
310
+ if (kind === "list-item") {
311
+ const index = listIndexOf(element);
312
+ const position = Math.min(index, MOTION_LIST_STAGGER_LIMIT);
313
+ play(index < MOTION_LIST_STAGGER_LIMIT ? {
314
+ opacity: [0, 1],
315
+ transform: [`translateY(${-MOTION_LIST_ITEM_DISTANCE}px)`, "translateY(0px)"],
316
+ filter: ["blur(3px)", "blur(0px)"]
317
+ } : { opacity: [0, 1] }, {
318
+ ...tween(MOTION_TIMING.listItem),
319
+ delay: MOTION_TIMING.listDelay + position * MOTION_TIMING.listStagger
320
+ });
321
+ } else {
322
+ if (kind === "delayed-pop" && typeof element.animate === "function") {
323
+ const pop = element.animate({
324
+ opacity: [0, 1],
325
+ scale: [String(MOTION_CLOSE_BUTTON_SCALE), "1"]
326
+ }, {
327
+ duration: MOTION_TIMING.closeEnter * MILLISECONDS_PER_SECOND,
328
+ delay: MOTION_TIMING.closeDelay * MILLISECONDS_PER_SECOND,
329
+ easing: `cubic-bezier(${EASE_OUT.join(", ")})`,
330
+ fill: "backwards"
331
+ });
332
+ cleanups.push(() => pop.cancel());
333
+ } else {
334
+ if (kind === "selection") {
335
+ play({
336
+ opacity: [0, 1],
337
+ transform: ["scale(0.5)", "scale(1)"]
338
+ }, {
339
+ ...SPRING_PANEL,
340
+ opacity: tween(MOTION_TIMING.fast)
341
+ });
342
+ } else {
343
+ if (kind === "content") {
344
+ play({
345
+ opacity: [0, 1],
346
+ transform: [`translateY(${MOTION_CONTENT_DISTANCE}px)`, "translateY(0px)"]
347
+ }, tween(MOTION_TIMING.enter));
348
+ } else {
349
+ if (kind === "message") {
350
+ play({
351
+ opacity: [0, 1],
352
+ transform: [`translateY(${-MOTION_CONTENT_DISTANCE}px)`, "translateY(0px)"],
353
+ filter: ["blur(4px)", "blur(0px)"]
354
+ }, tween(MOTION_TIMING.message));
355
+ } else {
356
+ if (kind === "fade" || kind === "card" || kind === "row") {
357
+ play({ opacity: [0, 1] }, tween(MOTION_TIMING.enter));
358
+ }
359
+ }
360
+ }
361
+ }
362
+ }
207
363
  }
208
364
  }
209
365
  }
210
366
  }
211
- if (kind === "press") {
367
+ const pressScale = PRESS_SCALES[kind];
368
+ if (pressScale !== undefined) {
369
+ const liftsOnHover = kind === "press" && element.getAttribute("data-variant") !== "link";
370
+ let hovered = false;
371
+ let pressed = false;
372
+ const settle = () => {
373
+ const target = pressed ? pressScale : hovered ? MOTION_HOVER_SCALE : 1;
374
+ play({ transform: [currentTransform(element), `scale(${target})`] }, SPRING_PRESS, { restoreOnComplete: target === 1 });
375
+ };
212
376
  cleanups.push(press(element, () => {
213
377
  if (!isEnabled(element)) {
214
378
  return;
215
379
  }
216
- play({ transform: ["scale(1)", `scale(${MOTION_PRESS_SCALE})`] }, MOTION_TIMING.fast, false, false);
217
- return () => play({ transform: [`scale(${MOTION_PRESS_SCALE})`, "scale(1)"] }, MOTION_TIMING.fast);
380
+ pressed = true;
381
+ settle();
382
+ return () => {
383
+ pressed = false;
384
+ settle();
385
+ };
218
386
  }));
387
+ if (liftsOnHover) {
388
+ cleanups.push(hover(element, () => {
389
+ if (!isEnabled(element) || !canHover()) {
390
+ return;
391
+ }
392
+ hovered = true;
393
+ settle();
394
+ return () => {
395
+ hovered = false;
396
+ settle();
397
+ };
398
+ }));
399
+ }
219
400
  }
220
401
  if (kind === "card" || kind === "link" || kind === "icon") {
221
402
  cleanups.push(hover(element, () => {
222
- if (!isEnabled(element) || kind === "card" && !element.querySelector("a,button")) {
403
+ if (!isEnabled(element) || !canHover() || kind === "card" && !element.querySelector("a,button")) {
223
404
  return;
224
405
  }
406
+ const keep = { restoreOnComplete: false };
225
407
  if (kind === "card") {
226
- play({ boxShadow: "0 4px 16px rgba(0,0,0,0.08)" }, MOTION_TIMING.enter, false, false);
408
+ play({ boxShadow: "0 4px 16px rgba(0,0,0,0.08)" }, tween(MOTION_TIMING.enter), keep);
227
409
  } else {
228
410
  if (kind === "icon") {
229
- play({ transform: ["rotate(0deg) scale(1)", "rotate(-3deg) scale(1.025)"] }, MOTION_TIMING.enter, false, false);
411
+ play({ transform: ["rotate(0deg) scale(1)", "rotate(-3deg) scale(1.025)"] }, SPRING_PRESS, keep);
230
412
  } else {
231
- play({ opacity: .75 }, MOTION_TIMING.fast, false, false);
413
+ play({ opacity: .75 }, tween(MOTION_TIMING.fast), keep);
232
414
  }
233
415
  }
234
416
  return () => {
235
417
  if (kind === "card") {
236
- play({ boxShadow: original.boxShadow || "0 0px 0px rgba(0,0,0,0)" }, MOTION_TIMING.fast);
418
+ play({ boxShadow: original.boxShadow || "0 0px 0px rgba(0,0,0,0)" }, tween(MOTION_TIMING.fast));
237
419
  } else {
238
420
  if (kind === "icon") {
239
- play({ transform: ["rotate(-3deg) scale(1.025)", "rotate(0deg) scale(1)"] }, MOTION_TIMING.fast);
421
+ play({ transform: [currentTransform(element), "rotate(0deg) scale(1)"] }, SPRING_PRESS);
240
422
  } else {
241
- play({ opacity: 1 }, MOTION_TIMING.fast);
423
+ play({ opacity: 1 }, tween(MOTION_TIMING.fast));
242
424
  }
243
425
  }
244
426
  };
245
427
  }));
246
428
  }
247
- if (kind === "field") {
248
- const focus = () => {
249
- if (isEnabled(element)) {
250
- play({ opacity: [.94, 1] }, MOTION_TIMING.fast);
429
+ if (FIELD_KINDS.includes(kind)) {
430
+ const settleField = () => {
431
+ if (isEnabled(element) && !element.hasAttribute("hidden")) {
432
+ play({ opacity: [.94, 1] }, tween(MOTION_TIMING.fast));
251
433
  }
252
434
  };
253
- element.addEventListener("focusin", focus);
254
- cleanups.push(() => element.removeEventListener("focusin", focus));
255
- const observer = new MutationObserver(() => {
256
- if (isEnabled(element) && !element.hasAttribute("hidden")) {
257
- play({ opacity: [.94, 1] }, MOTION_TIMING.fast);
435
+ element.addEventListener("focusin", settleField);
436
+ cleanups.push(() => element.removeEventListener("focusin", settleField));
437
+ const isGroup = element.getAttribute("data-slot") === INPUT_GROUP_SLOT;
438
+ const isGroupControl = !isGroup && element.parentElement?.closest(`[data-slot="${INPUT_GROUP_SLOT}"]`) != null;
439
+ const readInvalid = () => isGroup ? element.querySelector("[data-slot][aria-invalid=\"true\"]") !== null : element.getAttribute("aria-invalid") === "true";
440
+ let wasInvalid = readInvalid();
441
+ const observer = new MutationObserver((records) => {
442
+ const isInvalid = readInvalid();
443
+ const becameInvalid = isInvalid && !wasInvalid;
444
+ wasInvalid = isInvalid;
445
+ if (becameInvalid && !isGroupControl && !element.hasAttribute("hidden")) {
446
+ play({ transform: MOTION_INVALID_SHAKE_PX.map(_temp3) }, { duration: MOTION_TIMING.shake });
447
+ } else {
448
+ if (records.some((record) => record.target === element && record.attributeName !== "aria-invalid")) {
449
+ settleField();
450
+ }
258
451
  }
259
452
  });
260
453
  observer.observe(element, {
261
454
  attributes: true,
262
- attributeFilter: [
263
- "data-state",
264
- "aria-invalid",
265
- "aria-selected",
266
- "aria-checked",
267
- "data-loading",
268
- "data-error"
269
- ]
455
+ attributeFilter: FIELD_STATE_ATTRIBUTES,
456
+ subtree: isGroup
270
457
  });
271
458
  cleanups.push(() => observer.disconnect());
272
459
  }
273
- if (kind === "thumb") {
274
- let previousTranslate = getComputedStyle(element).translate;
460
+ if (kind === "check") {
461
+ let wasChecked = isChecked(element);
462
+ const strokeDraws = new Set();
463
+ const settleStrokes = () => {
464
+ strokeDraws.forEach(_temp4);
465
+ strokeDraws.clear();
466
+ element.querySelectorAll("path").forEach(_temp5);
467
+ };
468
+ cleanups.push(settleStrokes);
275
469
  const observer_0 = new MutationObserver(() => {
276
- const from = animations.size ? getComputedStyle(element).translate : previousTranslate;
277
- animations.forEach(_temp4);
278
- animations.clear();
279
- element.style.translate = original.translate;
280
- const to = getComputedStyle(element).translate;
281
- previousTranslate = to;
282
- if (from !== to) {
283
- play({ translate: [from, to] }, MOTION_TIMING.enter);
470
+ const checked = isChecked(element);
471
+ if (checked === wasChecked) {
472
+ return;
473
+ }
474
+ wasChecked = checked;
475
+ settleStrokes();
476
+ if (checked) {
477
+ play({
478
+ opacity: [0, 1],
479
+ transform: ["scale(0.5)", "scale(1)"],
480
+ filter: ["blur(0px)", "blur(0px)"]
481
+ }, tween(MOTION_TIMING.enter));
482
+ element.querySelectorAll("path").forEach((path_0) => {
483
+ path_0.setAttribute("pathLength", "1");
484
+ path_0.setAttribute("stroke-dasharray", "0 1");
485
+ path_0.setAttribute("stroke-dashoffset", "0");
486
+ const draw_0 = animate(path_0, { pathLength: [0, 1] }, {
487
+ ...tween(MOTION_TIMING.checkDraw),
488
+ delay: MOTION_TIMING.checkDrawDelay
489
+ });
490
+ strokeDraws.add(draw_0);
491
+ draw_0.then(() => {
492
+ if (!strokeDraws.delete(draw_0)) {
493
+ return;
494
+ }
495
+ frame.postRender(() => {
496
+ if (active && !strokeDraws.size) {
497
+ settleStrokes();
498
+ }
499
+ });
500
+ });
501
+ });
502
+ } else {
503
+ play({
504
+ opacity: [1, 0],
505
+ transform: ["scale(1)", "scale(0.5)"],
506
+ filter: ["blur(0px)", "blur(4px)"]
507
+ }, tween(MOTION_TIMING.enter));
284
508
  }
285
509
  });
286
510
  observer_0.observe(element, {
@@ -289,65 +513,168 @@ export function MotionSlot(t0) {
289
513
  });
290
514
  cleanups.push(() => observer_0.disconnect());
291
515
  }
516
+ if (kind === "rotate") {
517
+ const restingAngle = () => parseFloat(getComputedStyle(element).rotate) || 0;
518
+ let previousAngle = restingAngle();
519
+ const turn = () => {
520
+ const inFlight = rotationOf(getComputedStyle(element).transform);
521
+ cancel(["transform"]);
522
+ restore(["transform"]);
523
+ const nextAngle = restingAngle();
524
+ const delta = previousAngle + inFlight - nextAngle;
525
+ previousAngle = nextAngle;
526
+ if (Math.abs(delta) >= 1) {
527
+ play({ transform: [`rotate(${delta}deg)`, "rotate(0deg)"] }, SPRING_CHEVRON);
528
+ }
529
+ };
530
+ const observer_1 = new MutationObserver(turn);
531
+ observer_1.observe(element, {
532
+ attributes: true,
533
+ attributeFilter: ["class"]
534
+ });
535
+ if (element.parentElement) {
536
+ observer_1.observe(element.parentElement, {
537
+ attributes: true,
538
+ attributeFilter: [
539
+ "aria-expanded",
540
+ "data-state",
541
+ "class"
542
+ ]
543
+ });
544
+ }
545
+ cleanups.push(() => observer_1.disconnect());
546
+ }
547
+ if (kind === "thumb") {
548
+ const restingOffset = () => {
549
+ if (!element.getClientRects().length) {
550
+ return null;
551
+ }
552
+ const rect = element.getBoundingClientRect();
553
+ const trackLeft = element.parentElement?.getBoundingClientRect().left ?? 0;
554
+ return rect.left - trackLeft + (rect.width - element.offsetWidth) / 2;
555
+ };
556
+ let previousOffset = restingOffset();
557
+ const remeasure = () => {
558
+ if (!animations.size) {
559
+ previousOffset = restingOffset();
560
+ }
561
+ };
562
+ element.parentElement?.addEventListener("pointerenter", remeasure);
563
+ element.parentElement?.addEventListener("focusin", remeasure);
564
+ cleanups.push(() => {
565
+ element.parentElement?.removeEventListener("pointerenter", remeasure);
566
+ element.parentElement?.removeEventListener("focusin", remeasure);
567
+ });
568
+ const observer_2 = new MutationObserver(() => {
569
+ const glideOffset = translateXOf(getComputedStyle(element).transform);
570
+ cancel(["transform"]);
571
+ restore(["transform"]);
572
+ const nextOffset = restingOffset();
573
+ const delta_0 = previousOffset === null || nextOffset === null ? 0 : previousOffset + glideOffset - nextOffset;
574
+ previousOffset = nextOffset;
575
+ if (delta_0 !== 0) {
576
+ play({ transform: [`translateX(${delta_0}px)`, "translateX(0px)"] }, SPRING_THUMB);
577
+ }
578
+ });
579
+ observer_2.observe(element, {
580
+ attributes: true,
581
+ attributeFilter: ["data-state"]
582
+ });
583
+ cleanups.push(() => observer_2.disconnect());
584
+ const root = element.parentElement;
585
+ if (root && typeof element.animate === "function") {
586
+ const squishTiming = {
587
+ duration: MOTION_TIMING.fast * MILLISECONDS_PER_SECOND,
588
+ easing: `cubic-bezier(${EASE_OUT.join(", ")})`,
589
+ fill: "forwards"
590
+ };
591
+ let squish = null;
592
+ cleanups.push(() => squish?.cancel());
593
+ cleanups.push(press(root, () => {
594
+ if (!isEnabled(root)) {
595
+ return;
596
+ }
597
+ squish?.cancel();
598
+ squish = element.animate({ scale: ["1", String(MOTION_THUMB_SQUISH_SCALE)] }, squishTiming);
599
+ return () => {
600
+ const from = getComputedStyle(element).scale;
601
+ squish?.cancel();
602
+ const release = element.animate({ scale: [from === "none" ? "1" : from, "1"] }, squishTiming);
603
+ squish = release;
604
+ release.finished.then(() => {
605
+ if (squish === release) {
606
+ release.cancel();
607
+ squish = null;
608
+ }
609
+ }).catch(_temp6);
610
+ };
611
+ }));
612
+ }
613
+ }
292
614
  return dispose;
293
615
  };
294
616
  t4 = [
295
617
  element,
296
618
  kind,
297
619
  reduceMotion,
298
- isPresent,
299
- safeToRemove
620
+ isPresent
300
621
  ];
301
622
  $[7] = element;
302
623
  $[8] = isPresent;
303
624
  $[9] = kind;
304
625
  $[10] = reduceMotion;
305
- $[11] = safeToRemove;
306
- $[12] = t3;
307
- $[13] = t4;
626
+ $[11] = t3;
627
+ $[12] = t4;
308
628
  } else {
309
- t3 = $[12];
310
- t4 = $[13];
629
+ t3 = $[11];
630
+ t4 = $[12];
311
631
  }
312
632
  useLayoutEffect(t3, t4);
313
633
  let t5;
314
- if ($[14] !== isPresent) {
634
+ if ($[13] !== isPresent) {
315
635
  t5 = isPresent ? {} : {
316
636
  inert: true,
317
637
  "aria-hidden": true
318
638
  };
319
- $[14] = isPresent;
320
- $[15] = t5;
639
+ $[13] = isPresent;
640
+ $[14] = t5;
321
641
  } else {
322
- t5 = $[15];
642
+ t5 = $[14];
323
643
  }
324
644
  let t6;
325
- if ($[16] !== attach || $[17] !== children || $[18] !== props || $[19] !== t5) {
645
+ if ($[15] !== attach || $[16] !== children || $[17] !== props || $[18] !== t5) {
326
646
  t6 = /* @__PURE__ */ _jsx(Slot.Root, {
327
647
  ...props,
328
648
  ref: attach,
329
649
  ...t5,
330
650
  children
331
651
  });
332
- $[16] = attach;
333
- $[17] = children;
334
- $[18] = props;
335
- $[19] = t5;
336
- $[20] = t6;
652
+ $[15] = attach;
653
+ $[16] = children;
654
+ $[17] = props;
655
+ $[18] = t5;
656
+ $[19] = t6;
337
657
  } else {
338
- t6 = $[20];
658
+ t6 = $[19];
339
659
  }
340
660
  return t6;
341
661
  }
342
- function _temp4(animation_2) {
343
- return animation_2.cancel();
662
+ function _temp6() {}
663
+ function _temp5(path) {
664
+ STROKE_DRAW_ATTRIBUTES.forEach((attribute) => path.removeAttribute(attribute));
665
+ }
666
+ function _temp4(draw) {
667
+ return draw.cancel();
344
668
  }
345
- function _temp3(animation_0) {
346
- return animation_0.cancel();
669
+ function _temp3(offset) {
670
+ return `translateX(${offset}px)`;
347
671
  }
348
- function _temp2(animation) {
349
- return animation.cancel();
672
+ function _temp2(duration) {
673
+ return {
674
+ duration,
675
+ ease: EASE_OUT
676
+ };
350
677
  }
351
- function _temp(cleanup_0) {
352
- return cleanup_0();
678
+ function _temp(cleanup) {
679
+ return cleanup();
353
680
  }