diff --git a/bun.lockb b/bun.lockb
index be11679..85207b1 100755
Binary files a/bun.lockb and b/bun.lockb differ
diff --git a/index.html b/index.html
index 2712433..b97ae88 100644
--- a/index.html
+++ b/index.html
@@ -7,6 +7,7 @@
Tutu
+
diff --git a/package.json b/package.json
index 54e32ca..e0b0a54 100644
--- a/package.json
+++ b/package.json
@@ -42,7 +42,8 @@
"nanostores": "^0.9.5",
"solid-js": "^1.8.18",
"solid-styled": "^0.11.1",
- "stacktrace-js": "^2.0.2"
+ "stacktrace-js": "^2.0.2",
+ "web-animations-js": "^2.3.2"
},
"packageManager": "bun@1.1.21"
}
diff --git a/src/App.tsx b/src/App.tsx
index 1522795..c3573a5 100644
--- a/src/App.tsx
+++ b/src/App.tsx
@@ -22,6 +22,7 @@ const AccountMastodonOAuth2Callback = lazy(
);
const TimelineHome = lazy(() => import("./timelines/Home.js"));
const Settings = lazy(() => import("./settings/Settings.js"));
+const TootBottomSheet = lazy(() => import("./timelines/TootBottomSheet.js"));
const Routing: Component = () => {
return (
@@ -29,6 +30,7 @@ const Routing: Component = () => {
+
@@ -53,7 +55,7 @@ const App: Component = () => {
);
});
-const UnexpectedError = lazy(() => import("./UnexpectedError.js"))
+ const UnexpectedError = lazy(() => import("./UnexpectedError.js"));
return (
) {
+ return {
+ top: `${top}px`,
+ left: `${left}px`,
+ height: `${height}px`,
+ width: `${width}px`,
+ };
+}
+
+const MOVE_SPEED = 1400; // 1400px/s, bottom sheet is big and a bit heavier than small papers
+
const BottomSheet: ParentComponent = (props) => {
let element: HTMLDialogElement;
+ let animation: Animation | undefined;
+ const hero = useHeroSignal(HERO);
createEffect(() => {
if (props.open) {
if (!element.open) {
element.showModal();
+ animateOpen();
}
} else {
if (element.open) {
+ if (animation) {
+ animation.cancel();
+ }
element.close();
}
}
});
+ const animateOpen = () => {
+ // Do hero animation
+ const startRect = hero();
+ console.debug("capture hero source", startRect);
+ if (!startRect) return;
+ const endRect = element.getBoundingClientRect();
+ const easing = "ease-in-out";
+ console.debug("easing", easing);
+ element.classList.add(styles.animated);
+ const distance = Math.sqrt(
+ Math.pow(Math.abs(startRect.top - endRect.top), 2) +
+ Math.pow(Math.abs(startRect.left - startRect.top), 2),
+ );
+ const duration = (distance / MOVE_SPEED) * 1000;
+ animation = element.animate(
+ [composeAnimationFrame(startRect), composeAnimationFrame(endRect)],
+ { easing, duration },
+ );
+ const onAnimationEnd = () => {
+ element.classList.remove(styles.animated);
+ animation = undefined;
+ };
+ animation.addEventListener("finish", onAnimationEnd);
+ animation.addEventListener("cancel", onAnimationEnd);
+ };
+
+ onCleanup(() => {
+ if (animation) {
+ animation.cancel();
+ }
+ });
+
return (