pageturn 0.1.0 → 0.2.0

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.
Files changed (75) hide show
  1. package/CHANGELOG.md +41 -0
  2. package/README.md +179 -4
  3. package/dist/astro.js +0 -2
  4. package/dist/chunks/chunk-3ABPYO7H.js +43 -0
  5. package/dist/chunks/chunk-6FXXBNAF.js +197 -0
  6. package/dist/chunks/chunk-AEOAXMU3.js +33 -0
  7. package/dist/chunks/chunk-BOM5VXA5.js +167 -0
  8. package/dist/chunks/chunk-C3T7BGNG.js +30 -0
  9. package/dist/chunks/chunk-FYSSNYA4.js +17 -0
  10. package/dist/chunks/chunk-JHM7F4CH.js +172 -0
  11. package/dist/chunks/chunk-LM3XLSCW.js +799 -0
  12. package/dist/chunks/{chunk-QJ7GGFM4.js → chunk-YAL4MNCU.js} +4 -6
  13. package/dist/core.js +4 -3
  14. package/dist/dev/chunks/chunk-3JAWIMHB.js +184 -0
  15. package/dist/dev/chunks/chunk-3SU4CIKL.js +799 -0
  16. package/dist/dev/chunks/chunk-AEOAXMU3.js +33 -0
  17. package/dist/dev/chunks/chunk-C3T7BGNG.js +30 -0
  18. package/dist/dev/chunks/chunk-FYSSNYA4.js +17 -0
  19. package/dist/dev/chunks/chunk-I4SBUABE.js +175 -0
  20. package/dist/dev/chunks/chunk-U3XN54YY.js +219 -0
  21. package/dist/dev/chunks/chunk-Y6TD2T53.js +43 -0
  22. package/dist/dev/chunks/chunk-YAL4MNCU.js +22 -0
  23. package/dist/dev/core.js +19 -0
  24. package/dist/dev/index.js +21 -0
  25. package/dist/dev/morph-markup.js +34 -0
  26. package/dist/dev/morph.js +457 -0
  27. package/dist/dev/native.js +3 -0
  28. package/dist/dev/next.js +79 -0
  29. package/dist/dev/nuxt-plugin.js +24 -0
  30. package/dist/dev/prefetch.js +2 -0
  31. package/dist/dev/react-router.js +46 -0
  32. package/dist/dev/router.js +9 -0
  33. package/dist/dev/sveltekit.js +32 -0
  34. package/dist/dev/testing.js +48 -0
  35. package/dist/dev/turbo.js +49 -0
  36. package/dist/dev/vue.js +11 -0
  37. package/dist/head-core.js +1 -1
  38. package/dist/head-string.js +6 -6
  39. package/dist/head.js +1 -1
  40. package/dist/index.js +6 -5
  41. package/dist/morph-head.js +1 -1
  42. package/dist/morph-markup.js +11 -9
  43. package/dist/morph.js +145 -287
  44. package/dist/native.js +3 -2
  45. package/dist/next.js +79 -0
  46. package/dist/nuxt-plugin.js +24 -0
  47. package/dist/nuxt.js +34 -0
  48. package/dist/pageturn-morph.js +1 -1
  49. package/dist/pageturn.js +1 -1
  50. package/dist/prefetch.js +2 -2
  51. package/dist/react-router.js +46 -0
  52. package/dist/router.js +5 -183
  53. package/dist/sveltekit-server.js +22 -0
  54. package/dist/sveltekit.js +32 -0
  55. package/dist/testing.js +12 -22
  56. package/dist/turbo.js +49 -0
  57. package/dist/types/astro/integration.d.ts +0 -4
  58. package/dist/types/common/dev.d.ts +6 -0
  59. package/dist/types/common/page.d.ts +5 -0
  60. package/dist/types/common/registry.d.ts +3 -1
  61. package/dist/types/next/next.d.ts +27 -0
  62. package/dist/types/nuxt/module.d.ts +32 -0
  63. package/dist/types/react-router/react-router.d.ts +12 -0
  64. package/dist/types/router/router.d.ts +4 -2
  65. package/dist/types/sveltekit/server.d.ts +21 -0
  66. package/dist/types/sveltekit/sveltekit.d.ts +32 -0
  67. package/dist/types/turbo/turbo.d.ts +1 -0
  68. package/dist/types/vue/vue.d.ts +36 -0
  69. package/dist/vue.js +11 -0
  70. package/package.json +102 -23
  71. package/dist/chunks/chunk-237GNQNZ.js +0 -37
  72. package/dist/chunks/chunk-7LMNJ25V.js +0 -244
  73. package/dist/chunks/chunk-QC36TQP3.js +0 -24
  74. package/dist/chunks/chunk-WTHN3UIQ.js +0 -1000
  75. package/dist/chunks/chunk-YBAP3PHO.js +0 -247
