fix(ui): use sonner toast library with product styling
Replace hand-rolled notice with sonner; single Toaster in AppLayout; token-based toast styles, top-right.
This commit is contained in:
parent
e0ecf5b106
commit
dcf66f23d0
|
|
@ -2,7 +2,6 @@
|
||||||
* 鉴权页主体:薄 SSR — 外层容器 + AuthForm 在浏览器渲染。
|
* 鉴权页主体:薄 SSR — 外层容器 + AuthForm 在浏览器渲染。
|
||||||
*/
|
*/
|
||||||
import type { Locale } from "@app/i18n";
|
import type { Locale } from "@app/i18n";
|
||||||
import { Toaster } from "@app/ui";
|
|
||||||
import AuthForm, { type AuthFormMode } from "~/components/AuthForm";
|
import AuthForm, { type AuthFormMode } from "~/components/AuthForm";
|
||||||
|
|
||||||
export interface AuthBodyProps {
|
export interface AuthBodyProps {
|
||||||
|
|
@ -18,7 +17,6 @@ export default function AuthBody({ locale, mode, next, token }: AuthBodyProps) {
|
||||||
<div className="flex min-h-[60vh] items-center justify-center px-4 py-16">
|
<div className="flex min-h-[60vh] items-center justify-center px-4 py-16">
|
||||||
<AuthForm locale={locale} mode={mode} next={next} defaultToken={token} />
|
<AuthForm locale={locale} mode={mode} next={next} defaultToken={token} />
|
||||||
</div>
|
</div>
|
||||||
<Toaster />
|
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
import { ClientRouter } from "astro:transitions";
|
import { ClientRouter } from "astro:transitions";
|
||||||
import "../styles/globals.css";
|
import "../styles/globals.css";
|
||||||
import UserMenu from "~/components/UserMenu";
|
import UserMenu from "~/components/UserMenu";
|
||||||
|
import { Toaster } from "@app/ui";
|
||||||
import ThemeSwitch from "~/components/ThemeSwitch";
|
import ThemeSwitch from "~/components/ThemeSwitch";
|
||||||
import LocaleSwitch from "~/components/LocaleSwitch";
|
import LocaleSwitch from "~/components/LocaleSwitch";
|
||||||
import {
|
import {
|
||||||
|
|
@ -238,5 +239,6 @@ const nav = [
|
||||||
</footer>
|
</footer>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</body>
|
<Toaster client:load />
|
||||||
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
---
|
---
|
||||||
import { type Locale, t } from "@app/i18n";
|
import { type Locale, t } from "@app/i18n";
|
||||||
import { Toaster } from "@app/ui";
|
|
||||||
import { BrowseBody } from "~/components/pages/BrowseBody";
|
import { BrowseBody } from "~/components/pages/BrowseBody";
|
||||||
import AppLayout from "~/layouts/AppLayout.astro";
|
import AppLayout from "~/layouts/AppLayout.astro";
|
||||||
|
|
||||||
|
|
@ -23,5 +22,4 @@ const { locale } = Astro.props;
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<BrowseBody locale={locale} client:load />
|
<BrowseBody locale={locale} client:load />
|
||||||
<Toaster client:load />
|
|
||||||
</AppLayout>
|
</AppLayout>
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
---
|
---
|
||||||
import { type Locale, t } from "@app/i18n";
|
import { type Locale, t } from "@app/i18n";
|
||||||
import { Toaster } from "@app/ui";
|
|
||||||
import { DanmakuSettingsBody } from "~/components/pages/DanmakuSettingsBody";
|
import { DanmakuSettingsBody } from "~/components/pages/DanmakuSettingsBody";
|
||||||
import AppLayout from "~/layouts/AppLayout.astro";
|
import AppLayout from "~/layouts/AppLayout.astro";
|
||||||
|
|
||||||
|
|
@ -23,5 +22,4 @@ const { locale } = Astro.props;
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<DanmakuSettingsBody locale={locale} client:load />
|
<DanmakuSettingsBody locale={locale} client:load />
|
||||||
<Toaster client:load />
|
|
||||||
</AppLayout>
|
</AppLayout>
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
---
|
---
|
||||||
import { type Locale, t } from "@app/i18n";
|
import { type Locale, t } from "@app/i18n";
|
||||||
import { Toaster } from "@app/ui";
|
|
||||||
import { LibraryBody } from "~/components/pages/LibraryBody";
|
import { LibraryBody } from "~/components/pages/LibraryBody";
|
||||||
import AppLayout from "~/layouts/AppLayout.astro";
|
import AppLayout from "~/layouts/AppLayout.astro";
|
||||||
|
|
||||||
|
|
@ -23,5 +22,4 @@ const { locale } = Astro.props;
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<LibraryBody locale={locale} client:load />
|
<LibraryBody locale={locale} client:load />
|
||||||
<Toaster client:load />
|
|
||||||
</AppLayout>
|
</AppLayout>
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
---
|
---
|
||||||
import { type Locale, t } from "@app/i18n";
|
import { type Locale, t } from "@app/i18n";
|
||||||
import { Toaster } from "@app/ui";
|
|
||||||
import { MountsBody } from "~/components/pages/MountsBody";
|
import { MountsBody } from "~/components/pages/MountsBody";
|
||||||
import AppLayout from "~/layouts/AppLayout.astro";
|
import AppLayout from "~/layouts/AppLayout.astro";
|
||||||
|
|
||||||
|
|
@ -23,5 +22,4 @@ const { locale } = Astro.props;
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<MountsBody locale={locale} client:load />
|
<MountsBody locale={locale} client:load />
|
||||||
<Toaster client:load />
|
|
||||||
</AppLayout>
|
</AppLayout>
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
---
|
---
|
||||||
import { type Locale, t } from "@app/i18n";
|
import { type Locale, t } from "@app/i18n";
|
||||||
import { Toaster } from "@app/ui";
|
|
||||||
import { WatchBody } from "~/components/pages/WatchBody";
|
import { WatchBody } from "~/components/pages/WatchBody";
|
||||||
import AppLayout from "~/layouts/AppLayout.astro";
|
import AppLayout from "~/layouts/AppLayout.astro";
|
||||||
|
|
||||||
|
|
@ -24,5 +23,4 @@ const { locale, mediaItemId } = Astro.props;
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<WatchBody locale={locale} mediaItemId={mediaItemId} client:load />
|
<WatchBody locale={locale} mediaItemId={mediaItemId} client:load />
|
||||||
<Toaster client:load />
|
|
||||||
</AppLayout>
|
</AppLayout>
|
||||||
|
|
|
||||||
|
|
@ -3,19 +3,31 @@ import { Toaster as SonnerToaster, toast } from "sonner";
|
||||||
|
|
||||||
type ToasterProps = React.ComponentProps<typeof SonnerToaster>;
|
type ToasterProps = React.ComponentProps<typeof SonnerToaster>;
|
||||||
|
|
||||||
const Toaster = ({ style, ...props }: ToasterProps) => {
|
/**
|
||||||
|
* sonner 封装:站点统一 toast。
|
||||||
|
* 视觉用 design tokens,位置右上角,跟随主题。
|
||||||
|
*/
|
||||||
|
const Toaster = ({ ...props }: ToasterProps) => {
|
||||||
return (
|
return (
|
||||||
<SonnerToaster
|
<SonnerToaster
|
||||||
theme="light"
|
position="top-right"
|
||||||
className="toaster group"
|
offset={16}
|
||||||
|
gap={10}
|
||||||
|
mobileOffset={12}
|
||||||
toastOptions={{
|
toastOptions={{
|
||||||
|
unstyled: false,
|
||||||
classNames: {
|
classNames: {
|
||||||
toast: "group toast group-[.toaster]:bg-[var(--color-background)] group-[.toaster]:text-[var(--color-foreground)] group-[.toaster]:border-[var(--color-border)] group-[.toaster]:shadow-lg",
|
toast:
|
||||||
description: "group-[.toast]:text-[var(--color-muted-foreground)]",
|
"group toast w-[min(22rem,calc(100vw-2rem))] rounded-lg border border-[var(--color-border)] bg-[var(--color-background)] px-3.5 py-2.5 text-sm text-[var(--color-foreground)] shadow-lg",
|
||||||
|
title: "text-sm font-medium leading-snug",
|
||||||
|
description: "text-[var(--color-muted-foreground)] text-xs",
|
||||||
actionButton:
|
actionButton:
|
||||||
"group-[.toast]:bg-[var(--color-primary)] group-[.toast]:text-[var(--color-primary-foreground)]",
|
"bg-[var(--color-primary)] text-[var(--color-primary-foreground)] rounded-md px-2.5 py-1 text-xs font-medium",
|
||||||
cancelButton:
|
cancelButton:
|
||||||
"group-[.toast]:bg-[var(--color-muted)] group-[.toast]:text-[var(--color-muted-foreground)]",
|
"bg-[var(--color-muted)] text-[var(--color-muted-foreground)] rounded-md px-2.5 py-1 text-xs",
|
||||||
|
success: "border-[var(--color-border)]",
|
||||||
|
error: "border-[var(--color-destructive,#dc2626)]/45",
|
||||||
|
info: "border-[var(--color-border)]",
|
||||||
},
|
},
|
||||||
}}
|
}}
|
||||||
{...props}
|
{...props}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue