1
0

ref: structure of the layout components

add: some links in the left column and a FAB on the top of the right column (desktop view)
This commit is contained in:
alikia2x (寒寒) 2025-08-07 03:16:38 +08:00
parent 1959046184
commit 493cf287f1
19 changed files with 209 additions and 162 deletions

View File

@ -4,7 +4,7 @@
"": {
"name": "example-basic",
"dependencies": {
"@m3-components/solid": "0.1.17",
"@m3-components/solid": "0.2.0",
"@solid-primitives/media": "^2.3.3",
"@solidjs/meta": "^0.29.4",
"@solidjs/router": "^0.15.3",
@ -184,7 +184,7 @@
"@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.25", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ=="],
"@m3-components/solid": ["@m3-components/solid@0.1.17", "", { "dependencies": { "@solid-primitives/event-listener": "^2.4.3", "animejs": "^4.0.2", "solid-js": "^1.9.5", "tailwind-variants": "^1.0.0" } }, "sha512-KXNovjVIl0Tr0WhASn71t07DFO98S2VyDMa0453Q6Z4CA8FA91h65uaGkMxL4r9SmUdMOraxMeKNG8GJgV/bZQ=="],
"@m3-components/solid": ["@m3-components/solid@0.2.0", "", { "dependencies": { "@solid-primitives/event-listener": "^2.4.3", "animejs": "^4.0.2", "solid-js": "^1.9.5", "tailwind-variants": "^1.0.0" } }, "sha512-kJ2dPUeJlMJFVAUBBkeAbDkVt409LD0aT+hkilnUT5en6IfQCE4EUzk9BPPbYfzMa4B8nutMlfAyRwcYREbJ9A=="],
"@mapbox/node-pre-gyp": ["@mapbox/node-pre-gyp@2.0.0", "", { "dependencies": { "consola": "^3.2.3", "detect-libc": "^2.0.0", "https-proxy-agent": "^7.0.5", "node-fetch": "^2.6.7", "nopt": "^8.0.0", "semver": "^7.5.3", "tar": "^7.4.0" }, "bin": { "node-pre-gyp": "bin/node-pre-gyp" } }, "sha512-llMXd39jtP0HpQLVI37Bf1m2ADlEb35GYSh1SDSLsBhR+5iCxiNGlT31yqbNtVHygHAtMy6dWFERpU2JgufhPg=="],

View File

@ -8,7 +8,7 @@
"version": "vinxi version"
},
"dependencies": {
"@m3-components/solid": "0.1.17",
"@m3-components/solid": "0.2.0",
"@solid-primitives/media": "^2.3.3",
"@solidjs/meta": "^0.29.4",
"@solidjs/router": "^0.15.3",

View File

@ -4,7 +4,7 @@ import { FileRoutes } from "@solidjs/start/router";
import { onMount, Suspense } from "solid-js";
import "./app.css";
import "@m3-components/solid/index.css";
import { setActiveTab, tabMap } from "./components/shell/Navigation";
import { setActiveTab, tabMap } from "./components/layout/Navigation";
import { minimatch } from "minimatch";
export const refreshTab = (path: string) => {

View File

@ -1,3 +1,4 @@
import { JSX } from "solid-js";
export type DivProps = JSX.HTMLAttributes<HTMLDivElement>
export type DivProps = JSX.HTMLAttributes<HTMLDivElement>;
export type ElementProps = JSX.HTMLAttributes<HTMLElement>;

View File

@ -0,0 +1,12 @@
import { SVGIconComponent } from "./types";
export const EditIcon: SVGIconComponent = (props) => {
return (
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" {...props}>
<path
fill="currentColor"
d="M5 19h1.425L16.2 9.225L14.775 7.8L5 17.575zm-2 2v-4.25L16.2 3.575q.3-.275.663-.425t.762-.15t.775.15t.65.45L20.425 5q.3.275.438.65T21 6.4q0 .4-.137.763t-.438.662L7.25 21zM19 6.4L17.6 5zm-3.525 2.125l-.7-.725L16.2 9.225z"
/>
</svg>
);
};

View File

@ -0,0 +1,5 @@
export * from "./Home";
export * from "./Music";
export * from "./Album";
export * from "./Search";
export * from "./Edit";

View File

@ -0,0 +1,10 @@
import { Component } from "solid-js";
import { DivProps } from "~/components/common";
export const BodyRegion: Component<DivProps> = (props) => {
return (
<div class="min-h-full" {...props}>
{props.children}
</div>
);
};

View File

@ -0,0 +1,31 @@
import { Component } from "solid-js";
import LogoLight from "/icons/zh/appbar_desktop_light.svg";
import LogoDark from "/icons/zh/appbar_desktop_dark.svg";
import { DynamicImage } from "~/components/utils/DynamicImage";
import {
AppBar,
AppBarLeadingElement,
AppBarSearchBox,
AppBarSearchContainer,
AppBarTrailingElement,
AppBarTrailingElementGroup,
IconButton
} from "@m3-components/solid";
export const NavigationDesktop: Component = () => {
return (
<AppBar class="hidden lg:flex h-20 xl:h-22 2xl:h-24" variant="search">
<AppBarLeadingElement class="h-full grow shrink basis-0">
<DynamicImage class="lg:block h-full" darkSrc={LogoDark} lightSrc={LogoLight} />
</AppBarLeadingElement>
<AppBarSearchContainer>
<AppBarSearchBox class="mx-auto text-center" placeholder="搜索" />
</AppBarSearchContainer>
<AppBarTrailingElementGroup class="h-full grow shrink basis-0">
<AppBarTrailingElement>
<IconButton></IconButton>
</AppBarTrailingElement>
</AppBarTrailingElementGroup>
</AppBar>
);
};

View File

@ -1,6 +1,4 @@
import { Component, createEffect, createMemo, createSignal, For, on, onMount } from "solid-js";
import { HomeIcon } from "../icons/Home";
import { MusicIcon } from "../icons/Music";
import { Component, createEffect, createSignal, For } from "solid-js";
import {
NavigationRailFAB,
NavigationRail,
@ -12,78 +10,14 @@ import {
AppBarSearchBox,
AppBarTrailingElementGroup,
AppBarTrailingElement,
IconButton, AppBarSearchContainer
IconButton,
AppBarSearchContainer
} from "@m3-components/solid";
import { A } from "@solidjs/router";
import { AlbumIcon } from "~/components/icons/Album";
import { SearchIcon } from "../icons/Search";
import { SearchIcon } from "~/components/icons/Search";
import { Portal } from "solid-js/web";
import { animate, createTimer, utils } from "animejs";
import { tv } from "tailwind-variants";
export const [activeTab, setActiveTab] = createSignal(-1);
export const [navigationExpanded, setNavigationExpanded] = createSignal(false);
interface Action {
icon: Component;
label: string;
href: string;
}
export const actions: Action[] = [
{
icon: HomeIcon,
label: "主页",
href: "/"
},
{
icon: MusicIcon,
label: "歌曲",
href: "/songs"
},
{
icon: AlbumIcon,
label: "专辑",
href: "/albums"
}
];
export const actionsEn: Action[] = [
{
icon: HomeIcon,
label: "Home",
href: "/en/"
},
{
icon: MusicIcon,
label: "Songs",
href: "/en/songs"
},
{
icon: AlbumIcon,
label: "Albums",
href: "/en/albums"
}
];
export const tabMap = {
"/": 0,
"/song*": 1,
"/song/**/*": 1,
"/albums": 2,
"/album/**/*": 2,
"/en/": 0,
"/en/songs": 1,
"/en/song*": 1,
"/en/song/**/*": 1,
"/en/albums": 2,
"/en/album/**/*": 2
};
const searchT = {
zh: "搜索",
en: "Search"
};
import { animate } from "animejs";
import { actions, actionsEn, activeTab, navigationExpanded, searchT, setActiveTab, setNavigationExpanded } from ".";
export const NavigationMobile: Component<{ lang?: "zh" | "en" }> = (props) => {
const [el, setEl] = createSignal<HTMLElement | null>(null);
@ -119,7 +53,7 @@ export const NavigationMobile: Component<{ lang?: "zh" | "en" }> = (props) => {
<AppBarLeadingElement>
<NavigationRailMenu class="invisible" />
</AppBarLeadingElement>
<AppBarSearchContainer class="w-[calc(100%-7.9rem)]">
<AppBarSearchContainer class="max-sm:w-[calc(100%-7.9rem)]">
<AppBarSearchBox placeholder="搜索" />
</AppBarSearchContainer>
<AppBarTrailingElementGroup>
@ -166,4 +100,4 @@ export const NavigationMobile: Component<{ lang?: "zh" | "en" }> = (props) => {
</Portal>
</>
);
};
};

View File

@ -0,0 +1,66 @@
import { Component, createSignal } from "solid-js";
import { AlbumIcon, HomeIcon, MusicIcon } from "~/components/icons";
export const [activeTab, setActiveTab] = createSignal(-1);
export const [navigationExpanded, setNavigationExpanded] = createSignal(false);
interface Action {
icon: Component;
label: string;
href: string;
}
export const actions: Action[] = [
{
icon: HomeIcon,
label: "主页",
href: "/"
},
{
icon: MusicIcon,
label: "歌曲",
href: "/songs"
},
{
icon: AlbumIcon,
label: "专辑",
href: "/albums"
}
];
export const actionsEn: Action[] = [
{
icon: HomeIcon,
label: "Home",
href: "/en/"
},
{
icon: MusicIcon,
label: "Songs",
href: "/en/songs"
},
{
icon: AlbumIcon,
label: "Albums",
href: "/en/albums"
}
];
export const tabMap = {
"/": 0,
"/song*": 1,
"/song/**/*": 1,
"/albums": 2,
"/album/**/*": 2,
"/en/": 0,
"/en/songs": 1,
"/en/song*": 1,
"/en/song/**/*": 1,
"/en/albums": 2,
"/en/album/**/*": 2
};
export const searchT = {
zh: "搜索",
en: "Search"
};

View File

@ -0,0 +1,28 @@
import { NavigationMobile } from "./Navigation/Mobile";
import { DivProps } from "../common";
import { Component } from "solid-js";
import { BeforeLeaveEventArgs, useBeforeLeave } from "@solidjs/router";
import { refreshTab } from "~/app";
import { NavigationDesktop } from "./Navigation/Desktop";
import { BodyRegion } from "./Body";
interface LayoutProps extends DivProps {
lang?: "zh" | "en";
}
export const Layout: Component<LayoutProps> = (props) => {
useBeforeLeave((e: BeforeLeaveEventArgs) => {
if (typeof e.to === "number") {
refreshTab(e.to.toString());
return;
}
refreshTab(e.to);
});
return (
<div class="relatve w-screen min-h-screen">
<NavigationMobile lang={props.lang} />
<NavigationDesktop />
<BodyRegion>{props.children}</BodyRegion>
</div>
);
};

View File

@ -1,58 +0,0 @@
import { NavigationMobile } from "./Navigation";
import { DivProps } from "../common";
import { Component } from "solid-js";
import { BeforeLeaveEventArgs, useBeforeLeave } from "@solidjs/router";
import { refreshTab } from "~/app";
import LogoLight from "/icons/zh/appbar_desktop_light.svg";
import LogoDark from "/icons/zh/appbar_desktop_dark.svg";
import { DynamicImage } from "~/components/utils/DynamicImage";
import {
AppBar,
AppBarLeadingElement,
AppBarSearchBox,
AppBarSearchContainer,
AppBarTrailingElement,
AppBarTrailingElementGroup,
IconButton
} from "@m3-components/solid";
export const BodyRegion: Component<DivProps> = (props) => {
return (
<div class="pt-12 px-4" {...props}>
{props.children}
</div>
);
};
interface LayoutProps extends DivProps {
lang?: "zh" | "en";
}
export const Layout: Component<LayoutProps> = (props) => {
useBeforeLeave((e: BeforeLeaveEventArgs) => {
if (typeof e.to === "number") {
refreshTab(e.to.toString());
return;
}
refreshTab(e.to);
});
return (
<div class="relatve w-screen min-h-screen">
<NavigationMobile lang={props.lang} />
<AppBar class="hidden lg:flex h-20 xl:h-22 2xl:h-24" variant="search">
<AppBarLeadingElement class="h-full grow shrink basis-0">
<DynamicImage class="lg:block h-full" darkSrc={LogoDark} lightSrc={LogoLight} />
</AppBarLeadingElement>
<AppBarSearchContainer>
<AppBarSearchBox class="mx-auto text-center" placeholder="搜索" />
</AppBarSearchContainer>
<AppBarTrailingElementGroup class="h-full grow shrink basis-0">
<AppBarTrailingElement>
<IconButton></IconButton>
</AppBarTrailingElement>
</AppBarTrailingElementGroup>
</AppBar>
<BodyRegion>{props.children}</BodyRegion>
</div>
);
};

View File

@ -1,21 +1,14 @@
import { Button } from "@m3-components/solid";
import { A } from "@solidjs/router";
import { tv } from "tailwind-variants";
import { navigationExpanded } from "~/components/shell/Navigation";
import { Component, splitProps } from "solid-js";
import { ElementProps } from "../common";
export const TabSwitcher: Component<ElementProps> = (props) => {
const [_v, rest] = splitProps(props, ["class"]);
export const TabSwitcher = () => {
const tabsContainerStyle = tv({
base: "w-full lg:w-48 gap-4 flex lg:flex-col items-center",
variants: {
expanded: {
true: "lg:self-start xl:self-center",
false: "self-center"
}
}
});
return (
<nav class="flex flex-col lg:h-screen lg:px-6 lg:pt-12">
<div class={tabsContainerStyle({ expanded: navigationExpanded() })}>
<nav class="flex flex-col" {...rest}>
<div class="w-full lg:w-48 gap-4 flex lg:flex-col items-center lg:self-center 2xl:self-end">
<A class="w-full" href="../info">
<Button class="w-full" variant="filled">

View File

@ -1,6 +1,6 @@
import { Title } from "@solidjs/meta";
import { HttpStatusCode } from "@solidjs/start";
import { Layout } from "~/components/shell/Layout";
import { Layout } from "~/components/layout";
import { A } from "@solidjs/router";
export default function NotFound() {
@ -8,7 +8,7 @@ export default function NotFound() {
<Layout>
<Title></Title>
<HttpStatusCode code={404} />
<main class="w-full h-screen flex flex-col flex-grow items-center justify-center gap-8">
<main class="w-full h-[calc(100vh-6rem)] flex flex-col flex-grow items-center justify-center gap-8">
<h1 class="text-9xl font-thin">404</h1>
<p class="text-xl font-medium">(Дд)!?</p>
<A href="/">

View File

@ -1,4 +1,4 @@
import { Layout } from "~/components/shell/Layout";
import { Layout } from "~/components/layout";
import { query } from "@solidjs/router";
import { Card, CardContent, CardMedia, Typography } from "@m3-components/solid";

View File

@ -1,4 +1,4 @@
import { Layout } from "~/components/shell/Layout";
import { Layout } from "~/components/layout";
import { dbMain } from "~/drizzle";
import { bilibiliMetadata, latestVideoSnapshot } from "~db/main/schema";
import { and, desc, eq, gte, lt } from "drizzle-orm";

View File

@ -9,7 +9,7 @@ import { bilibiliMetadata, videoSnapshot } from "~db/main/schema";
import { desc, eq } from "drizzle-orm";
import { BilibiliMetadataType, VideoSnapshotType } from "~db/outerSchema";
import { Context, useRequestContext } from "~/components/requestContext";
import { Layout } from "~/components/shell/Layout";
import { Layout } from "~/components/layout";
async function getAllSnapshots(aid: number, context: Context) {
"use server";

View File

@ -1,17 +1,37 @@
import { Layout } from "~/components/shell/Layout";
import { Card, CardContent, CardMedia, Typography } from "@m3-components/solid";
import { Layout } from "~/components/layout";
import {
Button,
Card,
CardContent,
CardMedia,
ExtendedFAB,
FloatingActionButton,
Typography
} from "@m3-components/solid";
import { TabSwitcher } from "~/components/song/TabSwitcher";
import { EditIcon, HomeIcon, MusicIcon } from "~/components/icons";
export default function Info() {
return (
<Layout>
<title></title>
<div
class="w-full sm:w-120 sm:mx-auto lg:w-full lg:grid lg:grid-cols-[1fr_560px_minmax(300px,_1fr)]
class="pt-12 px-4 w-full sm:w-120 sm:mx-auto lg:w-full 2xl:w-360 lg:grid lg:grid-cols-[1fr_560px_1fr]
xl:grid-cols-[1fr_648px_1fr]"
>
<nav class="hidden opacity-0 pointer-events-none lg:block xl:opacity-100 xl:pointer-events-auto pt-4"></nav>
<nav class="hidden pointer-events-none lg:block xl:pointer-events-auto pt-4">
<div class="inline-flex flex-col gap-2">
<Button variant="outlined" class="gap-1 items-center" size="extra-small">
<HomeIcon class="w-5 h-5 text-xl -translate-y-0.25" />
<span></span>
</Button>
<Button variant="outlined" class="gap-1 items-center" size="extra-small">
<MusicIcon class="w-5 h-5 text-xl" />
<span></span>
</Button>
</div>
</nav>
<main>
<Card variant="outlined" class="w-full">
<CardMedia
@ -57,7 +77,12 @@ export default function Info() {
</Typography.Body>
</article>
</main>
<div class="hidden lg:block">
<div class="hidden lg:flex flex-col px-6">
<div class="w-48 self-center 2xl:self-end flex justify-end mb-6">
<ExtendedFAB position="unset" size="small" text="编辑" color="primary">
<EditIcon />
</ExtendedFAB>
</div>
<TabSwitcher />
</div>
</div>

View File

@ -1,4 +1,4 @@
import { Layout } from "~/components/shell/Layout";
import { Layout } from "~/components/layout";
export default function SongsHome() {
return (