@mks2508/mks-ui 0.11.0 → 0.11.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/dist/css/primitives-GooeyMorphingSurface-gooey.css +9 -8
- package/dist/index.css +9 -8
- package/dist/react-ui/index.js +1 -1
- package/dist/react-ui/primitives/GooeyMorphingSurface/gooey-morphing.css +9 -8
- package/dist/react-ui/primitives/GooeyMorphingSurface/index.d.ts +0 -18
- package/dist/react-ui/primitives/GooeyMorphingSurface/index.d.ts.map +1 -1
- package/dist/react-ui/primitives/GooeyMorphingSurface/index.js +99 -19
- package/dist/react-ui/primitives/index.js +1 -1
- package/package.json +1 -1
|
@@ -41,21 +41,22 @@
|
|
|
41
41
|
position: absolute;
|
|
42
42
|
top: 0;
|
|
43
43
|
left: 50%;
|
|
44
|
-
|
|
44
|
+
/* translateZ(0) keeps the filtered region on its own GPU layer so Safari
|
|
45
|
+
* re-renders the gooey filter smoothly while the body rect's geometry is
|
|
46
|
+
* animated via WAAPI (Sileo Safari formula). */
|
|
47
|
+
transform: translateX(-50%) translateZ(0);
|
|
48
|
+
contain: layout style;
|
|
45
49
|
display: block;
|
|
46
50
|
overflow: visible;
|
|
47
51
|
pointer-events: none;
|
|
48
52
|
}
|
|
49
53
|
|
|
54
|
+
/* The body rect's x/y/width/height are driven by the Web Animations API
|
|
55
|
+
* (rect.animate, see index.tsx) — NOT a CSS transition, which WebKit refuses
|
|
56
|
+
* to interpolate on SVG geometry attributes. `will-change` is the only hint
|
|
57
|
+
* left here. */
|
|
50
58
|
.gms-body-rect {
|
|
51
59
|
will-change: x, y, width, height;
|
|
52
|
-
transition:
|
|
53
|
-
x var(--gms-duration) var(--gms-ease),
|
|
54
|
-
y var(--gms-duration) var(--gms-ease),
|
|
55
|
-
width var(--gms-duration) var(--gms-ease),
|
|
56
|
-
height var(--gms-duration) var(--gms-ease),
|
|
57
|
-
rx var(--gms-duration) var(--gms-ease),
|
|
58
|
-
ry var(--gms-duration) var(--gms-ease);
|
|
59
60
|
}
|
|
60
61
|
|
|
61
62
|
.gms-pill {
|
package/dist/index.css
CHANGED
|
@@ -1033,21 +1033,22 @@
|
|
|
1033
1033
|
position: absolute;
|
|
1034
1034
|
top: 0;
|
|
1035
1035
|
left: 50%;
|
|
1036
|
-
|
|
1036
|
+
/* translateZ(0) keeps the filtered region on its own GPU layer so Safari
|
|
1037
|
+
* re-renders the gooey filter smoothly while the body rect's geometry is
|
|
1038
|
+
* animated via WAAPI (Sileo Safari formula). */
|
|
1039
|
+
transform: translateX(-50%) translateZ(0);
|
|
1040
|
+
contain: layout style;
|
|
1037
1041
|
display: block;
|
|
1038
1042
|
overflow: visible;
|
|
1039
1043
|
pointer-events: none;
|
|
1040
1044
|
}
|
|
1041
1045
|
|
|
1046
|
+
/* The body rect's x/y/width/height are driven by the Web Animations API
|
|
1047
|
+
* (rect.animate, see index.tsx) — NOT a CSS transition, which WebKit refuses
|
|
1048
|
+
* to interpolate on SVG geometry attributes. `will-change` is the only hint
|
|
1049
|
+
* left here. */
|
|
1042
1050
|
.gms-body-rect {
|
|
1043
1051
|
will-change: x, y, width, height;
|
|
1044
|
-
transition:
|
|
1045
|
-
x var(--gms-duration) var(--gms-ease),
|
|
1046
|
-
y var(--gms-duration) var(--gms-ease),
|
|
1047
|
-
width var(--gms-duration) var(--gms-ease),
|
|
1048
|
-
height var(--gms-duration) var(--gms-ease),
|
|
1049
|
-
rx var(--gms-duration) var(--gms-ease),
|
|
1050
|
-
ry var(--gms-duration) var(--gms-ease);
|
|
1051
1052
|
}
|
|
1052
1053
|
|
|
1053
1054
|
.gms-pill {
|
package/dist/react-ui/index.js
CHANGED
|
@@ -7,9 +7,9 @@ import { useIsInView } from "./hooks/DOM/UseIsInView.js";
|
|
|
7
7
|
import { CountingNumber } from "./primitives/CountingNumber/index.js";
|
|
8
8
|
import { DOT_MATRIX_PATTERNS, buildDelays } from "./primitives/DotMatrix/patterns.js";
|
|
9
9
|
import { DotMatrix } from "./primitives/DotMatrix/index.js";
|
|
10
|
+
import { ANIMATION_CONFIGS, ANIMATION_DEFAULTS, EASINGS, EFFECTS, PRESETS, RESPONSIVE_CONFIGS, TIMING, TRANSFORMS, getResponsiveDuration, getResponsiveStagger } from "./primitives/waapi/core/animationConstants.js";
|
|
10
11
|
import { DEFAULT_GOOEY_CONFIG } from "./primitives/GooeyMorphingSurface/GooeyMorphingSurface.types.js";
|
|
11
12
|
import { GooeyMorphingSurface } from "./primitives/GooeyMorphingSurface/index.js";
|
|
12
|
-
import { ANIMATION_CONFIGS, ANIMATION_DEFAULTS, EASINGS, EFFECTS, PRESETS, RESPONSIVE_CONFIGS, TIMING, TRANSFORMS, getResponsiveDuration, getResponsiveStagger } from "./primitives/waapi/core/animationConstants.js";
|
|
13
13
|
import { useElementRegistry } from "./primitives/waapi/core/useElementRegistry.js";
|
|
14
14
|
import { usePositionCapture } from "./primitives/waapi/core/usePositionCapture.js";
|
|
15
15
|
import { useFLIPAnimation } from "./primitives/waapi/core/useFLIPAnimation.js";
|
|
@@ -41,21 +41,22 @@
|
|
|
41
41
|
position: absolute;
|
|
42
42
|
top: 0;
|
|
43
43
|
left: 50%;
|
|
44
|
-
|
|
44
|
+
/* translateZ(0) keeps the filtered region on its own GPU layer so Safari
|
|
45
|
+
* re-renders the gooey filter smoothly while the body rect's geometry is
|
|
46
|
+
* animated via WAAPI (Sileo Safari formula). */
|
|
47
|
+
transform: translateX(-50%) translateZ(0);
|
|
48
|
+
contain: layout style;
|
|
45
49
|
display: block;
|
|
46
50
|
overflow: visible;
|
|
47
51
|
pointer-events: none;
|
|
48
52
|
}
|
|
49
53
|
|
|
54
|
+
/* The body rect's x/y/width/height are driven by the Web Animations API
|
|
55
|
+
* (rect.animate, see index.tsx) — NOT a CSS transition, which WebKit refuses
|
|
56
|
+
* to interpolate on SVG geometry attributes. `will-change` is the only hint
|
|
57
|
+
* left here. */
|
|
50
58
|
.gms-body-rect {
|
|
51
59
|
will-change: x, y, width, height;
|
|
52
|
-
transition:
|
|
53
|
-
x var(--gms-duration) var(--gms-ease),
|
|
54
|
-
y var(--gms-duration) var(--gms-ease),
|
|
55
|
-
width var(--gms-duration) var(--gms-ease),
|
|
56
|
-
height var(--gms-duration) var(--gms-ease),
|
|
57
|
-
rx var(--gms-duration) var(--gms-ease),
|
|
58
|
-
ry var(--gms-duration) var(--gms-ease);
|
|
59
60
|
}
|
|
60
61
|
|
|
61
62
|
.gms-pill {
|
|
@@ -1,21 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* GooeyMorphingSurface — headless pill→card gooey morph primitive.
|
|
3
|
-
*
|
|
4
|
-
* Paints an SVG canvas where a pill-shaped rect sits on top and a card-
|
|
5
|
-
* shaped rect sits below (body). Both rects share a gaussian-blur +
|
|
6
|
-
* alpha-threshold filter that fuses them into a single silhouette with a
|
|
7
|
-
* soft metaball throat wherever they overlap. When `open` is false, the
|
|
8
|
-
* body rect is a geometric clone of the pill (same x/y/w/h/rx) — no extra
|
|
9
|
-
* halo, no pop. When `open` flips to true, all six SVG presentation attrs
|
|
10
|
-
* (x, y, width, height, rx, ry) interpolate in lock-step, producing a
|
|
11
|
-
* continuous morph from pill to card.
|
|
12
|
-
*
|
|
13
|
-
* The primitive is purely presentational: it does not own open state, does
|
|
14
|
-
* not bind click handlers, does not trap focus. Use `<GooeyButton>` for
|
|
15
|
-
* the ergonomic wrapper that adds those behaviours.
|
|
16
|
-
*
|
|
17
|
-
* @module @mks2508/mks-ui/react/primitives/GooeyMorphingSurface
|
|
18
|
-
*/
|
|
19
1
|
import './gooey-morphing-surface.css';
|
|
20
2
|
import { type IGooeyMorphingSurfaceProps } from './GooeyMorphingSurface.types';
|
|
21
3
|
export * from './GooeyMorphingSurface.types';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/react-ui/primitives/GooeyMorphingSurface/index.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/react-ui/primitives/GooeyMorphingSurface/index.tsx"],"names":[],"mappings":"AAuCA,OAAO,8BAA8B,CAAC;AACtC,OAAO,EAEN,KAAK,0BAA0B,EAC/B,MAAM,8BAA8B,CAAC;AAEtC,cAAc,8BAA8B,CAAC;AAoB7C;;;;;;;;;;;;GAYG;AACH,wBAAgB,oBAAoB,CAAC,EACpC,IAAI,EACJ,IAAI,EACJ,IAAI,EACJ,MAAM,EAAE,UAAU,EAClB,SAAS,EACT,KAAK,EACL,aAAa,EAAE,UAAiB,GAChC,EAAE,0BAA0B,2CAsL5B"}
|
|
@@ -1,7 +1,10 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
1
3
|
import { cn } from "../../lib/utils.js";
|
|
4
|
+
import { getResponsiveDuration } from "../waapi/core/animationConstants.js";
|
|
2
5
|
import "./gooey-morphing-surface.js";
|
|
3
6
|
import { DEFAULT_GOOEY_CONFIG } from "./GooeyMorphingSurface.types.js";
|
|
4
|
-
import { useId } from "react";
|
|
7
|
+
import { useEffect, useId, useRef, useState } from "react";
|
|
5
8
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
6
9
|
|
|
7
10
|
//#region src/react-ui/primitives/GooeyMorphingSurface/index.tsx
|
|
@@ -12,10 +15,23 @@ import { jsx, jsxs } from "react/jsx-runtime";
|
|
|
12
15
|
* shaped rect sits below (body). Both rects share a gaussian-blur +
|
|
13
16
|
* alpha-threshold filter that fuses them into a single silhouette with a
|
|
14
17
|
* soft metaball throat wherever they overlap. When `open` is false, the
|
|
15
|
-
* body rect is a geometric clone of the pill (same x/y/w/h
|
|
16
|
-
* halo, no pop. When `open` flips to true,
|
|
17
|
-
* (x, y, width, height
|
|
18
|
-
*
|
|
18
|
+
* body rect is a geometric clone of the pill (same x/y/w/h) — no extra
|
|
19
|
+
* halo, no pop. When `open` flips to true, the body rect's geometry
|
|
20
|
+
* (x, y, width, height) interpolates to the card, producing a continuous
|
|
21
|
+
* morph from pill to card.
|
|
22
|
+
*
|
|
23
|
+
* ── Cross-browser note (the reason this primitive is WAAPI-driven) ──
|
|
24
|
+
* The body rect morphs via the **Web Animations API** (`rect.animate(...)`),
|
|
25
|
+
* NOT a CSS `transition` on the SVG geometry attributes. WebKit/Safari does
|
|
26
|
+
* not interpolate `x/y/width/height` of an `<rect>` through a CSS transition
|
|
27
|
+
* (the morph hard-SNAPS), whereas it DOES animate them through WAAPI. This is
|
|
28
|
+
* the same "Sileo Safari formula" the `GooeyCanvas` primitive uses:
|
|
29
|
+
* - geometry animated via `rect.animate([from], [to], { fill: 'forwards' })`
|
|
30
|
+
* with explicit `px` units (Chrome WAAPI requires units on SVG geom props),
|
|
31
|
+
* - a 1-rAF readiness gate before the first animation,
|
|
32
|
+
* - `lastValues` ref so mid-flight reversals start from the live target,
|
|
33
|
+
* - `transform: translateZ(0)` + a static filter id so Safari keeps the
|
|
34
|
+
* filtered region on its own layer and re-renders it smoothly.
|
|
19
35
|
*
|
|
20
36
|
* The primitive is purely presentational: it does not own open state, does
|
|
21
37
|
* not bind click handlers, does not trap focus. Use `<GooeyButton>` for
|
|
@@ -23,6 +39,15 @@ import { jsx, jsxs } from "react/jsx-runtime";
|
|
|
23
39
|
*
|
|
24
40
|
* @module @mks2508/mks-ui/react/primitives/GooeyMorphingSurface
|
|
25
41
|
*/
|
|
42
|
+
/** Build a WAAPI keyframe from a geometry, with the `px` units Chrome needs. */
|
|
43
|
+
function geomKeyframe(g) {
|
|
44
|
+
return {
|
|
45
|
+
x: `${g.x}px`,
|
|
46
|
+
y: `${g.y}px`,
|
|
47
|
+
width: `${g.width}px`,
|
|
48
|
+
height: `${g.height}px`
|
|
49
|
+
};
|
|
50
|
+
}
|
|
26
51
|
/**
|
|
27
52
|
* Headless gooey pill→card morph surface.
|
|
28
53
|
*
|
|
@@ -45,11 +70,67 @@ function GooeyMorphingSurface({ open, pill, card, config: userConfig, className,
|
|
|
45
70
|
const pillRadius = pillHeight / 2;
|
|
46
71
|
const svgHeight = pillHeight + cardHeight + mergeOverlap;
|
|
47
72
|
const pillX = (cardWidth - pillWidth) / 2 + pillOffsetX;
|
|
48
|
-
const
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
73
|
+
const closedGeom = {
|
|
74
|
+
x: pillX,
|
|
75
|
+
y: 0,
|
|
76
|
+
width: pillWidth,
|
|
77
|
+
height: pillHeight
|
|
78
|
+
};
|
|
79
|
+
const openGeom = {
|
|
80
|
+
x: 0,
|
|
81
|
+
y: pillHeight - mergeOverlap,
|
|
82
|
+
width: cardWidth,
|
|
83
|
+
height: cardHeight + mergeOverlap
|
|
84
|
+
};
|
|
85
|
+
const bodyRectRef = useRef(null);
|
|
86
|
+
const animRef = useRef(null);
|
|
87
|
+
const lastValues = useRef(open ? openGeom : closedGeom);
|
|
88
|
+
const [ready, setReady] = useState(false);
|
|
89
|
+
useEffect(() => {
|
|
90
|
+
const rect = bodyRectRef.current;
|
|
91
|
+
if (!rect) return;
|
|
92
|
+
if (open) {
|
|
93
|
+
lastValues.current = openGeom;
|
|
94
|
+
rect.animate([geomKeyframe(openGeom)], {
|
|
95
|
+
duration: 0,
|
|
96
|
+
fill: "forwards"
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
const raf = requestAnimationFrame(() => setReady(true));
|
|
100
|
+
return () => cancelAnimationFrame(raf);
|
|
101
|
+
}, []);
|
|
102
|
+
useEffect(() => {
|
|
103
|
+
const rect = bodyRectRef.current;
|
|
104
|
+
if (!rect || !ready) return;
|
|
105
|
+
const to = open ? openGeom : closedGeom;
|
|
106
|
+
const from = lastValues.current;
|
|
107
|
+
lastValues.current = to;
|
|
108
|
+
const duration = getResponsiveDuration(durationMs);
|
|
109
|
+
if (animRef.current) animRef.current.cancel();
|
|
110
|
+
if (duration === 0) {
|
|
111
|
+
rect.animate([geomKeyframe(to)], {
|
|
112
|
+
duration: 0,
|
|
113
|
+
fill: "forwards"
|
|
114
|
+
});
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
animRef.current = rect.animate([geomKeyframe(from), geomKeyframe(to)], {
|
|
118
|
+
duration,
|
|
119
|
+
easing,
|
|
120
|
+
fill: "forwards"
|
|
121
|
+
});
|
|
122
|
+
}, [
|
|
123
|
+
ready,
|
|
124
|
+
open,
|
|
125
|
+
durationMs,
|
|
126
|
+
easing,
|
|
127
|
+
pillWidth,
|
|
128
|
+
pillHeight,
|
|
129
|
+
cardWidth,
|
|
130
|
+
cardHeight,
|
|
131
|
+
mergeOverlap,
|
|
132
|
+
pillOffsetX
|
|
133
|
+
]);
|
|
53
134
|
const rootStyle = {
|
|
54
135
|
"--gms-pill-w": `${pillWidth}px`,
|
|
55
136
|
"--gms-pill-h": `${pillHeight}px`,
|
|
@@ -111,16 +192,15 @@ function GooeyMorphingSurface({ open, pill, card, config: userConfig, className,
|
|
|
111
192
|
ry: pillRadius,
|
|
112
193
|
fill
|
|
113
194
|
}), /* @__PURE__ */ jsx("rect", {
|
|
195
|
+
ref: bodyRectRef,
|
|
114
196
|
className: "gms-body-rect",
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
ry: bodyR
|
|
123
|
-
}
|
|
197
|
+
x: closedGeom.x,
|
|
198
|
+
y: closedGeom.y,
|
|
199
|
+
width: closedGeom.width,
|
|
200
|
+
height: closedGeom.height,
|
|
201
|
+
rx: cardRadius,
|
|
202
|
+
ry: cardRadius,
|
|
203
|
+
fill
|
|
124
204
|
})]
|
|
125
205
|
})]
|
|
126
206
|
}),
|
|
@@ -4,9 +4,9 @@ import { AutoHeight } from "./AutoHeight/index.js";
|
|
|
4
4
|
import { CountingNumber } from "./CountingNumber/index.js";
|
|
5
5
|
import { DOT_MATRIX_PATTERNS, buildDelays } from "./DotMatrix/patterns.js";
|
|
6
6
|
import { DotMatrix } from "./DotMatrix/index.js";
|
|
7
|
+
import { ANIMATION_CONFIGS, ANIMATION_DEFAULTS, EASINGS, EFFECTS, PRESETS, RESPONSIVE_CONFIGS, TIMING, TRANSFORMS, getResponsiveDuration, getResponsiveStagger } from "./waapi/core/animationConstants.js";
|
|
7
8
|
import { DEFAULT_GOOEY_CONFIG } from "./GooeyMorphingSurface/GooeyMorphingSurface.types.js";
|
|
8
9
|
import { GooeyMorphingSurface } from "./GooeyMorphingSurface/index.js";
|
|
9
|
-
import { ANIMATION_CONFIGS, ANIMATION_DEFAULTS, EASINGS, EFFECTS, PRESETS, RESPONSIVE_CONFIGS, TIMING, TRANSFORMS, getResponsiveDuration, getResponsiveStagger } from "./waapi/core/animationConstants.js";
|
|
10
10
|
import { useElementRegistry } from "./waapi/core/useElementRegistry.js";
|
|
11
11
|
import { usePositionCapture } from "./waapi/core/usePositionCapture.js";
|
|
12
12
|
import { useFLIPAnimation } from "./waapi/core/useFLIPAnimation.js";
|