From a15e2d62fe7dd5e5e7be78648a767f4e7c84a71f Mon Sep 17 00:00:00 2001 From: thislight Date: Wed, 4 Dec 2024 20:02:29 +0800 Subject: [PATCH] first prototype of MediaQuickview --- bun.lockb | Bin 301673 -> 301705 bytes package.json | 1 + src/platform/MediaQuickview.css | 69 ++++++++ src/platform/MediaQuickview.tsx | 179 ++++++++++++++++++++ src/timelines/toots/MediaAttachmentGrid.tsx | 43 ++--- 5 files changed, 261 insertions(+), 31 deletions(-) create mode 100644 src/platform/MediaQuickview.css create mode 100644 src/platform/MediaQuickview.tsx diff --git a/bun.lockb b/bun.lockb index 0645c92b80358a366bc2a50d54c803ad3054f6c8..bec25b12c78c96f732f0b82a831c37f2ac0aa4af 100755 GIT binary patch delta 23277 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 AA^-pY delta 23020 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 diff --git a/package.json b/package.json index 4e04928..6222ecd 100644 --- a/package.json +++ b/package.json @@ -42,6 +42,7 @@ "@solid-primitives/map": "^0.4.13", "@solid-primitives/page-visibility": "^2.0.17", "@solid-primitives/resize-observer": "^2.0.26", + "@solid-primitives/rootless": "^1.4.5", "@solidjs/router": "^0.15.1", "@suid/icons-material": "^0.8.1", "@suid/material": "^0.18.0", diff --git a/src/platform/MediaQuickview.css b/src/platform/MediaQuickview.css new file mode 100644 index 0000000..b1ceb07 --- /dev/null +++ b/src/platform/MediaQuickview.css @@ -0,0 +1,69 @@ +.MediaQuickview__root { + display: contents; +} + +.MediaQuickview { + border: none; + position: fixed; + width: 100vw; + width: 100dvw; + height: 100vh; + height: 100dvh; + max-width: 100vw; + max-height: 100vh; + contain: content; + padding: 0; + + &::backdrop { + background: none; + } + + >.Scaffold>.topbar { + position: fixed; + left: 0; + right: 0; + + >* { + background-color: var(--tutu-color-surface); + color: var(--tutu-color-on-surface); + } + } + + >.Scaffold>.pages { + display: grid; + grid-auto-flow: column; + grid-auto-columns: 100%; + height: 100%; + width: 100%; + overflow: auto hidden; + scroll-snap-type: x mandatory; + scroll-snap-align: center; + scroll-snap-stop: always; + + + >.page { + width: 100%; + height: 100%; + max-width: 100vw; + max-height: 100vh; + contain: layout style size paint; + + >* { + display: block; + width: 100%; + height: 100%; + + object-fit: contain; + object-position: center; + } + } + } + + &.lightout { + >.Scaffold { + >.topbar { + visibility: hidden; + } + } + } +} \ No newline at end of file diff --git a/src/platform/MediaQuickview.tsx b/src/platform/MediaQuickview.tsx new file mode 100644 index 0000000..2a9d7e1 --- /dev/null +++ b/src/platform/MediaQuickview.tsx @@ -0,0 +1,179 @@ +import { createCallback, createSubRoot } from "@solid-primitives/rootless"; +import { + createRenderEffect, + createSignal, + Index, + Match, + onCleanup, + onMount, + Switch, + type Component, +} from "solid-js"; +import { render } from "solid-js/web"; +import Scaffold from "~material/Scaffold"; +import { isPointNotInRect } from "./dom"; +import "./MediaQuickview.css"; +import AppTopBar from "~material/AppTopBar"; +import { IconButton } from "@suid/material"; +import { Close } from "@suid/icons-material"; + +function renderIsolateMediaQuickview( + each: QuickviewMedia[], + index: number, + transitionFrom?: Element, +) { + createSubRoot((disposeAll) => { + let container: HTMLDivElement; + + createRenderEffect(() => { + container = document.createElement("div"); + container.setAttribute("role", "presentation"); + container.classList.add("MediaQuickview__root"); + document.querySelector("body")!.appendChild(container); + + onCleanup(() => container.remove()); + + const dispose = render(() => { + return ( + + ); + }, container); + + onCleanup(dispose); + }); + }); +} + +export function createMediaQuickview() { + return createCallback(renderIsolateMediaQuickview); +} + +function ImagePage(props: { src: string; alt?: string }) { + const [scale, setScale] = createSignal(1); + const [offsetX, setOffsetX] = createSignal(0); + const [offsetY, setOffsetY] = createSignal(0); + + const onWheel = (event: WheelEvent & { currentTarget: HTMLElement }) => { + // This is a de-facto standard for scaling: + // Browsers will simulate ctrl + wheel for two point scaling gesture. + if (event.ctrlKey) { + event.preventDefault(); + event.stopPropagation(); + + const offset = event.deltaY; + + setScale((x) => x + offset / event.currentTarget.clientHeight); + } + }; + + return ( + {props.alt} { + const { top, left, width, height } = + currentTarget.getBoundingClientRect(); + }} + > + ); +} + +export type QuickviewMedia = { + cat: "image" | "video" | "gifv" | "audio" | "unknown"; + src: string; + alt?: string; +}; + +export type MediaQuickviewProps = { + each: QuickviewMedia[]; + defaultIndex: number; + transitonFrom?: Element; + + onClose?(): void; +}; + +const MediaQuickview: Component = (props) => { + let root: HTMLDialogElement; + const [lightOut, setLightOut] = createSignal(false); + + function onDialogClick( + event: MouseEvent & { currentTarget: HTMLDialogElement }, + ) { + event.stopPropagation(); + + if ( + isPointNotInRect( + event.currentTarget.getBoundingClientRect(), + event.clientX, + event.clientY, + ) + ) { + event.currentTarget.close(); + } else { + setLightOut((x) => !x); + } + } + + return ( + { + root = e; + onMount(() => { + e.showModal(); + }); + }} + class="MediaQuickview" + classList={{ lightout: lightOut() }} + onClose={props.onClose} + onCancel={props.onClose} + onClick={onDialogClick} + > + + root.close()}> + + + + } + > +
{ + onMount(() => { + e.children.item(props.defaultIndex)!.scrollIntoView({ + behavior: "instant", + inline: "center", + }); + }); + }} + class="pages" + > + + {(item, index) => { + return ( +
+ + + + + +
+ ); + }} +
+
+
+
+ ); +}; + +export default MediaQuickview; diff --git a/src/timelines/toots/MediaAttachmentGrid.tsx b/src/timelines/toots/MediaAttachmentGrid.tsx index c9d72fc..6291a86 100644 --- a/src/timelines/toots/MediaAttachmentGrid.tsx +++ b/src/timelines/toots/MediaAttachmentGrid.tsx @@ -5,13 +5,9 @@ import { Match, Switch, createMemo, - createRenderEffect, createSignal, - onCleanup, untrack, } from "solid-js"; -import MediaViewer from "../MediaViewer"; -import { render } from "solid-js/web"; import { createElementSize, useWindowSize, @@ -24,6 +20,7 @@ import cardStyle from "~material/cards.module.css"; import { Preview } from "@suid/icons-material"; import { IconButton } from "@suid/material"; import Masonry from "~platform/Masonry"; +import { createMediaQuickview } from "~platform/MediaQuickview"; type ElementSize = { width: number; height: number }; @@ -58,39 +55,23 @@ const MediaAttachmentGrid: Component<{ sensitive?: boolean; }> = (props) => { const [rootRef, setRootRef] = createSignal(); - const [viewerIndex, setViewerIndex] = createSignal(); - const viewerOpened = () => typeof viewerIndex() !== "undefined"; const settings = useStore($settings); const windowSize = useWindowSize(); const [reveal, setReveal] = createSignal([] as number[]); - createRenderEffect(() => { - const vidx = viewerIndex(); - if (typeof vidx === "undefined") return; - const container = document.createElement("div"); - container.setAttribute("role", "presentation"); - document.body.appendChild(container); - const dispose = render(() => { - onCleanup(() => { - document.body.removeChild(container); - }); - - return ( - setViewerIndex()} - /> - ); - }, container); - - onCleanup(dispose); - }); + const openMediaQuickview = createMediaQuickview(); const openViewerFor = (index: number) => { - setViewerIndex(index); + openMediaQuickview( + props.attachments.map((item) => { + return { + cat: item.type, + src: item.url as string, + alt: item.description || undefined, + }; + }), + index, + ); }; const columnCount = () => {