import { createApp, defineComponent, h, ref } from "vue"; import { OriginGesture, OriginGestureSurface, OriginScene, back, createOriginScene, defineOriginChoreography, forward, gesture, originView, slideLeft, useOrigin, } from "../../../src"; declare global { interface Window { log: string[]; inPointerup: boolean; scene: ReturnType; scrollTopOf: (selector: string) => number; begin: (name: "matched" | "mismatched") => Promise; } } window.log = []; window.inPointerup = false; window.addEventListener("pointerup", () => (window.inPointerup = true), true); window.addEventListener("pointerup", () => setTimeout(() => (window.inPointerup = false)), ); const warn = console.warn; console.warn = (...args: unknown[]) => { window.log.push("warn: " + String(args[0])); warn(...args); }; const rows = (n: number) => Array.from({ length: n }, (_, i) => h("div", { class: "row" }, `row ${i}`)); const Detail = defineComponent({ name: "Detail", setup() { const list = ref(null); const dismiss = gesture.from .scrollEdge(list, "top") .to.down() .navigate((c) => (c.canGoBack ? back() : null)) .onCommit(() => window.log.push("dismiss committed")) .animate( defineOriginChoreography({ name: "dismiss", effects: ({ progress }) => ({ source: { transform: `translateY(${progress * 100}%)` }, }), }), ); return () => h(OriginGesture, { gesture: dismiss, class: "page", id: "detail" }, () => h("div", { class: "list", id: "detail-list", ref: list }, rows(40)), ); }, }); const Home = defineComponent({ name: "Home", setup() { const open = gesture.to .left() .navigate(() => forward(originView(Detail, undefined, { key: "detail" }))) .onCommit(() => window.log.push(`open committed inPointerup=${window.inPointerup}`), ) .animate(slideLeft); return () => h( OriginGestureSurface, { gestures: [open], class: "page", id: "home" }, () => h("div", { class: "list", id: "home-list" }, rows(40)), ); }, }); // Continuity check pair: a drawer that rests at 80% and a close that starts // either from the same pose (matched) or from 0 (mismatched). const drawerOpen = defineOriginChoreography({ name: "drawer-open", persistAtRest: true, effects: ({ progress }) => ({ source: { transform: `translateX(${progress * 80}%)` }, }), }); const closes = { matched: defineOriginChoreography({ name: "drawer-close-matched", effects: ({ progress }) => ({ target: { transform: `translateX(${(1 - progress) * 80}%)` }, }), }), mismatched: defineOriginChoreography({ name: "drawer-close-mismatched", effects: ({ progress }) => ({ target: { transform: `translateX(${(1 - progress) * 50}%)` }, }), }), }; const Drawer = defineComponent({ name: "Drawer", render: () => h("div", { class: "page" }, "drawer"), }); const Plain = defineComponent({ name: "Plain", setup() { const origin = useOrigin(); (window as unknown as { openDrawer: () => Promise }).openDrawer = () => origin.perform( forward(originView(Drawer, undefined, { key: "drawer" }), drawerOpen), ); return () => h("div", { class: "page", id: "plain" }, "plain"); }, }); const params = new URLSearchParams(location.search); const initial = params.get("view") === "plain" ? Plain : Home; const scene = createOriginScene({ initial: originView(initial, undefined, { key: "root" }), }); window.scene = scene; window.scrollTopOf = (selector) => document.querySelector(selector)?.scrollTop ?? -1; window.begin = async (name) => { await ( window as unknown as { openDrawer: () => Promise } ).openDrawer(); const drawerKey = scene.nodes.value.find((n) => n.view.key === "drawer")!.key; const handle = await scene.begin(drawerKey, back(closes[name])); await handle.cancel({ animate: false }); await handle.finish({ commit: false, animate: false }); }; createApp({ render: () => h(OriginScene, { scene }) }).mount("#app");