@otfdashkit/ui-native 0.1.3 → 0.1.5
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/index.d.ts +4 -3
- package/dist/index.js +4 -4
- package/dist/index.js.map +1 -1
- package/dist/index.mjs.map +1 -1
- package/package.json +4 -15
- package/dist/skia.d.ts +0 -14
- package/dist/skia.js +0 -317
- package/dist/skia.js.map +0 -1
- package/dist/skia.mjs +0 -310
- package/dist/skia.mjs.map +0 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@otfdashkit/ui-native",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.5",
|
|
4
4
|
"description": "OTF UI Native — Tamagui-powered component library for React Native + Expo. Same component API as @otfdashkit/ui (web) — port a screen by changing the import.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"otfdashkit",
|
|
@@ -34,11 +34,6 @@
|
|
|
34
34
|
"types": "./dist/index.d.ts",
|
|
35
35
|
"import": "./dist/index.mjs",
|
|
36
36
|
"require": "./dist/index.js"
|
|
37
|
-
},
|
|
38
|
-
"./skia": {
|
|
39
|
-
"types": "./dist/skia.d.ts",
|
|
40
|
-
"import": "./dist/skia.mjs",
|
|
41
|
-
"require": "./dist/skia.js"
|
|
42
37
|
}
|
|
43
38
|
},
|
|
44
39
|
"files": [
|
|
@@ -63,35 +58,29 @@
|
|
|
63
58
|
"typescript": "^5.0.0"
|
|
64
59
|
},
|
|
65
60
|
"peerDependencies": {
|
|
66
|
-
"@shopify/react-native-skia": ">=2.0.0",
|
|
67
61
|
"@tamagui/config": ">=1.100.0",
|
|
68
62
|
"@tamagui/core": ">=1.100.0",
|
|
69
63
|
"@tamagui/lucide-icons": ">=1.100.0",
|
|
64
|
+
"expo-router": ">=6.0.0",
|
|
70
65
|
"react": ">=18.0.0",
|
|
71
66
|
"react-native": ">=0.76.0",
|
|
67
|
+
"react-native-reanimated": ">=4.0.0",
|
|
72
68
|
"react-native-svg": ">=13.0.0",
|
|
73
|
-
"react-native-worklets": ">=0.5.0",
|
|
74
69
|
"tamagui": ">=1.100.0"
|
|
75
70
|
},
|
|
76
71
|
"peerDependenciesMeta": {
|
|
77
|
-
"@shopify/react-native-skia": {
|
|
78
|
-
"optional": true
|
|
79
|
-
},
|
|
80
72
|
"expo-router": {
|
|
81
73
|
"optional": true
|
|
82
74
|
},
|
|
83
75
|
"react-native-reanimated": {
|
|
84
76
|
"optional": true
|
|
85
|
-
},
|
|
86
|
-
"react-native-worklets": {
|
|
87
|
-
"optional": true
|
|
88
77
|
}
|
|
89
78
|
},
|
|
90
79
|
"publishConfig": {
|
|
91
80
|
"access": "public"
|
|
92
81
|
},
|
|
93
82
|
"scripts": {
|
|
94
|
-
"build": "tsup && cp types/index.d.ts dist/index.d.ts
|
|
83
|
+
"build": "tsup && cp types/index.d.ts dist/index.d.ts",
|
|
95
84
|
"dev": "tsup --watch",
|
|
96
85
|
"type-check": "tsc --noEmit",
|
|
97
86
|
"clean": "rm -rf dist"
|
package/dist/skia.d.ts
DELETED
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
// Hand-written type stubs for `@otfdashkit/ui-native/skia` — the Skia-backed
|
|
2
|
-
// subpath of the SDK. tsup `dts: false`, see tsup.config.ts for the rationale
|
|
3
|
-
// (Tamagui's React-18-pinned types collide with Expo SDK 54's hoisted React
|
|
4
|
-
// 19). Mirror of `dist/skia.{mjs,js}` exports.
|
|
5
|
-
//
|
|
6
|
-
// Subpath exists so the main `@otfdashkit/ui-native` entry stays Skia-free.
|
|
7
|
-
// See `docs/sdk-design.md#subpath-exports`.
|
|
8
|
-
|
|
9
|
-
export const Shockwave: any
|
|
10
|
-
export type ShockwaveProps = any
|
|
11
|
-
export type ShockwaveSlotProps = any
|
|
12
|
-
export type ShockwaveContextValue = any
|
|
13
|
-
export type ShockwaveOrigin = any
|
|
14
|
-
export type ShockwaveValue = 'from' | 'to'
|
package/dist/skia.js
DELETED
|
@@ -1,317 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __defProp = Object.defineProperty;
|
|
3
|
-
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
-
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
-
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
-
var __export = (target, all) => {
|
|
7
|
-
for (var name in all)
|
|
8
|
-
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
-
};
|
|
10
|
-
var __copyProps = (to, from, except, desc) => {
|
|
11
|
-
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
-
for (let key of __getOwnPropNames(from))
|
|
13
|
-
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
-
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
-
}
|
|
16
|
-
return to;
|
|
17
|
-
};
|
|
18
|
-
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
-
|
|
20
|
-
// src/patterns/Shockwave/index.ts
|
|
21
|
-
var Shockwave_exports = {};
|
|
22
|
-
__export(Shockwave_exports, {
|
|
23
|
-
Shockwave: () => Shockwave
|
|
24
|
-
});
|
|
25
|
-
module.exports = __toCommonJS(Shockwave_exports);
|
|
26
|
-
|
|
27
|
-
// src/patterns/Shockwave/Shockwave.tsx
|
|
28
|
-
var import_react = require("react");
|
|
29
|
-
var import_react_native2 = require("react-native");
|
|
30
|
-
var import_react_native_skia2 = require("@shopify/react-native-skia");
|
|
31
|
-
var import_react_native_reanimated = require("react-native-reanimated");
|
|
32
|
-
var import_react_native_worklets = require("react-native-worklets");
|
|
33
|
-
|
|
34
|
-
// src/patterns/Shockwave/conf.ts
|
|
35
|
-
var SHOCKWAVE_DEFAULTS = {
|
|
36
|
-
WIDTH: 320,
|
|
37
|
-
HEIGHT: 320,
|
|
38
|
-
DURATION: 900,
|
|
39
|
-
SHOCK_STRENGTH: 0.12,
|
|
40
|
-
LENSING_SPREAD: 0.2
|
|
41
|
-
};
|
|
42
|
-
|
|
43
|
-
// src/patterns/Shockwave/shader.ts
|
|
44
|
-
var SHOCKWAVE_SHADER_SOURCE = `
|
|
45
|
-
uniform float2 iResolution;
|
|
46
|
-
uniform float iTime;
|
|
47
|
-
uniform float2 iMouse;
|
|
48
|
-
uniform float uShockStrength;
|
|
49
|
-
uniform float uLensingSpread;
|
|
50
|
-
uniform shader iChannel0;
|
|
51
|
-
uniform shader iChannel1;
|
|
52
|
-
|
|
53
|
-
half4 main(float2 fragCoord) {
|
|
54
|
-
float2 uv = fragCoord / iResolution;
|
|
55
|
-
float2 origin = iMouse / iResolution;
|
|
56
|
-
|
|
57
|
-
float t = clamp(iTime, 0.0, 1.0);
|
|
58
|
-
float radius = sqrt(2.0) * t;
|
|
59
|
-
float circle = radius - distance(origin, uv);
|
|
60
|
-
float factor = uShockStrength
|
|
61
|
-
* sin(t * 3.14159265)
|
|
62
|
-
* pow(clamp(1.0 - abs(circle), 0.0, 1.0), 20.0);
|
|
63
|
-
|
|
64
|
-
float2 delta = origin - uv;
|
|
65
|
-
float2 dir = delta / (length(delta) + 1e-6);
|
|
66
|
-
|
|
67
|
-
float2 d0 = (uLensingSpread) * factor * dir * iResolution;
|
|
68
|
-
float2 d1 = (uLensingSpread * 1.2) * factor * dir * iResolution;
|
|
69
|
-
float2 d2 = (uLensingSpread * 1.5) * factor * dir * iResolution;
|
|
70
|
-
|
|
71
|
-
half4 a = half4(
|
|
72
|
-
iChannel0.eval(fragCoord + d0).r,
|
|
73
|
-
iChannel0.eval(fragCoord + d1).g,
|
|
74
|
-
iChannel0.eval(fragCoord + d2).b,
|
|
75
|
-
1.0
|
|
76
|
-
);
|
|
77
|
-
half4 b = half4(
|
|
78
|
-
iChannel1.eval(fragCoord + d0).r,
|
|
79
|
-
iChannel1.eval(fragCoord + d1).g,
|
|
80
|
-
iChannel1.eval(fragCoord + d2).b,
|
|
81
|
-
1.0
|
|
82
|
-
);
|
|
83
|
-
|
|
84
|
-
half mixT = half(clamp(t + circle * 5.0, 0.0, 1.0));
|
|
85
|
-
return mix(a, b, mixT);
|
|
86
|
-
}
|
|
87
|
-
`;
|
|
88
|
-
|
|
89
|
-
// src/patterns/Shockwave/utils.ts
|
|
90
|
-
var import_react_native = require("react-native");
|
|
91
|
-
var import_react_native_skia = require("@shopify/react-native-skia");
|
|
92
|
-
var webNoopCallback = () => Promise.resolve(null);
|
|
93
|
-
async function snapshotPair(fromRef, toRef, prev, next) {
|
|
94
|
-
const callback = import_react_native.Platform.OS === "web" ? webNoopCallback : null;
|
|
95
|
-
const fromImg = await (0, import_react_native_skia.makeImageFromView)(
|
|
96
|
-
fromRef,
|
|
97
|
-
callback
|
|
98
|
-
);
|
|
99
|
-
const toImg = await (0, import_react_native_skia.makeImageFromView)(
|
|
100
|
-
toRef,
|
|
101
|
-
callback
|
|
102
|
-
);
|
|
103
|
-
if (!fromImg || !toImg) return null;
|
|
104
|
-
const fromSnapshot = prev === "from" ? fromImg : toImg;
|
|
105
|
-
const toSnapshot = next === "from" ? fromImg : toImg;
|
|
106
|
-
return { from: fromSnapshot, to: toSnapshot };
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
// src/patterns/Shockwave/Shockwave.tsx
|
|
110
|
-
var import_jsx_runtime = require("react/jsx-runtime");
|
|
111
|
-
var useShockwaveShader = () => {
|
|
112
|
-
const [shader, setShader] = (0, import_react.useState)(null);
|
|
113
|
-
(0, import_react.useEffect)(() => {
|
|
114
|
-
let cancelled = false;
|
|
115
|
-
let attempts = 0;
|
|
116
|
-
let timer = null;
|
|
117
|
-
const tryCompile = () => {
|
|
118
|
-
if (cancelled) return;
|
|
119
|
-
try {
|
|
120
|
-
if (import_react_native_skia2.Skia?.RuntimeEffect?.Make) {
|
|
121
|
-
const compiled = import_react_native_skia2.Skia.RuntimeEffect.Make(SHOCKWAVE_SHADER_SOURCE);
|
|
122
|
-
if (compiled) {
|
|
123
|
-
setShader(compiled);
|
|
124
|
-
return;
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
} catch {
|
|
128
|
-
}
|
|
129
|
-
attempts += 1;
|
|
130
|
-
if (attempts < 30) {
|
|
131
|
-
timer = setTimeout(tryCompile, 80);
|
|
132
|
-
}
|
|
133
|
-
};
|
|
134
|
-
tryCompile();
|
|
135
|
-
return () => {
|
|
136
|
-
cancelled = true;
|
|
137
|
-
if (timer !== null) clearTimeout(timer);
|
|
138
|
-
};
|
|
139
|
-
}, []);
|
|
140
|
-
return shader;
|
|
141
|
-
};
|
|
142
|
-
var ShockwaveContext = (0, import_react.createContext)(null);
|
|
143
|
-
var useShockwaveContext = () => {
|
|
144
|
-
const ctx = (0, import_react.useContext)(ShockwaveContext);
|
|
145
|
-
if (!ctx) {
|
|
146
|
-
throw new Error(
|
|
147
|
-
"<Shockwave.Transition.From> / <Shockwave.Transition.To> must be rendered inside <Shockwave>"
|
|
148
|
-
);
|
|
149
|
-
}
|
|
150
|
-
return ctx;
|
|
151
|
-
};
|
|
152
|
-
var OFFSCREEN_OFFSET = 1e5;
|
|
153
|
-
var From = (0, import_react.memo)(function From2({ children, style }) {
|
|
154
|
-
const { fromRef, activeValue, isTransitioning } = useShockwaveContext();
|
|
155
|
-
const isActive = activeValue === "from";
|
|
156
|
-
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
157
|
-
import_react_native2.View,
|
|
158
|
-
{
|
|
159
|
-
ref: fromRef,
|
|
160
|
-
collapsable: false,
|
|
161
|
-
pointerEvents: isActive && !isTransitioning ? "auto" : "none",
|
|
162
|
-
style: [
|
|
163
|
-
import_react_native2.StyleSheet.absoluteFill,
|
|
164
|
-
{
|
|
165
|
-
zIndex: isActive ? 2 : 1,
|
|
166
|
-
transform: [{ translateX: isActive ? 0 : OFFSCREEN_OFFSET }]
|
|
167
|
-
},
|
|
168
|
-
style
|
|
169
|
-
],
|
|
170
|
-
children
|
|
171
|
-
}
|
|
172
|
-
);
|
|
173
|
-
});
|
|
174
|
-
var To = (0, import_react.memo)(function To2({ children, style }) {
|
|
175
|
-
const { toRef, activeValue, isTransitioning } = useShockwaveContext();
|
|
176
|
-
const isActive = activeValue === "to";
|
|
177
|
-
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
178
|
-
import_react_native2.View,
|
|
179
|
-
{
|
|
180
|
-
ref: toRef,
|
|
181
|
-
collapsable: false,
|
|
182
|
-
pointerEvents: isActive && !isTransitioning ? "auto" : "none",
|
|
183
|
-
style: [
|
|
184
|
-
import_react_native2.StyleSheet.absoluteFill,
|
|
185
|
-
{
|
|
186
|
-
zIndex: isActive ? 2 : 1,
|
|
187
|
-
transform: [{ translateX: isActive ? 0 : OFFSCREEN_OFFSET }]
|
|
188
|
-
},
|
|
189
|
-
style
|
|
190
|
-
],
|
|
191
|
-
children
|
|
192
|
-
}
|
|
193
|
-
);
|
|
194
|
-
});
|
|
195
|
-
var Transition = { From, To };
|
|
196
|
-
var ShockwaveRoot = (0, import_react.memo)(function ShockwaveRoot2({
|
|
197
|
-
value,
|
|
198
|
-
width = SHOCKWAVE_DEFAULTS.WIDTH,
|
|
199
|
-
height = SHOCKWAVE_DEFAULTS.HEIGHT,
|
|
200
|
-
duration = SHOCKWAVE_DEFAULTS.DURATION,
|
|
201
|
-
origin,
|
|
202
|
-
shockStrength = SHOCKWAVE_DEFAULTS.SHOCK_STRENGTH,
|
|
203
|
-
lensingSpread = SHOCKWAVE_DEFAULTS.LENSING_SPREAD,
|
|
204
|
-
style,
|
|
205
|
-
children,
|
|
206
|
-
onTransitionEnd
|
|
207
|
-
}) {
|
|
208
|
-
const fromRef = (0, import_react.useRef)(null);
|
|
209
|
-
const toRef = (0, import_react.useRef)(null);
|
|
210
|
-
const prevValueRef = (0, import_react.useRef)(value);
|
|
211
|
-
const shader = useShockwaveShader();
|
|
212
|
-
const [activeValue, setActiveValue] = (0, import_react.useState)(value);
|
|
213
|
-
const [snapshots, setSnapshots] = (0, import_react.useState)(null);
|
|
214
|
-
const progress = (0, import_react_native_reanimated.useSharedValue)(0);
|
|
215
|
-
const finishTransition = (0, import_react.useCallback)(
|
|
216
|
-
(next) => {
|
|
217
|
-
setActiveValue(next);
|
|
218
|
-
setSnapshots(null);
|
|
219
|
-
onTransitionEnd?.(next);
|
|
220
|
-
},
|
|
221
|
-
[onTransitionEnd]
|
|
222
|
-
);
|
|
223
|
-
(0, import_react.useEffect)(() => {
|
|
224
|
-
if (value === prevValueRef.current) return;
|
|
225
|
-
const prev = prevValueRef.current;
|
|
226
|
-
const next = value;
|
|
227
|
-
prevValueRef.current = value;
|
|
228
|
-
let cancelled = false;
|
|
229
|
-
void (async () => {
|
|
230
|
-
const pair = await snapshotPair(
|
|
231
|
-
fromRef,
|
|
232
|
-
toRef,
|
|
233
|
-
prev,
|
|
234
|
-
next
|
|
235
|
-
);
|
|
236
|
-
if (cancelled) return;
|
|
237
|
-
if (!pair) {
|
|
238
|
-
setActiveValue(next);
|
|
239
|
-
return;
|
|
240
|
-
}
|
|
241
|
-
setSnapshots(pair);
|
|
242
|
-
progress.value = 0;
|
|
243
|
-
progress.value = (0, import_react_native_reanimated.withTiming)(
|
|
244
|
-
1,
|
|
245
|
-
{ duration, easing: import_react_native_reanimated.Easing.linear },
|
|
246
|
-
(done) => {
|
|
247
|
-
if (done) (0, import_react_native_worklets.scheduleOnRN)(finishTransition, next);
|
|
248
|
-
}
|
|
249
|
-
);
|
|
250
|
-
})();
|
|
251
|
-
return () => {
|
|
252
|
-
cancelled = true;
|
|
253
|
-
};
|
|
254
|
-
}, [value, duration, finishTransition, progress]);
|
|
255
|
-
const ox = origin?.x ?? width / 2;
|
|
256
|
-
const oy = origin?.y ?? height / 2;
|
|
257
|
-
const uniforms = (0, import_react_native_reanimated.useDerivedValue)(() => ({
|
|
258
|
-
iResolution: [width, height],
|
|
259
|
-
iTime: progress.value,
|
|
260
|
-
iMouse: [ox, oy],
|
|
261
|
-
uShockStrength: shockStrength,
|
|
262
|
-
uLensingSpread: lensingSpread
|
|
263
|
-
}));
|
|
264
|
-
const ctxValue = (0, import_react.useMemo)(
|
|
265
|
-
() => ({
|
|
266
|
-
fromRef,
|
|
267
|
-
toRef,
|
|
268
|
-
activeValue,
|
|
269
|
-
isTransitioning: snapshots !== null
|
|
270
|
-
}),
|
|
271
|
-
[activeValue, snapshots]
|
|
272
|
-
);
|
|
273
|
-
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_react_native2.View, { style: [styles.wrapper, { width, height }, style], children: [
|
|
274
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(ShockwaveContext.Provider, { value: ctxValue, children }),
|
|
275
|
-
snapshots && shader ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
276
|
-
import_react_native_skia2.Canvas,
|
|
277
|
-
{
|
|
278
|
-
style: [import_react_native2.StyleSheet.absoluteFill, styles.canvasOverlay],
|
|
279
|
-
pointerEvents: "none",
|
|
280
|
-
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react_native_skia2.Fill, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_react_native_skia2.Shader, { source: shader, uniforms, children: [
|
|
281
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
282
|
-
import_react_native_skia2.ImageShader,
|
|
283
|
-
{
|
|
284
|
-
image: snapshots.from,
|
|
285
|
-
fit: "cover",
|
|
286
|
-
rect: { x: 0, y: 0, width, height }
|
|
287
|
-
}
|
|
288
|
-
),
|
|
289
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
290
|
-
import_react_native_skia2.ImageShader,
|
|
291
|
-
{
|
|
292
|
-
image: snapshots.to,
|
|
293
|
-
fit: "cover",
|
|
294
|
-
rect: { x: 0, y: 0, width, height }
|
|
295
|
-
}
|
|
296
|
-
)
|
|
297
|
-
] }) })
|
|
298
|
-
}
|
|
299
|
-
) : null
|
|
300
|
-
] });
|
|
301
|
-
});
|
|
302
|
-
var styles = import_react_native2.StyleSheet.create({
|
|
303
|
-
wrapper: {
|
|
304
|
-
position: "relative",
|
|
305
|
-
overflow: "hidden",
|
|
306
|
-
backgroundColor: "transparent"
|
|
307
|
-
},
|
|
308
|
-
canvasOverlay: {
|
|
309
|
-
zIndex: 3
|
|
310
|
-
}
|
|
311
|
-
});
|
|
312
|
-
var Shockwave = Object.assign(ShockwaveRoot, { Transition });
|
|
313
|
-
// Annotate the CommonJS export names for ESM import in node:
|
|
314
|
-
0 && (module.exports = {
|
|
315
|
-
Shockwave
|
|
316
|
-
});
|
|
317
|
-
//# sourceMappingURL=skia.js.map
|
package/dist/skia.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/patterns/Shockwave/index.ts","../src/patterns/Shockwave/Shockwave.tsx","../src/patterns/Shockwave/conf.ts","../src/patterns/Shockwave/shader.ts","../src/patterns/Shockwave/utils.ts"],"sourcesContent":["export { Shockwave } from './Shockwave'\nexport type {\n ShockwaveProps,\n ShockwaveSlotProps,\n ShockwaveContextValue,\n ShockwaveOrigin,\n ShockwaveValue,\n} from './types'\n","import {\n createContext,\n memo,\n useCallback,\n useContext,\n useEffect,\n useMemo,\n useRef,\n useState,\n type RefObject,\n} from 'react'\nimport { StyleSheet, View } from 'react-native'\nimport {\n Canvas,\n Fill,\n ImageShader,\n Shader,\n Skia,\n type SkImage,\n type Uniforms,\n} from '@shopify/react-native-skia'\nimport {\n Easing,\n useDerivedValue,\n useSharedValue,\n withTiming,\n} from 'react-native-reanimated'\nimport { scheduleOnRN } from 'react-native-worklets'\n\nimport { SHOCKWAVE_DEFAULTS } from './conf'\nimport { SHOCKWAVE_SHADER_SOURCE } from './shader'\nimport { snapshotPair } from './utils'\nimport type {\n ShockwaveContextValue,\n ShockwaveProps,\n ShockwaveSlotProps,\n ShockwaveValue,\n} from './types'\n\n// Lazy shader compile. Web Skia loads asynchronously (CanvasKit WASM), so\n// `Skia.RuntimeEffect` is undefined at module-load time on the web platform.\n// Compile inside the component via useMemo, after the host has had a chance\n// to call LoadSkiaWeb() / mount <WithSkiaWeb>.\ntype RuntimeEffect = ReturnType<typeof Skia.RuntimeEffect.Make>\n\nconst useShockwaveShader = (): RuntimeEffect | null => {\n const [shader, setShader] = useState<RuntimeEffect | null>(null)\n\n useEffect(() => {\n let cancelled = false\n let attempts = 0\n let timer: ReturnType<typeof setTimeout> | null = null\n const tryCompile = () => {\n if (cancelled) return\n try {\n if (Skia?.RuntimeEffect?.Make) {\n const compiled = Skia.RuntimeEffect.Make(SHOCKWAVE_SHADER_SOURCE)\n if (compiled) {\n setShader(compiled)\n return\n }\n }\n } catch {\n // CanvasKit not ready yet on web — retry\n }\n attempts += 1\n if (attempts < 30) {\n timer = setTimeout(tryCompile, 80)\n }\n }\n tryCompile()\n return () => {\n cancelled = true\n if (timer !== null) clearTimeout(timer)\n }\n }, [])\n\n return shader\n}\n\nconst ShockwaveContext = createContext<ShockwaveContextValue | null>(null)\n\nconst useShockwaveContext = (): ShockwaveContextValue => {\n const ctx = useContext(ShockwaveContext)\n if (!ctx) {\n throw new Error(\n '<Shockwave.Transition.From> / <Shockwave.Transition.To> must be rendered inside <Shockwave>',\n )\n }\n return ctx\n}\n\n// Off-screen translation used to keep the inactive slot mounted (so we can\n// snapshot it the moment a transition starts) without it eating taps or\n// painting visibly. Bigger than any plausible viewport — pulled from the\n// reacticx reference.\nconst OFFSCREEN_OFFSET = 100000\n\nconst From = memo<ShockwaveSlotProps>(function From({ children, style }) {\n const { fromRef, activeValue, isTransitioning } = useShockwaveContext()\n const isActive = activeValue === 'from'\n return (\n <View\n // RN's ref type wants `Ref<View>` but our useRef is typed `View | null`\n // (so the context API stays nullable). Same instance, narrower view.\n ref={fromRef as RefObject<View>}\n collapsable={false}\n pointerEvents={isActive && !isTransitioning ? 'auto' : 'none'}\n style={[\n StyleSheet.absoluteFill,\n {\n zIndex: isActive ? 2 : 1,\n transform: [{ translateX: isActive ? 0 : OFFSCREEN_OFFSET }],\n },\n style,\n ]}\n >\n {children}\n </View>\n )\n})\n\nconst To = memo<ShockwaveSlotProps>(function To({ children, style }) {\n const { toRef, activeValue, isTransitioning } = useShockwaveContext()\n const isActive = activeValue === 'to'\n return (\n <View\n // See <From> for rationale.\n ref={toRef as RefObject<View>}\n collapsable={false}\n pointerEvents={isActive && !isTransitioning ? 'auto' : 'none'}\n style={[\n StyleSheet.absoluteFill,\n {\n zIndex: isActive ? 2 : 1,\n transform: [{ translateX: isActive ? 0 : OFFSCREEN_OFFSET }],\n },\n style,\n ]}\n >\n {children}\n </View>\n )\n})\n\nconst Transition = { From, To } as const\n\nconst ShockwaveRoot = memo<ShockwaveProps>(function ShockwaveRoot({\n value,\n width = SHOCKWAVE_DEFAULTS.WIDTH,\n height = SHOCKWAVE_DEFAULTS.HEIGHT,\n duration = SHOCKWAVE_DEFAULTS.DURATION,\n origin,\n shockStrength = SHOCKWAVE_DEFAULTS.SHOCK_STRENGTH,\n lensingSpread = SHOCKWAVE_DEFAULTS.LENSING_SPREAD,\n style,\n children,\n onTransitionEnd,\n}: ShockwaveProps) {\n const fromRef = useRef<View | null>(null)\n const toRef = useRef<View | null>(null)\n const prevValueRef = useRef<ShockwaveValue>(value)\n const shader = useShockwaveShader()\n\n const [activeValue, setActiveValue] = useState<ShockwaveValue>(value)\n const [snapshots, setSnapshots] = useState<{\n from: SkImage\n to: SkImage\n } | null>(null)\n\n const progress = useSharedValue<number>(0)\n\n const finishTransition = useCallback(\n (next: ShockwaveValue) => {\n setActiveValue(next)\n setSnapshots(null)\n onTransitionEnd?.(next)\n },\n [onTransitionEnd],\n )\n\n useEffect(() => {\n if (value === prevValueRef.current) return\n const prev = prevValueRef.current\n const next = value\n prevValueRef.current = value\n\n let cancelled = false\n void (async () => {\n const pair = await snapshotPair(\n fromRef as RefObject<View>,\n toRef as RefObject<View>,\n prev,\n next,\n )\n if (cancelled) return\n if (!pair) {\n setActiveValue(next)\n return\n }\n setSnapshots(pair)\n progress.value = 0\n progress.value = withTiming(\n 1,\n { duration, easing: Easing.linear },\n (done) => {\n if (done) scheduleOnRN(finishTransition, next)\n },\n )\n })()\n\n return () => {\n cancelled = true\n }\n }, [value, duration, finishTransition, progress])\n\n const ox = origin?.x ?? width / 2\n const oy = origin?.y ?? height / 2\n\n const uniforms = useDerivedValue<Uniforms>(() => ({\n iResolution: [width, height],\n iTime: progress.value,\n iMouse: [ox, oy],\n uShockStrength: shockStrength,\n uLensingSpread: lensingSpread,\n }))\n\n const ctxValue = useMemo<ShockwaveContextValue>(\n () => ({\n fromRef,\n toRef,\n activeValue,\n isTransitioning: snapshots !== null,\n }),\n [activeValue, snapshots],\n )\n\n return (\n <View style={[styles.wrapper, { width, height }, style]}>\n <ShockwaveContext.Provider value={ctxValue}>\n {children}\n </ShockwaveContext.Provider>\n {snapshots && shader ? (\n <Canvas\n style={[StyleSheet.absoluteFill, styles.canvasOverlay]}\n pointerEvents=\"none\"\n >\n <Fill>\n <Shader source={shader} uniforms={uniforms}>\n <ImageShader\n image={snapshots.from}\n fit=\"cover\"\n rect={{ x: 0, y: 0, width, height }}\n />\n <ImageShader\n image={snapshots.to}\n fit=\"cover\"\n rect={{ x: 0, y: 0, width, height }}\n />\n </Shader>\n </Fill>\n </Canvas>\n ) : null}\n </View>\n )\n})\n\nconst styles = StyleSheet.create({\n wrapper: {\n position: 'relative',\n overflow: 'hidden',\n backgroundColor: 'transparent',\n },\n canvasOverlay: {\n zIndex: 3,\n },\n})\n\n/**\n * Shockwave — Skia-shader transition between two views.\n *\n * Wrap two children in `<Shockwave.Transition.From>` and\n * `<Shockwave.Transition.To>`, then flip `value` to play the wave.\n *\n * @example\n * ```tsx\n * const [v, setV] = useState<'from' | 'to'>('from')\n *\n * <Shockwave value={v} width={320} height={420}>\n * <Shockwave.Transition.From>\n * <ProductCardA />\n * </Shockwave.Transition.From>\n * <Shockwave.Transition.To>\n * <ProductCardB />\n * </Shockwave.Transition.To>\n * </Shockwave>\n *\n * <Button onPress={() => setV(v === 'from' ? 'to' : 'from')}>Swap</Button>\n * ```\n *\n * Adapted from `rit3zh/reacticx` (MIT) — same shader + slot pattern,\n * repackaged for the OTF SDK with stricter types and JSDoc.\n */\nexport const Shockwave = Object.assign(ShockwaveRoot, { Transition })\n\nexport type {\n ShockwaveProps,\n ShockwaveSlotProps,\n ShockwaveContextValue,\n ShockwaveOrigin,\n ShockwaveValue,\n} from './types'\n","// Defaults tuned for OTF — soft enough to feel premium, fast enough to land\n// inside a single tap interaction. Override per-instance via props.\nexport const SHOCKWAVE_DEFAULTS = {\n WIDTH: 320,\n HEIGHT: 320,\n DURATION: 900,\n SHOCK_STRENGTH: 0.12,\n LENSING_SPREAD: 0.2,\n} as const\n","// Skia SkSL shader. Produces a circular shockwave from `iMouse`, displaces\n// the source image along the radial direction with chromatic aberration\n// (per-channel offset for an analog-lens feel), then crossfades from\n// snapshot A → snapshot B as the wave passes over each pixel.\n//\n// Uniforms set per-frame from the host component. Don't edit unless you\n// know what you're doing — the shockStrength / lensingSpread tuning is\n// surface-area for users via the public Shockwave props.\nexport const SHOCKWAVE_SHADER_SOURCE = `\nuniform float2 iResolution;\nuniform float iTime;\nuniform float2 iMouse;\nuniform float uShockStrength;\nuniform float uLensingSpread;\nuniform shader iChannel0;\nuniform shader iChannel1;\n\nhalf4 main(float2 fragCoord) {\n float2 uv = fragCoord / iResolution;\n float2 origin = iMouse / iResolution;\n\n float t = clamp(iTime, 0.0, 1.0);\n float radius = sqrt(2.0) * t;\n float circle = radius - distance(origin, uv);\n float factor = uShockStrength\n * sin(t * 3.14159265)\n * pow(clamp(1.0 - abs(circle), 0.0, 1.0), 20.0);\n\n float2 delta = origin - uv;\n float2 dir = delta / (length(delta) + 1e-6);\n\n float2 d0 = (uLensingSpread) * factor * dir * iResolution;\n float2 d1 = (uLensingSpread * 1.2) * factor * dir * iResolution;\n float2 d2 = (uLensingSpread * 1.5) * factor * dir * iResolution;\n\n half4 a = half4(\n iChannel0.eval(fragCoord + d0).r,\n iChannel0.eval(fragCoord + d1).g,\n iChannel0.eval(fragCoord + d2).b,\n 1.0\n );\n half4 b = half4(\n iChannel1.eval(fragCoord + d0).r,\n iChannel1.eval(fragCoord + d1).g,\n iChannel1.eval(fragCoord + d2).b,\n 1.0\n );\n\n half mixT = half(clamp(t + circle * 5.0, 0.0, 1.0));\n return mix(a, b, mixT);\n}\n`\n","import type { RefObject } from 'react'\nimport { Platform, type View } from 'react-native'\nimport { makeImageFromView, type SkImage } from '@shopify/react-native-skia'\nimport type { ShockwaveValue } from './types'\n\n// Skia's `makeImageFromView` is native-only. On web it requires a callback\n// that returns a SkImage from a DOM snapshot — we don't ship one, so this\n// component gracefully no-ops on web and the showcase routes the user to\n// the OTF preview app QR instead.\nconst webNoopCallback = () => Promise.resolve<SkImage | null>(null)\n\n/**\n * Snapshots both slot views as Skia images. Returns the pair labelled\n * by previous / next active value so the shader can read them as\n * iChannel0 (current) and iChannel1 (target). Returns null if either\n * snapshot fails — caller should fall back to an instant swap.\n */\nexport async function snapshotPair(\n fromRef: RefObject<View>,\n toRef: RefObject<View>,\n prev: ShockwaveValue,\n next: ShockwaveValue,\n): Promise<{ from: SkImage; to: SkImage } | null> {\n const callback = Platform.OS === 'web' ? webNoopCallback : null\n const fromImg = await makeImageFromView(\n fromRef as RefObject<View>,\n callback,\n )\n const toImg = await makeImageFromView(\n toRef as RefObject<View>,\n callback,\n )\n if (!fromImg || !toImg) return null\n const fromSnapshot = prev === 'from' ? fromImg : toImg\n const toSnapshot = next === 'from' ? fromImg : toImg\n return { from: fromSnapshot, to: toSnapshot }\n}\n"],"mappings":"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","names":["import_react_native","import_react_native_skia","From","To","ShockwaveRoot"]}
|