@@ -0,0 +1,457 @@
1
+ import {
2
+ CONTENT_STYLE,
3
+ IN_VIEW,
4
+ LAYER_STYLE,
5
+ MORPH_KEY,
6
+ atTheirEnd,
7
+ back,
8
+ backFields,
9
+ centredOn,
10
+ cloned,
11
+ cornersCss,
12
+ defineMorph,
13
+ endOf,
14
+ forgetWayOut,
15
+ here,
16
+ isEnding,
17
+ markup,
18
+ onPage,
19
+ over,
20
+ ownCorners,
21
+ putBackFields,
22
+ seen,
23
+ shareOnScreen,
24
+ sizeOf,
25
+ wayBackOf,
26
+ wayOut,
27
+ within
28
+ } from "./chunks/chunk-3SU4CIKL.js";
29
+ import {
30
+ defineMove
31
+ } from "./chunks/chunk-U3XN54YY.js";
32
+ import "./chunks/chunk-3JAWIMHB.js";
33
+ import {
34
+ isFocusGivenBack,
35
+ reducesMotion
36
+ } from "./chunks/chunk-AEOAXMU3.js";
37
+ import {
38
+ isDeclined
39
+ } from "./chunks/chunk-YAL4MNCU.js";
40
+
41
+ // src/morph/frame.ts
42
+ var FRAME_STYLE = "position:fixed;top:0;border:0;margin:0;padding:0;max-width:none;max-height:none;pointer-events:none";
43
+ function loadInFrame(address, style, name = "") {
44
+ return new Promise((resolve) => {
45
+ let frame = document.createElement("iframe");
46
+ frame.name = name, frame.setAttribute("aria-hidden", "true"), frame.tabIndex = -1, frame.inert = !0, frame.style.cssText = `${FRAME_STYLE};width:${document.documentElement.clientWidth}px;height:${innerHeight}px;${style}`;
47
+ let fail = () => {
48
+ frame.remove(), resolve(null);
49
+ };
50
+ frame.addEventListener("error", fail), frame.addEventListener("load", async () => {
51
+ try {
52
+ if (await frame.contentDocument?.fonts.ready, !frame.contentDocument?.body) throw new Error("no page");
53
+ resolve(frame);
54
+ } catch {
55
+ fail();
56
+ }
57
+ }), frame.src = address, document.documentElement.append(frame);
58
+ });
59
+ }
60
+ var INTENTS = ["pointerover", "focusin", "touchstart"];
61
+ function onApproach(tell) {
62
+ let last = null, heard = (event) => {
63
+ let link = event.target instanceof Element ? event.target.closest("a[href]") : null;
64
+ link !== last && (last = link, link && !isFocusGivenBack(link) && !reducesMotion() && tell({
65
+ link,
66
+ via: "link",
67
+ from: new URL(location.href),
68
+ to: new URL(link.href),
69
+ canHandOver: !0
70
+ }));
71
+ };
72
+ for (let intent of INTENTS)
73
+ addEventListener(intent, heard, { passive: !0, capture: !0 });
74
+ return () => {
75
+ for (let intent of INTENTS)
76
+ removeEventListener(intent, heard, { capture: !0 });
77
+ };
78
+ }
79
+
80
+ // src/morph/behind.ts
81
+ var BEHIND_WAIT_MS = 800;
82
+ function behind(options = {}) {
83
+ return (stage) => {
84
+ let way = wayBackOf(stage);
85
+ stage.appear = options.appear;
86
+ let loading = null, dropFrame = () => {
87
+ loading?.frame.then((frame) => frame?.remove()), loading = null;
88
+ };
89
+ function showBehind(address, from) {
90
+ if (loading?.address !== address) {
91
+ dropFrame();
92
+ let frame = loadInFrame(
93
+ address,
94
+ // Just under the layer, above everything of the page's.
95
+ "left:0;opacity:0;z-index:2147483646",
96
+ `${MORPH_KEY}:${JSON.stringify({ x: from.x, y: from.y, m: way.name, d: from.d, p: from.p, ...putBackFields(from, stage.name) })}`
97
+ ).then((loaded) => {
98
+ let view = loaded?.contentWindow;
99
+ if (!loaded || !view) return null;
100
+ view.scrollTo(from.x, from.y);
101
+ let there = loaded.contentDocument?.querySelector(
102
+ from.p
103
+ );
104
+ return there && (there.style.visibility = "hidden"), new Promise(
105
+ (resolve) => view.requestAnimationFrame(
106
+ () => view.requestAnimationFrame(() => resolve(loaded))
107
+ )
108
+ );
109
+ });
110
+ loading = { address, frame };
111
+ }
112
+ return loading.frame;
113
+ }
114
+ way.aims = (from, end, about) => {
115
+ let ends = {
116
+ from: { ...from.e },
117
+ to: end
118
+ };
119
+ return isDeclined(stage.morph.ends?.(ends, about)) ? !1 : { stop: ends.from, end: ends.to ?? end };
120
+ }, way.frameBehind = (visit, from) => within(showBehind(visit.to.href, from), options.wait ?? BEHIND_WAIT_MS), way.dropFrame = dropFrame, stage.onRemove.push(
121
+ dropFrame,
122
+ onApproach((visit) => {
123
+ let from = way.origin();
124
+ from && way.leadsBack?.(visit) && showBehind(visit.to.href, from);
125
+ })
126
+ );
127
+ };
128
+ }
129
+
130
+ // src/morph/copy.ts
131
+ function copy() {
132
+ return {
133
+ build(element) {
134
+ let layer = document.createElement("div"), content = cloned(element), own = element, drawn = own.offsetWidth && own.offsetHeight ? null : element.getBoundingClientRect(), width = own.offsetWidth || (drawn?.width ?? 0), height = own.offsetHeight || (drawn?.height ?? 0);
135
+ return content.style.cssText += `;width:${width}px;height:${height}px;${CONTENT_STYLE}`, content.dataset.turnContent = "", layer.style.cssText = `${LAYER_STYLE};width:${width}px;height:${height}px`, layer.append(content), layer;
136
+ },
137
+ pose(layer) {
138
+ let [width, height] = sizeOf(layer);
139
+ return ({ box, rotate = 0, radius }) => {
140
+ let x = box.width / width, y = box.height / height;
141
+ return {
142
+ move: {
143
+ translate: centredOn(box, width, height),
144
+ rotate: `${rotate}deg`,
145
+ scale: `${x} ${y}`
146
+ },
147
+ look: {
148
+ borderRadius: `${cornersCss(radius, x)} / ${cornersCss(radius, y)}`
149
+ },
150
+ on: layer.firstElementChild
151
+ };
152
+ };
153
+ }
154
+ };
155
+ }
156
+
157
+ // src/morph/link-back.ts
158
+ function linkBack(options = {}) {
159
+ return (stage) => {
160
+ let way = wayBackOf(stage);
161
+ way.byLink = options, Object.assign(way.keep, options.keep);
162
+ let isMarked = (link) => link.getAttribute("data-turn-back") === stage.name;
163
+ way.leadsBack = (visit) => isMarked(visit.link) ? !0 : options.match ? options.match(visit) : visit.to.pathname === way.origin()?.f;
164
+ let started = way.origin();
165
+ for (let link of document.querySelectorAll(
166
+ "a[data-turn-back]"
167
+ ))
168
+ if (isMarked(link) && (link.dataset.turn = way.name, started && started.f !== link.pathname)) {
169
+ link.href = started.f;
170
+ let label = link.dataset.turnBackLabel;
171
+ label && link.setAttribute("aria-label", label);
172
+ }
173
+ stage.onRemove.push(
174
+ defineMove(way.name, {
175
+ match: way.leadsBack,
176
+ async leave(turn) {
177
+ let origin = way.origin(), from = origin?.f === turn.to.pathname ? origin : null, element = onPage(stage.morph.to);
178
+ if (!element || !turn.canHandOver || stage.live)
179
+ return turn.decline();
180
+ let about = stage.about(null, !0), end = stage.read(element), stop = null;
181
+ if (from && way.aims) {
182
+ let aimed = way.aims(from, end, about);
183
+ if (aimed === !1) return turn.decline();
184
+ ({ stop, end } = aimed);
185
+ }
186
+ if (shareOnScreen(end.box) < (options.inView ?? IN_VIEW))
187
+ return turn.decline();
188
+ let frame = from && stop ? await way.frameBehind?.(turn, from) ?? null : null, layer = await stage.put(element, end, stop), leaving = turn.leaving();
189
+ stop && await stage.travel(
190
+ end,
191
+ stop,
192
+ about,
193
+ stage.timing(options),
194
+ !0,
195
+ frame
196
+ ), await leaving;
197
+ let scrollsBack = from && (stop || options.scroll !== !1 && !turn.to.hash);
198
+ return stage.hand({
199
+ n: way.name,
200
+ m: way.name,
201
+ h: markup(layer),
202
+ e: stop ?? end,
203
+ ...stop ? { f: end } : {},
204
+ t: !!stop,
205
+ b: !!frame,
206
+ ...backFields(from, stage.name),
207
+ ...from && scrollsBack ? { x: from.x, y: from.y } : {}
208
+ }), { data: from?.d ?? null };
209
+ },
210
+ stay(turn, stay) {
211
+ stage.drop(), way.dropFrame?.(), stage.morph.stay?.(turn, stay);
212
+ }
213
+ })
214
+ );
215
+ };
216
+ }
217
+
218
+ // src/morph/peek.ts
219
+ var PEEK_WAIT_MS = 1500;
220
+ function peek(options = {}) {
221
+ let asked = /* @__PURE__ */ new Map(), measureThere = (to, selector, measure) => {
222
+ let key = `${to.href} ${selector} ${innerWidth}x${innerHeight}`, answer = asked.get(key);
223
+ return answer || (answer = loadInFrame(to.href, "left:-200vw;visibility:hidden").then(
224
+ (frame) => {
225
+ let page = frame?.contentDocument, element = page?.querySelector(selector);
226
+ for (let animation of page?.getAnimations() ?? [])
227
+ isEnding(animation) && animation.finish();
228
+ let end = element ? measure(element) : null;
229
+ return frame?.remove(), end || asked.delete(key), end;
230
+ }
231
+ ), asked.set(key, answer)), answer;
232
+ }, finder = (to, selector, measure) => within(measureThere(to, selector, measure), options.wait ?? PEEK_WAIT_MS);
233
+ return (options.warm ?? !0) && (finder.start = (stage) => {
234
+ stage.onRemove.push(
235
+ onApproach((visit) => {
236
+ let { match, from } = stage.morph;
237
+ (match ? match(visit) : from?.(visit.link)) && measureThere(visit.to, stage.morph.to, stage.read);
238
+ })
239
+ );
240
+ }), finder;
241
+ }
242
+
243
+ // src/morph/shadow.ts
244
+ function asDropShadow(shadow2) {
245
+ if (/^[-\d.]/.test(shadow2)) return shadow2;
246
+ let [, color, x, y, blur] = /^(.*?\)|\S+) (\S+) (\S+) (\S+)/.exec(shadow2) ?? [];
247
+ return color && blur ? `${x} ${y} ${blur} ${color}` : void 0;
248
+ }
249
+ var dropShadow = (shadow2) => `drop-shadow(${shadow2})`;
250
+ function shadow() {
251
+ return (stage) => {
252
+ let { read } = stage;
253
+ stage.read = (element) => {
254
+ let end = read(element), own = asDropShadow(getComputedStyle(element).boxShadow);
255
+ return own && (end.shadow = own), end;
256
+ }, stage.onPlay.push((layer, first, last, timing, finished) => {
257
+ let start = first.shadow && asDropShadow(first.shadow), stop = last.shadow && asDropShadow(last.shadow), one = start ?? stop;
258
+ if (!one) return;
259
+ let caster = document.createElement("div");
260
+ caster.setAttribute("aria-hidden", "true"), caster.dataset.turnShadow = "", caster.style.cssText = `position:fixed;left:0;top:0;z-index:2147483646;pointer-events:none;filter:${dropShadow(one)}`;
261
+ let shape = document.createElement("div");
262
+ shape.style.cssText = `background:#000;width:${layer.style.width};height:${layer.style.height}`;
263
+ for (let animation of layer.getAnimations()) {
264
+ let { effect } = animation;
265
+ effect instanceof KeyframeEffect && shape.animate(effect.getKeyframes(), timing);
266
+ }
267
+ caster.animate(
268
+ start && stop ? { filter: [dropShadow(start), dropShadow(stop)] } : { opacity: start ? [1, 0] : [0, 1] },
269
+ timing
270
+ ), caster.append(shape), layer.before(caster), finished.then(() => caster.remove());
271
+ });
272
+ };
273
+ }
274
+
275
+ // src/morph/text.ts
276
+ var TYPE = [
277
+ "fontSize",
278
+ "fontWeight",
279
+ "fontStretch",
280
+ "letterSpacing",
281
+ "fontVariationSettings"
282
+ ], FACE = [
283
+ "fontFamily",
284
+ "fontStyle",
285
+ "textTransform",
286
+ "fontKerning",
287
+ "fontFeatureSettings"
288
+ ];
289
+ function glyphs(element) {
290
+ let range = element.ownerDocument.createRange();
291
+ range.selectNodeContents(element);
292
+ let { left, top, width, height } = range.getBoundingClientRect();
293
+ return { left, top, width, height };
294
+ }
295
+ function splitDifference(one, other) {
296
+ let least = Math.min(one.length, other.length), head = 0;
297
+ for (; head < least && one[head] === other[head]; ) head++;
298
+ let tail = 0;
299
+ for (; tail < least - head && one[one.length - 1 - tail] === other[other.length - 1 - tail]; )
300
+ tail++;
301
+ return [
302
+ one.slice(0, head),
303
+ one.slice(head, one.length - tail),
304
+ other.slice(head, other.length - tail),
305
+ other.slice(other.length - tail)
306
+ ];
307
+ }
308
+ function text(options = {}) {
309
+ let pseudoOf = (isTo) => typeof options.box == "object" ? options.box[isTo ? "to" : "from"] : options.box, part = (layer, name) => layer.querySelector(`[data-turn-part="${name}"]`);
310
+ return {
311
+ read(element, end, isTo) {
312
+ let style = getComputedStyle(element), type = {};
313
+ for (let property of TYPE) type[property] = style[property];
314
+ type.letterSpacing === "normal" && (type.letterSpacing = "0px"), type.fontVariationSettings === "normal" && (type.fontVariationSettings = `"opsz" ${Number.parseFloat(style.fontSize)}`);
315
+ let box = glyphs(element), words = (element.textContent ?? "").replace(/\s+/g, " ").trim();
316
+ if (end.background = style.backgroundColor, element.getClientRects().length === 0) {
317
+ let copy2 = document.createElement("span");
318
+ copy2.style.cssText = "position:fixed;left:0;top:0;white-space:nowrap;line-height:1;font-optical-sizing:none;visibility:hidden";
319
+ for (let property of FACE) copy2.style[property] = style[property];
320
+ Object.assign(copy2.style, type), copy2.textContent = words, document.documentElement.append(copy2), box = glyphs(copy2), copy2.remove(), end.box = { ...box };
321
+ } else if (pseudoOf(isTo)) {
322
+ let drawn = getComputedStyle(element, pseudoOf(isTo)), [left, top, width, height] = [
323
+ drawn.left,
324
+ drawn.top,
325
+ drawn.width,
326
+ drawn.height
327
+ ].map(Number.parseFloat);
328
+ if (drawn.content !== "none" && [left, top, width, height].every(Number.isFinite)) {
329
+ let own = element.getBoundingClientRect();
330
+ end.box = {
331
+ left: own.left + element.clientLeft + (left ?? 0),
332
+ top: own.top + element.clientTop + (top ?? 0),
333
+ width: width ?? 0,
334
+ height: height ?? 0
335
+ }, end.radius = ownCorners(drawn, width ?? 0), end.background = drawn.backgroundColor;
336
+ }
337
+ }
338
+ end.text = {
339
+ box,
340
+ type,
341
+ color: style.color,
342
+ words
343
+ };
344
+ },
345
+ build(element, from) {
346
+ let layer = document.createElement("div"), content = document.createElement("span"), fill = document.createElement("span"), style = getComputedStyle(element), type = from.text?.type ?? {};
347
+ layer.style.cssText = `${LAYER_STYLE};width:0;height:0;white-space:nowrap;line-height:1;font-optical-sizing:none`;
348
+ for (let property of FACE) layer.style[property] = style[property];
349
+ return Object.assign(layer.style, type), content.style.cssText = "position:absolute;left:0;top:0", content.textContent = from.text?.words ?? "", content.dataset.turnContent = "", content.dataset.turnWords = "", fill.style.cssText = "position:absolute;z-index:-1", fill.dataset.turnBox = "", layer.append(content, fill), layer;
350
+ },
351
+ pose(layer) {
352
+ let fill = layer.lastElementChild, insetOf = (words) => {
353
+ let probe = layer.cloneNode(!1), line = document.createElement("span");
354
+ probe.removeAttribute("popover"), delete probe.dataset.turnLayer, Object.assign(probe.style, words.type, {
355
+ translate: "none",
356
+ rotate: "none",
357
+ visibility: "hidden"
358
+ }), line.style.cssText = "position:absolute;left:0;top:0", line.textContent = words.words, probe.append(line), document.documentElement.append(probe);
359
+ let corner = probe.getBoundingClientRect(), drawn = glyphs(line);
360
+ return probe.remove(), [drawn.left - corner.left, drawn.top - corner.top];
361
+ };
362
+ return ({ text: words, rotate = 0, box, radius, background }) => {
363
+ if (!words) throw new Error("pageturn/morph: an end has no text");
364
+ let [x, y] = insetOf(words), left = words.box.left - x, top = words.box.top - y;
365
+ return {
366
+ // By a translation, not `left` and `top`: Safari does not show those travel.
367
+ move: { translate: `${left}px ${top}px`, rotate: `${rotate}deg` },
368
+ look: {
369
+ ...words.type,
370
+ color: words.color,
371
+ // Around the middle of its box: a site that scales the layer (a button that
372
+ // gives under the press) scales it around what is seen.
373
+ transformOrigin: `${box.left - left + box.width / 2}px ${box.top - top + box.height / 2}px`
374
+ },
375
+ on: layer,
376
+ // The element's own box behind its words: a button's pill, a title's band. In
377
+ // pixels: a length in em inside a keyframe is not read at the animated font
378
+ // size by every browser.
379
+ inner: {
380
+ on: fill,
381
+ look: {
382
+ left: `${box.left - left}px`,
383
+ top: `${box.top - top}px`,
384
+ width: `${box.width}px`,
385
+ height: `${box.height}px`,
386
+ borderRadius: cornersCss(radius),
387
+ backgroundColor: background ?? "transparent"
388
+ }
389
+ }
390
+ };
391
+ };
392
+ },
393
+ play(layer, start, stop, timing, back2) {
394
+ let content = layer.firstElementChild, [from, to] = [start.text?.words ?? "", stop.text?.words ?? ""];
395
+ if (from === to || !to || content.firstElementChild) return;
396
+ let [head, leaving, arriving, rest] = splitDifference(from, to), swap = (typeof options.swap == "function" ? options.swap({ back: back2 }) : options.swap) ?? {}, span = (name, words, style) => `<span data-turn-part="${name}" style="${style}">${words.replace(/[&<]/g, (sign) => sign === "&" ? "&amp;" : "&lt;")}</span>`;
397
+ content.innerHTML = span("head", head, "") + `<span style="position:relative;display:inline-block">${span("arriving", arriving, "display:inline-block")}${span("leaving", leaving, "position:absolute;left:0;top:0")}</span><span data-turn-part="fit" style="display:inline-block">${span("rest", rest, "display:inline-block;white-space:pre")}</span>`;
398
+ let [fit, restPart, leavingPart, arrivingPart] = [
399
+ "fit",
400
+ "rest",
401
+ "leaving",
402
+ "arriving"
403
+ ].map((name) => part(layer, name)), slot = arrivingPart?.parentElement;
404
+ swap.clip && (slot.style.clipPath = swap.clip);
405
+ let widthOf = (element) => element?.getBoundingClientRect().width ?? 0, shareToClose = () => 100 * (widthOf(arrivingPart) - widthOf(leavingPart)) / (widthOf(restPart) || 1), atStart = shareToClose(), atStop = atTheirEnd(layer.getAnimations(), shareToClose), swapTiming = over(timing, swap);
406
+ fit?.animate({ translate: [`${-atStart}% 0`, `${-atStop}% 0`] }, timing), restPart?.animate({ translate: ["0% 0", `${atStop}% 0`] }, swapTiming), leavingPart?.animate(
407
+ swap.leave ?? [{ opacity: 1 }, { opacity: 0 }],
408
+ swapTiming
409
+ ), arrivingPart?.animate(
410
+ swap.arrive ?? [{ opacity: 0 }, { opacity: 1 }],
411
+ swapTiming
412
+ );
413
+ }
414
+ };
415
+ }
416
+
417
+ // src/morph/turned.ts
418
+ function ownTransform(style) {
419
+ let [x = 1, y = x] = style.scale.split(" ").map(Number.parseFloat);
420
+ return new DOMMatrix().rotate(Number.parseFloat(style.rotate) || 0).scale(x || 1, y || 1).multiply(new DOMMatrix(style.transform));
421
+ }
422
+ function turned() {
423
+ return (stage) => {
424
+ stage.endOf = (element) => {
425
+ let end = endOf(element), matrix = new DOMMatrix();
426
+ for (let node = element; node; node = node.parentElement)
427
+ matrix = ownTransform(getComputedStyle(node)).multiply(matrix);
428
+ let rotate = Math.atan2(matrix.b, matrix.a) * 180 / Math.PI, scaleX = Math.hypot(matrix.a, matrix.b), own = element, isTurned = Math.abs(rotate) > 0.01 && own.offsetWidth > 0, { box } = end, width = isTurned ? own.offsetWidth * scaleX : box.width, height = isTurned ? own.offsetHeight * Math.hypot(matrix.c, matrix.d) : box.height;
429
+ return {
430
+ ...end,
431
+ box: {
432
+ left: box.left + (box.width - width) / 2,
433
+ top: box.top + (box.height - height) / 2,
434
+ width,
435
+ height
436
+ },
437
+ rotate,
438
+ radius: ownCorners(getComputedStyle(element), width, scaleX)
439
+ };
440
+ };
441
+ };
442
+ }
443
+ export {
444
+ back,
445
+ behind,
446
+ copy,
447
+ defineMorph,
448
+ forgetWayOut,
449
+ here,
450
+ linkBack,
451
+ peek,
452
+ seen,
453
+ shadow,
454
+ text,
455
+ turned,
456
+ wayOut
457
+ };
@@ -0,0 +1,3 @@
1
+ import "./chunks/chunk-C3T7BGNG.js";
2
+ import "./chunks/chunk-AEOAXMU3.js";
3
+ import "./chunks/chunk-YAL4MNCU.js";
@@ -0,0 +1,79 @@
1
+ "use client";
2
+ import "./chunks/chunk-U3XN54YY.js";
3
+ import {
4
+ startNavigation
5
+ } from "./chunks/chunk-I4SBUABE.js";
6
+ import "./chunks/chunk-3JAWIMHB.js";
7
+ import {
8
+ inBrowser
9
+ } from "./chunks/chunk-AEOAXMU3.js";
10
+ import "./chunks/chunk-YAL4MNCU.js";
11
+
12
+ // src/next/next.ts
13
+ import NextLink from "next/link.js";
14
+ import { usePathname, useRouter, useSearchParams } from "next/navigation.js";
15
+ import {
16
+ createElement,
17
+ Suspense,
18
+ useLayoutEffect,
19
+ useMemo,
20
+ useRef
21
+ } from "react";
22
+ var pending = null, shownAt = null;
23
+ function go(router, href, how, link, options) {
24
+ let turn = startNavigation({
25
+ to: link ? link.href : new URL(href, location.href),
26
+ via: link ? "link" : "script",
27
+ link
28
+ });
29
+ pending = turn, router.prefetch(href), turn.left.then(() => {
30
+ pending === turn && router[how](href, options);
31
+ });
32
+ }
33
+ function Link(props) {
34
+ let router = useRouter(), anchor = useRef(null), { href, replace, scroll, onNavigate, ref } = props;
35
+ return createElement(NextLink, {
36
+ ...props,
37
+ ref(element) {
38
+ anchor.current = element, typeof ref == "function" ? ref(element) : ref && (ref.current = element);
39
+ },
40
+ onNavigate(event) {
41
+ let isStopped = !1;
42
+ onNavigate?.({
43
+ preventDefault() {
44
+ isStopped = !0, event.preventDefault();
45
+ }
46
+ }), !(isStopped || typeof href != "string" || !inBrowser) && (event.preventDefault(), go(router, href, replace ? "replace" : "push", anchor.current, {
47
+ scroll: scroll !== !1
48
+ }));
49
+ }
50
+ });
51
+ }
52
+ function usePageturnRouter() {
53
+ let router = useRouter();
54
+ return useMemo(
55
+ () => ({
56
+ ...router,
57
+ push: (href, options) => go(router, href, "push", null, options),
58
+ replace: (href, options) => go(router, href, "replace", null, options)
59
+ }),
60
+ [router]
61
+ );
62
+ }
63
+ function Watcher() {
64
+ let pathname = usePathname(), search = useSearchParams().toString();
65
+ return useLayoutEffect(() => {
66
+ let here = location.href, isFirst = shownAt === null;
67
+ if (shownAt = here, isFirst) return;
68
+ let turn = pending ?? startNavigation({ to: here, via: "history" });
69
+ pending = null, turn.swapped();
70
+ }, [pathname, search]), null;
71
+ }
72
+ function Pageturn() {
73
+ return createElement(Suspense, { fallback: null }, createElement(Watcher));
74
+ }
75
+ export {
76
+ Link,
77
+ Pageturn,
78
+ usePageturnRouter
79
+ };
@@ -0,0 +1,24 @@
1
+ import {
2
+ pageturn
3
+ } from "./chunks/chunk-Y6TD2T53.js";
4
+ import "./chunks/chunk-U3XN54YY.js";
5
+ import "./chunks/chunk-I4SBUABE.js";
6
+ import "./chunks/chunk-3JAWIMHB.js";
7
+ import "./chunks/chunk-AEOAXMU3.js";
8
+ import "./chunks/chunk-YAL4MNCU.js";
9
+
10
+ // src/nuxt/plugin.ts
11
+ var SHOWN_MAX_MS = 4e3;
12
+ function pageturnPlugin(nuxtApp) {
13
+ pageturn(nuxtApp.$router, {
14
+ whenShown(swap) {
15
+ let isDone = !1, once = () => {
16
+ isDone || (isDone = !0, swap());
17
+ };
18
+ nuxtApp.hooks.hookOnce("page:finish", once), setTimeout(once, SHOWN_MAX_MS);
19
+ }
20
+ });
21
+ }
22
+ export {
23
+ pageturnPlugin as default
24
+ };
@@ -0,0 +1,2 @@
1
+ import "./chunks/chunk-FYSSNYA4.js";
2
+ import "./chunks/chunk-YAL4MNCU.js";
@@ -0,0 +1,46 @@
1
+ import "./chunks/chunk-U3XN54YY.js";
2
+ import {
3
+ startNavigation
4
+ } from "./chunks/chunk-I4SBUABE.js";
5
+ import "./chunks/chunk-3JAWIMHB.js";
6
+ import {
7
+ inBrowser
8
+ } from "./chunks/chunk-AEOAXMU3.js";
9
+ import "./chunks/chunk-YAL4MNCU.js";
10
+
11
+ // src/react-router/react-router.ts
12
+ import { useLayoutEffect } from "react";
13
+ import { useLocation } from "react-router";
14
+ var current = null, popped = !1, isMounted = !1;
15
+ inBrowser && addEventListener(
16
+ "popstate",
17
+ () => {
18
+ popped = !0;
19
+ },
20
+ !0
21
+ );
22
+ var pageturnMiddleware = async ({ request }, next) => {
23
+ let via = popped ? "history" : "link";
24
+ if (popped = !1, !isMounted) return next();
25
+ let turn = startNavigation({ to: request.url, via });
26
+ current = turn, request.signal.addEventListener("abort", () => {
27
+ setTimeout(() => {
28
+ current === turn && (current = null, turn.cancelled());
29
+ });
30
+ });
31
+ let [result] = await Promise.all([next(), turn.left]);
32
+ return result;
33
+ };
34
+ function usePageturn() {
35
+ let { key } = useLocation();
36
+ useLayoutEffect(() => {
37
+ isMounted = !0;
38
+ }, []), useLayoutEffect(() => {
39
+ let turn = current;
40
+ current = null, turn?.swapped();
41
+ }, [key]);
42
+ }
43
+ export {
44
+ pageturnMiddleware,
45
+ usePageturn
46
+ };
@@ -0,0 +1,9 @@
1
+ import {
2
+ startNavigation
3
+ } from "./chunks/chunk-I4SBUABE.js";
4
+ import "./chunks/chunk-3JAWIMHB.js";
5
+ import "./chunks/chunk-AEOAXMU3.js";
6
+ import "./chunks/chunk-YAL4MNCU.js";
7
+ export {
8
+ startNavigation
9
+ };
@@ -0,0 +1,32 @@
1
+ import "./chunks/chunk-U3XN54YY.js";
2
+ import {
3
+ startNavigation
4
+ } from "./chunks/chunk-I4SBUABE.js";
5
+ import "./chunks/chunk-3JAWIMHB.js";
6
+ import "./chunks/chunk-AEOAXMU3.js";
7
+ import "./chunks/chunk-YAL4MNCU.js";
8
+
9
+ // src/sveltekit/sveltekit.ts
10
+ var started = null;
11
+ function begin(navigation) {
12
+ if (!navigation.to || navigation.willUnload) return null;
13
+ let turn = startNavigation({
14
+ to: navigation.to.url,
15
+ via: navigation.type === "link" ? "link" : navigation.type === "popstate" ? "history" : "script"
16
+ });
17
+ return navigation.complete.catch(() => {
18
+ started === turn && (started = null), turn.cancelled();
19
+ }), turn;
20
+ }
21
+ function pageturn({ beforeNavigate, onNavigate }) {
22
+ beforeNavigate((navigation) => {
23
+ started = begin(navigation);
24
+ }), onNavigate(async (navigation) => {
25
+ let turn = started ?? begin(navigation);
26
+ if (started = null, !!turn)
27
+ return await turn.left, () => turn.swapped();
28
+ });
29
+ }
30
+ export {
31
+ pageturn
32
+ };