- useContextMenu gains buttonProps (a row's visible "…" opens the same menu under the button; a second press closes it) and anchor (the element the menu came from, for a panel a row opens). - ui/MoreButton; ActionMenu rows can run inGesture (a clipboard write inside the click, Safari). - useVideoActions: one builder for a video's menu (play / restart, channel, watched, save, add to playlist, download, details, copy title / link, hide, reset, pin), composed by the feed and by Home's rails; the playlists and details panels open anchored beside the card (VideoFollowUp). - The card keeps watched / save / add to playlist and the "…": on the picture on hover (the duration steps aside), in the rows' action column; on touch only a 44px "…" beside the title. Download, hide, reset and the hover info card leave the card. - The title is selectable: a click ending a selection neither plays nor follows the link. - DESIGN.md: how C1 is built + the log row.
36 lines
1.3 KiB
TypeScript
36 lines
1.3 KiB
TypeScript
import { lazy, Suspense } from "react";
|
|
import { type Video } from "../lib/api";
|
|
import { AddToPlaylistAt } from "./AddToPlaylist";
|
|
import VideoDetailsAt from "./VideoInfoCard";
|
|
|
|
// Lazy for the same reason DownloadButton's is: the dialog and its profile editor load only when
|
|
// someone downloads, not with every feed.
|
|
const DownloadDialog = lazy(() => import("./DownloadDialog"));
|
|
|
|
/** What a video menu row opened that outlives the menu: a panel anchored where the menu came from
|
|
* (the playlists, the details) or the download dialog. */
|
|
export type VideoFollowUpState =
|
|
| { kind: "playlist" | "details"; video: Video; anchor: Element }
|
|
| { kind: "download"; video: Video };
|
|
|
|
/** The one follow-up a video list has open, mounted once per list beside its menu (see
|
|
* `useVideoActions`) rather than once per card. */
|
|
export function VideoFollowUp({
|
|
state,
|
|
onClose,
|
|
}: {
|
|
state: VideoFollowUpState;
|
|
onClose: () => void;
|
|
}) {
|
|
const { video } = state;
|
|
if (state.kind === "download")
|
|
return (
|
|
<Suspense fallback={null}>
|
|
<DownloadDialog source={video.id} title={video.title} onClose={onClose} />
|
|
</Suspense>
|
|
);
|
|
if (state.kind === "playlist")
|
|
return <AddToPlaylistAt videoId={video.id} anchor={state.anchor} onClose={onClose} />;
|
|
return <VideoDetailsAt video={video} anchor={state.anchor} onClose={onClose} />;
|
|
}
|