@zyncat/ui 0.8.0 → 0.9.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/README.md +2 -2
- package/dist/alert.js +6 -7
- package/dist/alert.js.map +1 -1
- package/dist/button.css +10 -2
- package/dist/button.d.ts +2 -6
- package/dist/button.js +1 -1
- package/dist/{chunk-U5QQZC3U.js → chunk-2F5PITAO.js} +2 -6
- package/dist/chunk-2F5PITAO.js.map +1 -0
- package/dist/chunk-2QWJ6FAX.js +78 -0
- package/dist/chunk-2QWJ6FAX.js.map +1 -0
- package/dist/{chunk-MXOPCFWR.js → chunk-356HZMT4.js} +25 -4
- package/dist/chunk-356HZMT4.js.map +1 -0
- package/dist/{chunk-QMSJZCK3.js → chunk-45XJ75GZ.js} +9 -9
- package/dist/chunk-45XJ75GZ.js.map +1 -0
- package/dist/{chunk-4R2GQO43.js → chunk-FLO4A6BI.js} +4 -4
- package/dist/{chunk-4R2GQO43.js.map → chunk-FLO4A6BI.js.map} +1 -1
- package/dist/{chunk-XZAEIQDR.js → chunk-GLT5IKQY.js} +5 -5
- package/dist/{chunk-XZAEIQDR.js.map → chunk-GLT5IKQY.js.map} +1 -1
- package/dist/{chunk-O4N2OBTR.js → chunk-W5YM2FV4.js} +4 -4
- package/dist/{chunk-O4N2OBTR.js.map → chunk-W5YM2FV4.js.map} +1 -1
- package/dist/{chunk-MKIQFK7S.js → chunk-WM5J5TDI.js} +3 -3
- package/dist/{chunk-MKIQFK7S.js.map → chunk-WM5J5TDI.js.map} +1 -1
- package/dist/{chunk-W3SHPKSB.js → chunk-XWUWYSCT.js} +16 -6
- package/dist/chunk-XWUWYSCT.js.map +1 -0
- package/dist/{chunk-2HORSLXJ.js → chunk-Y7AJQVWJ.js} +17 -8
- package/dist/chunk-Y7AJQVWJ.js.map +1 -0
- package/dist/{chunk-L3Q3VDIW.js → chunk-YGCVWGUG.js} +6 -32
- package/dist/chunk-YGCVWGUG.js.map +1 -0
- package/dist/date-field.js +8 -8
- package/dist/date-range-field.js +32 -13
- package/dist/date-range-field.js.map +1 -1
- package/dist/datetime-field.js +8 -8
- package/dist/dialog.js +4 -4
- package/dist/glide.js +2 -2
- package/dist/motion-devtools.js +2 -2
- package/dist/multi-select.js +6 -6
- package/dist/pagination.js +2 -2
- package/dist/popover.js +4 -4
- package/dist/radio-group.js +36 -9
- package/dist/radio-group.js.map +1 -1
- package/dist/select.js +6 -6
- package/dist/sheet.js +5 -5
- package/dist/table.js +7 -7
- package/dist/table.js.map +1 -1
- package/dist/tabs.js +2 -2
- package/dist/tag.js +7 -7
- package/dist/tag.js.map +1 -1
- package/dist/toast.js +5 -6
- package/dist/toast.js.map +1 -1
- package/dist/tooltip.js +3 -3
- package/llms.txt +9 -8
- package/package.json +11 -10
- package/src/components/composites/date-picker/calendar-panel.tsx +12 -3
- package/src/components/composites/date-picker/range-panel.tsx +19 -5
- package/src/components/composites/select/core/trigger.tsx +8 -12
- package/src/components/composites/table/Table.tsx +4 -2
- package/src/components/primitives/button/Button.tsx +0 -16
- package/src/components/primitives/button/button.css +10 -2
- package/src/components/primitives/radio-group/RadioGroup.tsx +26 -7
- package/src/components/primitives/tag/Tag.tsx +2 -1
- package/src/engine/ease.ts +7 -39
- package/src/engine/index.ts +1 -11
- package/src/motion/element.tsx +9 -9
- package/src/motion/flip.ts +61 -0
- package/src/motion/presence-context.ts +4 -3
- package/src/motion/presence.tsx +25 -1
- package/src/motion/use-motion.ts +13 -2
- package/dist/chunk-2HORSLXJ.js.map +0 -1
- package/dist/chunk-36WVAQKL.js +0 -40
- package/dist/chunk-36WVAQKL.js.map +0 -1
- package/dist/chunk-JSKXZMLH.js +0 -67
- package/dist/chunk-JSKXZMLH.js.map +0 -1
- package/dist/chunk-L3Q3VDIW.js.map +0 -1
- package/dist/chunk-MXOPCFWR.js.map +0 -1
- package/dist/chunk-QMSJZCK3.js.map +0 -1
- package/dist/chunk-U5QQZC3U.js.map +0 -1
- package/dist/chunk-W3SHPKSB.js.map +0 -1
- package/src/motion/flip.tsx +0 -87
|
@@ -19,10 +19,6 @@ interface ButtonOwnProps {
|
|
|
19
19
|
disabled?: boolean;
|
|
20
20
|
/** Loading - swaps content for a spinner and makes the button inert. */
|
|
21
21
|
loading?: boolean;
|
|
22
|
-
/** Leading icon node (e.g. a 16px <Icon>). Sized & aligned by the component. */
|
|
23
|
-
iconLeft?: ReactNode;
|
|
24
|
-
/** Trailing icon node. */
|
|
25
|
-
iconRight?: ReactNode;
|
|
26
22
|
/** Stretch to fill the container width. */
|
|
27
23
|
fullWidth?: boolean;
|
|
28
24
|
/** Extra class(es) merged onto the button. */
|
|
@@ -51,8 +47,6 @@ export function Button({
|
|
|
51
47
|
type = 'button',
|
|
52
48
|
disabled = false,
|
|
53
49
|
loading = false,
|
|
54
|
-
iconLeft = null,
|
|
55
|
-
iconRight = null,
|
|
56
50
|
fullWidth = false,
|
|
57
51
|
className = '',
|
|
58
52
|
style,
|
|
@@ -85,17 +79,7 @@ export function Button({
|
|
|
85
79
|
{...rest}
|
|
86
80
|
{...htmlProps}
|
|
87
81
|
>
|
|
88
|
-
{iconLeft ? (
|
|
89
|
-
<span className="btn__icon" aria-hidden="true">
|
|
90
|
-
{iconLeft}
|
|
91
|
-
</span>
|
|
92
|
-
) : null}
|
|
93
82
|
<span className="btn__label">{children}</span>
|
|
94
|
-
{iconRight ? (
|
|
95
|
-
<span className="btn__icon" aria-hidden="true">
|
|
96
|
-
{iconRight}
|
|
97
|
-
</span>
|
|
98
|
-
) : null}
|
|
99
83
|
{loading ? <span className="btn__spinner" aria-hidden="true" /> : null}
|
|
100
84
|
</button>
|
|
101
85
|
);
|
|
@@ -16,13 +16,14 @@
|
|
|
16
16
|
.btn {
|
|
17
17
|
--btn-shadow: none;
|
|
18
18
|
--btn-ring: var(--ring-accent);
|
|
19
|
+
--btn-gap: var(--space-2);
|
|
19
20
|
|
|
20
21
|
position: relative;
|
|
21
22
|
isolation: isolate;
|
|
22
23
|
display: inline-flex;
|
|
23
24
|
align-items: center;
|
|
24
25
|
justify-content: center;
|
|
25
|
-
gap: var(--
|
|
26
|
+
gap: var(--btn-gap);
|
|
26
27
|
|
|
27
28
|
height: var(--control-height);
|
|
28
29
|
padding: 0 var(--space-4);
|
|
@@ -64,7 +65,7 @@
|
|
|
64
65
|
padding: 0 var(--space-3);
|
|
65
66
|
font-size: var(--size-caption);
|
|
66
67
|
border-radius: var(--radius-sm);
|
|
67
|
-
gap: var(--space-1);
|
|
68
|
+
--btn-gap: var(--space-1);
|
|
68
69
|
}
|
|
69
70
|
.btn--lg {
|
|
70
71
|
height: var(--control-height-lg);
|
|
@@ -77,9 +78,16 @@
|
|
|
77
78
|
.btn--icon {
|
|
78
79
|
width: var(--control-height-sm);
|
|
79
80
|
height: var(--control-height-sm);
|
|
81
|
+
padding: 0;
|
|
80
82
|
border-radius: var(--radius-sm);
|
|
81
83
|
}
|
|
82
84
|
|
|
85
|
+
.btn__label {
|
|
86
|
+
display: inline-flex;
|
|
87
|
+
align-items: center;
|
|
88
|
+
gap: var(--btn-gap);
|
|
89
|
+
}
|
|
90
|
+
|
|
83
91
|
.btn__icon {
|
|
84
92
|
display: inline-flex;
|
|
85
93
|
flex: none;
|
|
@@ -6,13 +6,15 @@ import { Icon } from '../../internal/icon/Icon';
|
|
|
6
6
|
import { IconSlot } from '../../internal/icon/IconSlot';
|
|
7
7
|
import { Collapse } from '../collapse/Collapse';
|
|
8
8
|
import { GlidePill, useGlide } from '../../../motion/glide';
|
|
9
|
-
import {
|
|
9
|
+
import { Motion } from '../../../motion/element';
|
|
10
10
|
import { UIMotion } from '../../../tokens/motion-tokens';
|
|
11
11
|
import type { DataAttributes } from '../../../dom-props';
|
|
12
12
|
import { cx } from '../../internal/utils/cx';
|
|
13
13
|
import type { FieldRequirementProps } from '../input/field-chrome';
|
|
14
14
|
|
|
15
15
|
const SM = UIMotion;
|
|
16
|
+
const LAYOUT_FLIP = { timing: SM.t.layout };
|
|
17
|
+
const HOVER_FLIP = { scale: false, timing: SM.t.layout };
|
|
16
18
|
|
|
17
19
|
export interface RadioOption {
|
|
18
20
|
/** The stored value - what `onChange` returns and `value` matches. */
|
|
@@ -83,9 +85,6 @@ export function RadioGroup({
|
|
|
83
85
|
const [hovered, setHovered] = useState<string | null>(null);
|
|
84
86
|
const optionsRef = useRef<HTMLDivElement>(null);
|
|
85
87
|
const glide = useGlide(optionsRef);
|
|
86
|
-
const markerRef = useSharedFlip<HTMLSpanElement>(groupId + ':marker', { timing: SM.t.layout });
|
|
87
|
-
const fillRef = useSharedFlip<HTMLSpanElement>(groupId + ':card-fill', { timing: SM.t.layout });
|
|
88
|
-
const cardHoverRef = useSharedFlip<HTMLSpanElement>(groupId + ':card-hover', { scale: false, timing: SM.t.layout });
|
|
89
88
|
|
|
90
89
|
const cls = cx(
|
|
91
90
|
'rg',
|
|
@@ -148,10 +147,22 @@ export function RadioGroup({
|
|
|
148
147
|
onChange={() => onChange && onChange(opt.value)}
|
|
149
148
|
/>
|
|
150
149
|
{variant === 'cards' && hovered === opt.value && !selected && (
|
|
151
|
-
<
|
|
150
|
+
<Motion
|
|
151
|
+
as="span"
|
|
152
|
+
layoutId={groupId + ':card-hover'}
|
|
153
|
+
layoutTransition={HOVER_FLIP}
|
|
154
|
+
className="rg__card-hover"
|
|
155
|
+
aria-hidden="true"
|
|
156
|
+
/>
|
|
152
157
|
)}
|
|
153
158
|
{variant === 'cards' && selected && (
|
|
154
|
-
<
|
|
159
|
+
<Motion
|
|
160
|
+
as="span"
|
|
161
|
+
layoutId={groupId + ':card-fill'}
|
|
162
|
+
layoutTransition={LAYOUT_FLIP}
|
|
163
|
+
className="rg__card-fill"
|
|
164
|
+
aria-hidden="true"
|
|
165
|
+
/>
|
|
155
166
|
)}
|
|
156
167
|
{variant === 'cards' && opt.icon && (
|
|
157
168
|
<span className="rg-opt__icon">
|
|
@@ -160,7 +171,15 @@ export function RadioGroup({
|
|
|
160
171
|
)}
|
|
161
172
|
<span className="rg-opt__control">
|
|
162
173
|
<span className="rg-opt__dot">
|
|
163
|
-
{selected &&
|
|
174
|
+
{selected && (
|
|
175
|
+
<Motion
|
|
176
|
+
as="span"
|
|
177
|
+
layoutId={groupId + ':marker'}
|
|
178
|
+
layoutTransition={LAYOUT_FLIP}
|
|
179
|
+
className="rg__marker"
|
|
180
|
+
aria-hidden="true"
|
|
181
|
+
/>
|
|
182
|
+
)}
|
|
164
183
|
</span>
|
|
165
184
|
</span>
|
|
166
185
|
<span className="rg-opt__body">
|
|
@@ -102,7 +102,8 @@ export function Tag({
|
|
|
102
102
|
ref={ref}
|
|
103
103
|
animate={group?.animate}
|
|
104
104
|
exit={group?.exit}
|
|
105
|
-
layout={group
|
|
105
|
+
layout={!!group}
|
|
106
|
+
layoutTransition={group?.layout}
|
|
106
107
|
className={cls}
|
|
107
108
|
style={style}
|
|
108
109
|
data-disabled={disabled ? 'true' : undefined}
|
package/src/engine/ease.ts
CHANGED
|
@@ -14,40 +14,13 @@ const NAMED: Record<string, Bezier> = {
|
|
|
14
14
|
anticipate: [0.38, -0.4, 0.88, 1.08],
|
|
15
15
|
};
|
|
16
16
|
|
|
17
|
-
const bezierCurve = (a: number, b: number, t: number) => {
|
|
18
|
-
const u = 1 - t;
|
|
19
|
-
return 3 * a * u * u * t + 3 * b * u * t * t + t * t * t;
|
|
20
|
-
};
|
|
21
|
-
|
|
22
|
-
const bezierSlope = (a: number, b: number, t: number) => {
|
|
23
|
-
const u = 1 - t;
|
|
24
|
-
return 3 * a * (u * u - 2 * t * u) + 3 * b * (2 * t * u - t * t) + 3 * t * t;
|
|
25
|
-
};
|
|
26
|
-
|
|
27
|
-
export function bezierAt(b: Bezier, x: number): number {
|
|
28
|
-
const [x1, y1, x2, y2] = b;
|
|
29
|
-
if (x1 === y1 && x2 === y2) return x;
|
|
30
|
-
if (x <= 0) return 0;
|
|
31
|
-
if (x >= 1) return 1;
|
|
32
|
-
let t = x;
|
|
33
|
-
for (let i = 0; i < 8; i++) {
|
|
34
|
-
const err = bezierCurve(x1, x2, t) - x;
|
|
35
|
-
if (err < 1e-4 && err > -1e-4) break;
|
|
36
|
-
const slope = bezierSlope(x1, x2, t);
|
|
37
|
-
if (slope < 1e-6 && slope > -1e-6) break;
|
|
38
|
-
t -= err / slope;
|
|
39
|
-
}
|
|
40
|
-
return bezierCurve(y1, y2, t);
|
|
41
|
-
}
|
|
42
|
-
|
|
43
17
|
export type EaseValue = NonNullable<MotionTransition['ease']>;
|
|
44
18
|
|
|
45
|
-
|
|
46
|
-
typeof ease === 'string' ? (NAMED[ease] ?? NAMED.easeInOut) : ease;
|
|
19
|
+
const toBezier = (ease: EaseValue): Bezier => (typeof ease === 'string' ? (NAMED[ease] ?? NAMED.easeInOut) : ease);
|
|
47
20
|
|
|
48
|
-
|
|
21
|
+
const cssBezier = (ease: EaseValue): string => `cubic-bezier(${toBezier(ease).join(',')})`;
|
|
49
22
|
|
|
50
|
-
|
|
23
|
+
const isSegmented = (ease: unknown): ease is EaseValue[] =>
|
|
51
24
|
Array.isArray(ease) && (Array.isArray(ease[0]) || typeof ease[0] === 'string');
|
|
52
25
|
|
|
53
26
|
const ARRIVAL = 0.99;
|
|
@@ -103,7 +76,6 @@ function toLinearEasing(at: (progress: number) => number, durationMs: number): s
|
|
|
103
76
|
interface SpringCurve {
|
|
104
77
|
duration: number;
|
|
105
78
|
easing: string;
|
|
106
|
-
at(progress: number): number;
|
|
107
79
|
}
|
|
108
80
|
|
|
109
81
|
const springCache = new Map<string, SpringCurve>();
|
|
@@ -117,7 +89,7 @@ function springCurve(visual: number, bounce: number): SpringCurve {
|
|
|
117
89
|
const seconds = settleSeconds(zeta, omega, visual);
|
|
118
90
|
const duration = seconds * 1000;
|
|
119
91
|
const at = (progress: number) => (progress >= 1 ? 1 : springAt(zeta, omega, progress * seconds));
|
|
120
|
-
const curve: SpringCurve = { duration, easing: toLinearEasing(at, duration)
|
|
92
|
+
const curve: SpringCurve = { duration, easing: toLinearEasing(at, duration) };
|
|
121
93
|
springCache.set(key, curve);
|
|
122
94
|
return curve;
|
|
123
95
|
}
|
|
@@ -133,7 +105,6 @@ export interface ResolvedTiming {
|
|
|
133
105
|
delay: number;
|
|
134
106
|
easing: string;
|
|
135
107
|
segments: string[] | null;
|
|
136
|
-
at(progress: number): number;
|
|
137
108
|
}
|
|
138
109
|
|
|
139
110
|
export function resolveTiming(timing: Timing = {}): ResolvedTiming {
|
|
@@ -142,14 +113,11 @@ export function resolveTiming(timing: Timing = {}): ResolvedTiming {
|
|
|
142
113
|
if (spring) {
|
|
143
114
|
const visual = Math.max(timing.visualDuration ?? timing.duration ?? 0.3, 0.01);
|
|
144
115
|
const curve = springCurve(visual, timing.bounce ?? 0);
|
|
145
|
-
return { duration: curve.duration, delay, easing: curve.easing, segments: null
|
|
116
|
+
return { duration: curve.duration, delay, easing: curve.easing, segments: null };
|
|
146
117
|
}
|
|
147
118
|
const duration = (timing.duration ?? 0.3) * 1000;
|
|
148
119
|
const ease = timing.ease;
|
|
149
|
-
if (isSegmented(ease)) {
|
|
150
|
-
const first = toBezier(ease[0]);
|
|
151
|
-
return { duration, delay, easing: 'linear', segments: ease.map(cssBezier), at: (p) => bezierAt(first, p) };
|
|
152
|
-
}
|
|
120
|
+
if (isSegmented(ease)) return { duration, delay, easing: 'linear', segments: ease.map(cssBezier) };
|
|
153
121
|
const bezier = ease === undefined ? NAMED.easeInOut : toBezier(ease);
|
|
154
|
-
return { duration, delay, easing: cssBezier(bezier), segments: null
|
|
122
|
+
return { duration, delay, easing: cssBezier(bezier), segments: null };
|
|
155
123
|
}
|
package/src/engine/index.ts
CHANGED
|
@@ -1,13 +1,3 @@
|
|
|
1
|
-
export { animate, clock, type Layer, type Playback
|
|
2
|
-
export {
|
|
3
|
-
bezierAt,
|
|
4
|
-
cssBezier,
|
|
5
|
-
isSegmented,
|
|
6
|
-
resolveTiming,
|
|
7
|
-
toBezier,
|
|
8
|
-
type EaseValue,
|
|
9
|
-
type ResolvedTiming,
|
|
10
|
-
type Timing,
|
|
11
|
-
} from './ease';
|
|
1
|
+
export { animate, clock, type Layer, type Playback } from './animate';
|
|
12
2
|
export { flip, measure, keepShared, readShared, dropShared, type Box, type FlipOptions } from './flip';
|
|
13
3
|
export { startDrag, type PanInfo } from './drag';
|
package/src/motion/element.tsx
CHANGED
|
@@ -1,16 +1,18 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
3
|
import { createElement, useCallback, useRef, type ReactNode, type Ref } from 'react';
|
|
4
|
-
import {
|
|
4
|
+
import { useFlip, type FlipTuning } from './flip';
|
|
5
5
|
import { useMotion, type MotionSpecs } from './use-motion';
|
|
6
6
|
|
|
7
7
|
export interface MotionProps extends MotionSpecs {
|
|
8
8
|
/** Tag for the element this renders. @default 'div' */
|
|
9
9
|
as?: string;
|
|
10
|
-
/** FLIP this element from its previous box on every render
|
|
11
|
-
layout?: boolean
|
|
10
|
+
/** FLIP this element from its previous box on every render. Implied by `layoutId`. */
|
|
11
|
+
layout?: boolean;
|
|
12
12
|
/** FLIP from whatever element last held this id, so the box travels between nodes. */
|
|
13
13
|
layoutId?: string;
|
|
14
|
+
/** Tunes whichever layout animation is active; ignored without `layout` or `layoutId`. */
|
|
15
|
+
layoutTransition?: FlipTuning;
|
|
14
16
|
ref?: Ref<HTMLElement | null>;
|
|
15
17
|
children?: ReactNode;
|
|
16
18
|
[prop: string]: unknown;
|
|
@@ -24,13 +26,12 @@ export function Motion({
|
|
|
24
26
|
deps,
|
|
25
27
|
layout,
|
|
26
28
|
layoutId,
|
|
29
|
+
layoutTransition,
|
|
27
30
|
ref,
|
|
28
31
|
children,
|
|
29
32
|
...rest
|
|
30
33
|
}: MotionProps) {
|
|
31
|
-
const
|
|
32
|
-
const selfRef = useSelfFlip<HTMLElement>(tuning, !!layout && !layoutId);
|
|
33
|
-
const sharedRef = useSharedFlip<HTMLElement>(layoutId ?? '', tuning, !!layoutId);
|
|
34
|
+
const flipRef = useFlip<HTMLElement>(layoutId ?? null, layoutTransition, !!layoutId || !!layout);
|
|
34
35
|
const host = useRef<HTMLElement | null>(null);
|
|
35
36
|
const forwarded = useRef(ref);
|
|
36
37
|
forwarded.current = ref;
|
|
@@ -40,13 +41,12 @@ export function Motion({
|
|
|
40
41
|
const attach = useCallback(
|
|
41
42
|
(el: HTMLElement | null) => {
|
|
42
43
|
host.current = el;
|
|
43
|
-
|
|
44
|
-
sharedRef.current = el;
|
|
44
|
+
flipRef.current = el;
|
|
45
45
|
const outer = forwarded.current;
|
|
46
46
|
if (typeof outer === 'function') outer(el);
|
|
47
47
|
else if (outer) outer.current = el;
|
|
48
48
|
},
|
|
49
|
-
[
|
|
49
|
+
[flipRef],
|
|
50
50
|
);
|
|
51
51
|
|
|
52
52
|
return createElement(as, { ...rest, ref: attach }, children);
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { useLayoutEffect, useRef, type RefObject } from 'react';
|
|
4
|
+
import {
|
|
5
|
+
dropShared,
|
|
6
|
+
flip,
|
|
7
|
+
keepShared,
|
|
8
|
+
measure,
|
|
9
|
+
readShared,
|
|
10
|
+
type Box,
|
|
11
|
+
type FlipOptions,
|
|
12
|
+
type Playback,
|
|
13
|
+
} from '../engine';
|
|
14
|
+
import { UIMotion } from '../tokens/motion-tokens';
|
|
15
|
+
|
|
16
|
+
export type FlipTuning = FlipOptions;
|
|
17
|
+
|
|
18
|
+
export function useFlip<T extends HTMLElement>(
|
|
19
|
+
sharedId: string | null,
|
|
20
|
+
tuning: FlipTuning = {},
|
|
21
|
+
enabled = true,
|
|
22
|
+
): RefObject<T | null> {
|
|
23
|
+
const ref = useRef<T | null>(null);
|
|
24
|
+
const privateBox = useRef<Box | null>(null);
|
|
25
|
+
const playing = useRef<{ play: Playback; el: T } | null>(null);
|
|
26
|
+
const { scale, timing } = tuning;
|
|
27
|
+
|
|
28
|
+
useLayoutEffect(() => {
|
|
29
|
+
const el = ref.current;
|
|
30
|
+
if (!el || !enabled) return;
|
|
31
|
+
const live = playing.current;
|
|
32
|
+
const from = live && live.el === el ? measure(el) : sharedId === null ? privateBox.current : readShared(sharedId);
|
|
33
|
+
live?.play.stop();
|
|
34
|
+
const settled = measure(el);
|
|
35
|
+
if (sharedId === null) privateBox.current = settled;
|
|
36
|
+
else keepShared(sharedId, settled);
|
|
37
|
+
const next = from && !UIMotion.reduced ? flip(el, from, { scale, timing }) : null;
|
|
38
|
+
playing.current = next ? { play: next, el } : null;
|
|
39
|
+
next?.finished.then(() => {
|
|
40
|
+
if (playing.current?.play === next) playing.current = null;
|
|
41
|
+
});
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
useLayoutEffect(() => {
|
|
45
|
+
if (!enabled || sharedId === null) return;
|
|
46
|
+
return () => {
|
|
47
|
+
const live = playing.current;
|
|
48
|
+
if (live) {
|
|
49
|
+
const box = measure(live.el);
|
|
50
|
+
if (box.width && box.height) keepShared(sharedId, box);
|
|
51
|
+
live.play.stop();
|
|
52
|
+
}
|
|
53
|
+
const mine = readShared(sharedId);
|
|
54
|
+
queueMicrotask(() => {
|
|
55
|
+
if (readShared(sharedId) === mine) dropShared(sharedId);
|
|
56
|
+
});
|
|
57
|
+
};
|
|
58
|
+
}, [sharedId, enabled]);
|
|
59
|
+
|
|
60
|
+
return ref;
|
|
61
|
+
}
|
|
@@ -7,11 +7,12 @@ export interface PresenceState {
|
|
|
7
7
|
isPresent: boolean;
|
|
8
8
|
/** False for children present at the Presence's own first paint when `initial={false}`. */
|
|
9
9
|
initial: boolean;
|
|
10
|
-
/**
|
|
11
|
-
|
|
10
|
+
/** Claims responsibility for animating this child out; call the returned release once the
|
|
11
|
+
* exit has finished. A child nobody claims is dropped as soon as it leaves `children`. */
|
|
12
|
+
claimExit: () => () => void;
|
|
12
13
|
}
|
|
13
14
|
|
|
14
|
-
const DETACHED: PresenceState = { isPresent: true, initial: true,
|
|
15
|
+
const DETACHED: PresenceState = { isPresent: true, initial: true, claimExit: () => () => {} };
|
|
15
16
|
|
|
16
17
|
export const PresenceContext = createContext<PresenceState>(DETACHED);
|
|
17
18
|
|
package/src/motion/presence.tsx
CHANGED
|
@@ -47,6 +47,29 @@ export function Presence({ children, initial = true, mode = 'sync', onExitComple
|
|
|
47
47
|
const wasExiting = useRef(false);
|
|
48
48
|
const complete = useRef(onExitComplete);
|
|
49
49
|
complete.current = onExitComplete;
|
|
50
|
+
const claims = useRef(new Map<string, Set<object>>());
|
|
51
|
+
|
|
52
|
+
const drop = (key: string) =>
|
|
53
|
+
setShown((previous) => {
|
|
54
|
+
const next = previous.filter((entry) => entry.key !== key || !entry.exiting);
|
|
55
|
+
return next.length === previous.length ? previous : next;
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
const claimExit = (key: string) => (): (() => void) => {
|
|
59
|
+
let tokens = claims.current.get(key);
|
|
60
|
+
if (!tokens) {
|
|
61
|
+
tokens = new Set<object>();
|
|
62
|
+
claims.current.set(key, tokens);
|
|
63
|
+
}
|
|
64
|
+
const token = {};
|
|
65
|
+
const held = tokens;
|
|
66
|
+
held.add(token);
|
|
67
|
+
return () => {
|
|
68
|
+
if (!held.delete(token) || held.size) return;
|
|
69
|
+
if (claims.current.get(key) === held) claims.current.delete(key);
|
|
70
|
+
drop(key);
|
|
71
|
+
};
|
|
72
|
+
};
|
|
50
73
|
|
|
51
74
|
useLayoutEffect(() => {
|
|
52
75
|
setShown((previous) => {
|
|
@@ -54,6 +77,7 @@ export function Presence({ children, initial = true, mode = 'sync', onExitComple
|
|
|
54
77
|
const merged: Entry[] = incoming.map((entry) => ({ ...entry }));
|
|
55
78
|
previous.forEach((entry, index) => {
|
|
56
79
|
if (live.has(entry.key)) return;
|
|
80
|
+
if (!claims.current.get(entry.key)?.size) return;
|
|
57
81
|
merged.splice(index, 0, entry.exiting ? entry : { ...entry, exiting: true });
|
|
58
82
|
});
|
|
59
83
|
return merged;
|
|
@@ -81,7 +105,7 @@ export function Presence({ children, initial = true, mode = 'sync', onExitComple
|
|
|
81
105
|
value: {
|
|
82
106
|
isPresent: !entry.exiting,
|
|
83
107
|
initial: initial || !bornAtMount.current.has(entry.key),
|
|
84
|
-
|
|
108
|
+
claimExit: claimExit(entry.key),
|
|
85
109
|
},
|
|
86
110
|
},
|
|
87
111
|
fresh.get(entry.key) ?? entry.node,
|
package/src/motion/use-motion.ts
CHANGED
|
@@ -36,12 +36,23 @@ export function useMotion(ref: RefObject<HTMLElement | null>, { animate, exit, i
|
|
|
36
36
|
const mounted = useRef(false);
|
|
37
37
|
const spec = useRef({ animate, exit, initial });
|
|
38
38
|
spec.current = { animate, exit, initial };
|
|
39
|
+
const release = useRef<(() => void) | null>(null);
|
|
40
|
+
const ownsExit = !!exit;
|
|
39
41
|
|
|
40
42
|
const stop = () => {
|
|
41
43
|
for (const play of playing.current) play.stop();
|
|
42
44
|
playing.current = [];
|
|
43
45
|
};
|
|
44
46
|
|
|
47
|
+
useLayoutEffect(() => {
|
|
48
|
+
if (!ownsExit) return;
|
|
49
|
+
release.current = presence.claimExit();
|
|
50
|
+
return () => {
|
|
51
|
+
release.current?.();
|
|
52
|
+
release.current = null;
|
|
53
|
+
};
|
|
54
|
+
}, [ownsExit]); // eslint-disable-line react-hooks/exhaustive-deps
|
|
55
|
+
|
|
45
56
|
useLayoutEffect(() => {
|
|
46
57
|
const el = ref.current;
|
|
47
58
|
const atMount = !mounted.current;
|
|
@@ -53,11 +64,11 @@ export function useMotion(ref: RefObject<HTMLElement | null>, { animate, exit, i
|
|
|
53
64
|
}, deps ?? []); // eslint-disable-line react-hooks/exhaustive-deps
|
|
54
65
|
|
|
55
66
|
useLayoutEffect(() => {
|
|
56
|
-
if (presence.isPresent) return;
|
|
67
|
+
if (presence.isPresent || !ownsExit) return;
|
|
57
68
|
const el = ref.current;
|
|
58
69
|
stop();
|
|
59
70
|
playing.current = el ? run(spec.current.exit, el) : [];
|
|
60
|
-
allSettled(playing.current).then(
|
|
71
|
+
allSettled(playing.current).then(() => release.current?.());
|
|
61
72
|
}, [presence.isPresent]); // eslint-disable-line react-hooks/exhaustive-deps
|
|
62
73
|
|
|
63
74
|
useLayoutEffect(() => stop, []);
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/components/composites/date-picker/calendar-panel.tsx"],"names":[],"mappings":"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","file":"chunk-2HORSLXJ.js","sourcesContent":["'use client';\n\nimport './date-picker.css';\nimport { useEffect, useId, useRef, useState, type KeyboardEvent, type ReactNode } from 'react';\nimport { animate } from '../../../engine';\nimport { UIMotion } from '../../../tokens/motion-tokens';\nimport { Icon } from '../../internal/icon/Icon';\nimport { Button } from '../../primitives/button/Button';\nimport { GlidePill, useGlide } from '../../../motion/glide';\nimport { useSharedFlip } from '../../../motion/flip';\nimport { useDayFocus } from './use-day-focus';\nimport { MONTHS, DOW, toKey, parse, col, today, grid, tzLabel, within } from './date-utils';\nimport { cx } from '../../internal/utils/cx';\n\ninterface DtpPanelProps {\n val: string | null;\n commit: (key: string) => void;\n close: () => void;\n min?: string;\n max?: string;\n timezone?: string;\n label?: string;\n slot?: ReactNode;\n}\n\nexport function DtpPanel({ val, commit, min, max, timezone, label, close, slot }: DtpPanelProps) {\n const seed = val ? parse(val) : new Date();\n const [view, setView] = useState<{ y: number; m: number }>({ y: seed.getFullYear(), m: seed.getMonth() });\n const [focusKey, setFocusKey] = useState<string>(val || today());\n\n const daysRef = useRef<HTMLDivElement>(null);\n const prevViewRef = useRef<number | null>(null);\n const uid = useId();\n const pillId = 'dtp-pill-' + uid;\n\n const glide = useGlide(daysRef);\n const pillRef = useSharedFlip<HTMLSpanElement>(pillId, { timing: UIMotion.t.settle });\n\n const inRange = (key: string): boolean => within(key, min, max);\n\n function pickDay(key: string) {\n if (!inRange(key)) return;\n armFocus();\n setFocusKey(key);\n const d = parse(key);\n goToMonth(d.getFullYear(), d.getMonth());\n commit(key);\n }\n\n function goToMonth(y: number, m: number) {\n setView((v) => (v.y === y && v.m === m ? v : { y: y, m: m }));\n }\n function nav(dir: number) {\n const d = new Date(view.y, view.m + dir, 1);\n goToMonth(d.getFullYear(), d.getMonth());\n }\n const now = new Date();\n const viewIsCurrent = view.y === now.getFullYear() && view.m === now.getMonth();\n function goToToday() {\n const key = today();\n const d = parse(key);\n armFocus();\n setFocusKey(key);\n goToMonth(d.getFullYear(), d.getMonth());\n }\n const viewIdx = view.y * 12 + view.m;\n const armFocus = useDayFocus(daysRef, focusKey, viewIdx, '[tabindex=\"0\"]:not(:disabled)');\n useEffect(() => {\n const prev = prevViewRef.current;\n prevViewRef.current = viewIdx;\n const el = daysRef.current;\n if (prev == null || prev === viewIdx || !el) return;\n const dir = viewIdx > prev ? 1 : -1;\n animate(el, { x: [dir * 16, 0], opacity: [0, 1], timing: UIMotion.t.enter });\n }, [viewIdx]);\n\n function moveFocus(deltaDays: number, deltaMonths: number) {\n const d = parse(focusKey);\n if (deltaMonths) d.setMonth(d.getMonth() + deltaMonths);\n if (deltaDays) d.setDate(d.getDate() + deltaDays);\n const key = toKey(d);\n armFocus();\n setFocusKey(key);\n goToMonth(d.getFullYear(), d.getMonth());\n }\n function onGridKeyDown(e: KeyboardEvent<HTMLDivElement>) {\n const k = e.key;\n if (k === 'ArrowLeft') moveFocus(-1, 0);\n else if (k === 'ArrowRight') moveFocus(1, 0);\n else if (k === 'ArrowUp') moveFocus(-7, 0);\n else if (k === 'ArrowDown') moveFocus(7, 0);\n else if (k === 'PageUp') moveFocus(0, -1);\n else if (k === 'PageDown') moveFocus(0, 1);\n else if (k === 'Home') moveFocus(-col(parse(focusKey)), 0);\n else if (k === 'End') moveFocus(6 - col(parse(focusKey)), 0);\n else if (k === 'Enter' || k === ' ') pickDay(focusKey);\n else return;\n e.preventDefault();\n }\n\n const days = grid(view.y, view.m);\n const todayKey = today();\n const selKey = val || null;\n const gridKeys = days.map(toKey);\n const tabKey =\n gridKeys.indexOf(focusKey) >= 0\n ? focusKey\n : selKey && gridKeys.indexOf(selKey) >= 0\n ? selKey\n : gridKeys.indexOf(todayKey) >= 0\n ? todayKey\n : toKey(new Date(view.y, view.m, 1));\n\n const prevEnd = toKey(new Date(view.y, view.m, 0));\n const nextStart = toKey(new Date(view.y, view.m + 1, 1));\n const canPrev = !min || prevEnd >= min;\n const canNext = !max || nextStart <= max;\n\n const weeks: Date[][] = [];\n for (let w = 0; w < 6; w++) weeks.push(days.slice(w * 7, w * 7 + 7));\n\n return (\n <div className=\"dtp\" role=\"dialog\" aria-label={label || 'Pick a date'}>\n <div className=\"dtp__cal\">\n <div className=\"dtp__head\">\n <span className=\"dtp__month\" aria-live=\"polite\">\n {MONTHS[view.m]} <span className=\"dtp__year\">{view.y}</span>\n </span>\n <div className=\"dtp__navs\">\n <button\n type=\"button\"\n className=\"dtp__nav\"\n aria-label=\"Previous month\"\n disabled={!canPrev}\n onClick={() => nav(-1)}\n >\n <Icon name=\"caret-left\" size=\"sm\" />\n </button>\n <button\n type=\"button\"\n className=\"dtp__nav\"\n aria-label=\"Go to today\"\n disabled={viewIsCurrent}\n onClick={goToToday}\n >\n <Icon name=\"calendar-dot\" size=\"sm\" />\n </button>\n <button\n type=\"button\"\n className=\"dtp__nav\"\n aria-label=\"Next month\"\n disabled={!canNext}\n onClick={() => nav(1)}\n >\n <Icon name=\"caret-right\" size=\"sm\" />\n </button>\n </div>\n </div>\n <div className=\"dtp__dow\" aria-hidden=\"true\">\n {DOW.map((d) => (\n <span key={d}>{d}</span>\n ))}\n </div>\n <div\n className=\"dtp__days\"\n ref={daysRef}\n role=\"grid\"\n aria-label=\"Calendar\"\n onKeyDown={onGridKeyDown}\n onPointerLeave={glide.leave}\n >\n {weeks.map((week, wi) => (\n <div key={wi} role=\"row\" className=\"dtp__row\">\n {week.map((d) => {\n const key = toKey(d);\n const out = d.getMonth() !== view.m;\n const sel = key === selKey;\n const cls = cx('dtp__day', out && 'is-out', sel && 'is-selected', key === todayKey && 'is-today');\n return (\n <button\n key={key}\n type=\"button\"\n role=\"gridcell\"\n data-key={key}\n className={cls}\n disabled={!inRange(key)}\n tabIndex={key === tabKey ? 0 : -1}\n aria-selected={sel || undefined}\n aria-label={MONTHS[d.getMonth()] + ' ' + d.getDate() + ', ' + d.getFullYear()}\n onClick={() => pickDay(key)}\n onPointerEnter={(e) => glide.enter(e.currentTarget)}\n >\n {sel ? <span className=\"dtp__pill\" aria-hidden=\"true\" ref={pillRef}></span> : null}\n <span className=\"dtp__num\">{d.getDate()}</span>\n {key === todayKey ? <span className=\"dtp__dot\" aria-hidden=\"true\"></span> : null}\n </button>\n );\n })}\n </div>\n ))}\n <GlidePill className=\"dtp__hover\" glide={glide} />\n </div>\n </div>\n\n {slot || null}\n\n <div className=\"dtp__foot\">\n {timezone ? <span className=\"dtp__tz\">{tzLabel(timezone, selKey)}</span> : null}\n <span className=\"dtp__footSpacer\"></span>\n <Button variant=\"primary\" size=\"sm\" onClick={close}>\n Done\n </Button>\n </div>\n </div>\n );\n}\n"]}
|
package/dist/chunk-36WVAQKL.js
DELETED
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
'use client';import { useMotion } from './chunk-W3SHPKSB.js';
|
|
2
|
-
import { useSelfFlip, useSharedFlip } from './chunk-JSKXZMLH.js';
|
|
3
|
-
import { useRef, useCallback, createElement } from 'react';
|
|
4
|
-
|
|
5
|
-
function Motion({
|
|
6
|
-
as = "div",
|
|
7
|
-
animate,
|
|
8
|
-
exit,
|
|
9
|
-
initial,
|
|
10
|
-
deps,
|
|
11
|
-
layout,
|
|
12
|
-
layoutId,
|
|
13
|
-
ref,
|
|
14
|
-
children,
|
|
15
|
-
...rest
|
|
16
|
-
}) {
|
|
17
|
-
const tuning = typeof layout === "object" ? layout : {};
|
|
18
|
-
const selfRef = useSelfFlip(tuning, !!layout && !layoutId);
|
|
19
|
-
const sharedRef = useSharedFlip(layoutId ?? "", tuning, !!layoutId);
|
|
20
|
-
const host = useRef(null);
|
|
21
|
-
const forwarded = useRef(ref);
|
|
22
|
-
forwarded.current = ref;
|
|
23
|
-
useMotion(host, { animate, exit, initial, deps });
|
|
24
|
-
const attach = useCallback(
|
|
25
|
-
(el) => {
|
|
26
|
-
host.current = el;
|
|
27
|
-
selfRef.current = el;
|
|
28
|
-
sharedRef.current = el;
|
|
29
|
-
const outer = forwarded.current;
|
|
30
|
-
if (typeof outer === "function") outer(el);
|
|
31
|
-
else if (outer) outer.current = el;
|
|
32
|
-
},
|
|
33
|
-
[selfRef, sharedRef]
|
|
34
|
-
);
|
|
35
|
-
return createElement(as, { ...rest, ref: attach }, children);
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
export { Motion };
|
|
39
|
-
//# sourceMappingURL=chunk-36WVAQKL.js.map
|
|
40
|
-
//# sourceMappingURL=chunk-36WVAQKL.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/motion/element.tsx"],"names":[],"mappings":";;;;AAkBO,SAAS,MAAA,CAAO;AAAA,EACrB,EAAA,GAAK,KAAA;AAAA,EACL,OAAA;AAAA,EACA,IAAA;AAAA,EACA,OAAA;AAAA,EACA,IAAA;AAAA,EACA,MAAA;AAAA,EACA,QAAA;AAAA,EACA,GAAA;AAAA,EACA,QAAA;AAAA,EACA,GAAG;AACL,CAAA,EAAgB;AACd,EAAA,MAAM,MAAA,GAAS,OAAO,MAAA,KAAW,QAAA,GAAW,SAAS,EAAC;AACtD,EAAA,MAAM,UAAU,WAAA,CAAyB,MAAA,EAAQ,CAAC,CAAC,MAAA,IAAU,CAAC,QAAQ,CAAA;AACtE,EAAA,MAAM,YAAY,aAAA,CAA2B,QAAA,IAAY,IAAI,MAAA,EAAQ,CAAC,CAAC,QAAQ,CAAA;AAC/E,EAAA,MAAM,IAAA,GAAO,OAA2B,IAAI,CAAA;AAC5C,EAAA,MAAM,SAAA,GAAY,OAAO,GAAG,CAAA;AAC5B,EAAA,SAAA,CAAU,OAAA,GAAU,GAAA;AAEpB,EAAA,SAAA,CAAU,MAAM,EAAE,OAAA,EAAS,IAAA,EAAM,OAAA,EAAS,MAAM,CAAA;AAEhD,EAAA,MAAM,MAAA,GAAS,WAAA;AAAA,IACb,CAAC,EAAA,KAA2B;AAC1B,MAAA,IAAA,CAAK,OAAA,GAAU,EAAA;AACf,MAAA,OAAA,CAAQ,OAAA,GAAU,EAAA;AAClB,MAAA,SAAA,CAAU,OAAA,GAAU,EAAA;AACpB,MAAA,MAAM,QAAQ,SAAA,CAAU,OAAA;AACxB,MAAA,IAAI,OAAO,KAAA,KAAU,UAAA,EAAY,KAAA,CAAM,EAAE,CAAA;AAAA,WAAA,IAChC,KAAA,QAAa,OAAA,GAAU,EAAA;AAAA,IAClC,CAAA;AAAA,IACA,CAAC,SAAS,SAAS;AAAA,GACrB;AAEA,EAAA,OAAO,aAAA,CAAc,IAAI,EAAE,GAAG,MAAM,GAAA,EAAK,MAAA,IAAU,QAAQ,CAAA;AAC7D","file":"chunk-36WVAQKL.js","sourcesContent":["'use client';\n\nimport { createElement, useCallback, useRef, type ReactNode, type Ref } from 'react';\nimport { useSelfFlip, useSharedFlip, type FlipTuning } from './flip';\nimport { useMotion, type MotionSpecs } from './use-motion';\n\nexport interface MotionProps extends MotionSpecs {\n /** Tag for the element this renders. @default 'div' */\n as?: string;\n /** FLIP this element from its previous box on every render; an object tunes the timing. */\n layout?: boolean | FlipTuning;\n /** FLIP from whatever element last held this id, so the box travels between nodes. */\n layoutId?: string;\n ref?: Ref<HTMLElement | null>;\n children?: ReactNode;\n [prop: string]: unknown;\n}\n\nexport function Motion({\n as = 'div',\n animate,\n exit,\n initial,\n deps,\n layout,\n layoutId,\n ref,\n children,\n ...rest\n}: MotionProps) {\n const tuning = typeof layout === 'object' ? layout : {};\n const selfRef = useSelfFlip<HTMLElement>(tuning, !!layout && !layoutId);\n const sharedRef = useSharedFlip<HTMLElement>(layoutId ?? '', tuning, !!layoutId);\n const host = useRef<HTMLElement | null>(null);\n const forwarded = useRef(ref);\n forwarded.current = ref;\n\n useMotion(host, { animate, exit, initial, deps });\n\n const attach = useCallback(\n (el: HTMLElement | null) => {\n host.current = el;\n selfRef.current = el;\n sharedRef.current = el;\n const outer = forwarded.current;\n if (typeof outer === 'function') outer(el);\n else if (outer) outer.current = el;\n },\n [selfRef, sharedRef],\n );\n\n return createElement(as, { ...rest, ref: attach }, children);\n}\n"]}
|
package/dist/chunk-JSKXZMLH.js
DELETED
|
@@ -1,67 +0,0 @@
|
|
|
1
|
-
'use client';import { measure, flip, readShared, keepShared, dropShared } from './chunk-L3Q3VDIW.js';
|
|
2
|
-
import { UIMotion } from './chunk-S5ZTZANS.js';
|
|
3
|
-
import { useRef, useLayoutEffect } from 'react';
|
|
4
|
-
|
|
5
|
-
function useSelfFlip(tuning = {}, enabled = true) {
|
|
6
|
-
const ref = useRef(null);
|
|
7
|
-
const previous = useRef(null);
|
|
8
|
-
const playing = useRef(null);
|
|
9
|
-
const { scale, timing } = tuning;
|
|
10
|
-
useLayoutEffect(() => {
|
|
11
|
-
const el = ref.current;
|
|
12
|
-
if (!el || !enabled) return;
|
|
13
|
-
const from = playing.current ? measure(el) : previous.current;
|
|
14
|
-
playing.current?.stop();
|
|
15
|
-
previous.current = measure(el);
|
|
16
|
-
const next = from && !UIMotion.reduced ? flip(el, from, { scale, timing }) : null;
|
|
17
|
-
playing.current = next;
|
|
18
|
-
next?.finished.then(() => {
|
|
19
|
-
if (playing.current === next) playing.current = null;
|
|
20
|
-
});
|
|
21
|
-
});
|
|
22
|
-
useLayoutEffect(
|
|
23
|
-
() => () => {
|
|
24
|
-
playing.current?.stop();
|
|
25
|
-
},
|
|
26
|
-
[]
|
|
27
|
-
);
|
|
28
|
-
return ref;
|
|
29
|
-
}
|
|
30
|
-
function useSharedFlip(id, tuning = {}, enabled = true) {
|
|
31
|
-
const ref = useRef(null);
|
|
32
|
-
const playing = useRef(null);
|
|
33
|
-
const { scale, timing } = tuning;
|
|
34
|
-
useLayoutEffect(() => {
|
|
35
|
-
const el = ref.current;
|
|
36
|
-
if (!el || !enabled) return;
|
|
37
|
-
const live = playing.current;
|
|
38
|
-
const from = live && live.el === el ? measure(el) : readShared(id);
|
|
39
|
-
live?.play.stop();
|
|
40
|
-
keepShared(id, measure(el));
|
|
41
|
-
const next = from && !UIMotion.reduced ? flip(el, from, { scale, timing }) : null;
|
|
42
|
-
playing.current = next ? { play: next, el } : null;
|
|
43
|
-
next?.finished.then(() => {
|
|
44
|
-
if (playing.current?.play === next) playing.current = null;
|
|
45
|
-
});
|
|
46
|
-
});
|
|
47
|
-
useLayoutEffect(() => {
|
|
48
|
-
if (!enabled) return;
|
|
49
|
-
return () => {
|
|
50
|
-
const live = playing.current;
|
|
51
|
-
if (live) {
|
|
52
|
-
const box = measure(live.el);
|
|
53
|
-
if (box.width && box.height) keepShared(id, box);
|
|
54
|
-
live.play.stop();
|
|
55
|
-
}
|
|
56
|
-
const mine = readShared(id);
|
|
57
|
-
queueMicrotask(() => {
|
|
58
|
-
if (readShared(id) === mine) dropShared(id);
|
|
59
|
-
});
|
|
60
|
-
};
|
|
61
|
-
}, [id, enabled]);
|
|
62
|
-
return ref;
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
export { useSelfFlip, useSharedFlip };
|
|
66
|
-
//# sourceMappingURL=chunk-JSKXZMLH.js.map
|
|
67
|
-
//# sourceMappingURL=chunk-JSKXZMLH.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/motion/flip.tsx"],"names":[],"mappings":"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","file":"chunk-JSKXZMLH.js","sourcesContent":["'use client';\n\nimport { useLayoutEffect, useRef, type RefObject } from 'react';\nimport {\n dropShared,\n flip,\n keepShared,\n measure,\n readShared,\n type Box,\n type FlipOptions,\n type Playback,\n} from '../engine';\nimport { UIMotion } from '../tokens/motion-tokens';\n\nexport type FlipTuning = FlipOptions;\n\nexport function useSelfFlip<T extends HTMLElement>(tuning: FlipTuning = {}, enabled = true): RefObject<T | null> {\n const ref = useRef<T | null>(null);\n const previous = useRef<Box | null>(null);\n const playing = useRef<Playback | null>(null);\n const { scale, timing } = tuning;\n\n useLayoutEffect(() => {\n const el = ref.current;\n if (!el || !enabled) return;\n const from = playing.current ? measure(el) : previous.current;\n playing.current?.stop();\n previous.current = measure(el);\n const next = from && !UIMotion.reduced ? flip(el, from, { scale, timing }) : null;\n playing.current = next;\n next?.finished.then(() => {\n if (playing.current === next) playing.current = null;\n });\n });\n\n useLayoutEffect(\n () => () => {\n playing.current?.stop();\n },\n [],\n );\n\n return ref;\n}\n\nexport function useSharedFlip<T extends HTMLElement>(\n id: string,\n tuning: FlipTuning = {},\n enabled = true,\n): RefObject<T | null> {\n const ref = useRef<T | null>(null);\n const playing = useRef<{ play: Playback; el: T } | null>(null);\n const { scale, timing } = tuning;\n\n useLayoutEffect(() => {\n const el = ref.current;\n if (!el || !enabled) return;\n const live = playing.current;\n const from = live && live.el === el ? measure(el) : readShared(id);\n live?.play.stop();\n keepShared(id, measure(el));\n const next = from && !UIMotion.reduced ? flip(el, from, { scale, timing }) : null;\n playing.current = next ? { play: next, el } : null;\n next?.finished.then(() => {\n if (playing.current?.play === next) playing.current = null;\n });\n });\n\n useLayoutEffect(() => {\n if (!enabled) return;\n return () => {\n const live = playing.current;\n if (live) {\n const box = measure(live.el);\n if (box.width && box.height) keepShared(id, box);\n live.play.stop();\n }\n const mine = readShared(id);\n queueMicrotask(() => {\n if (readShared(id) === mine) dropShared(id);\n });\n };\n }, [id, enabled]);\n\n return ref;\n}\n"]}
|