diff --git a/docs/src/components/sandbox/index.ts b/docs/src/components/sandbox/index.ts index 7eb76a3..89f8ecd 100644 --- a/docs/src/components/sandbox/index.ts +++ b/docs/src/components/sandbox/index.ts @@ -7,6 +7,8 @@ export { export { defineSandbox, type SandboxDefinition, + type SandboxLocalizedText, + type SandboxPreviewProps, type SandboxPreviewModule, type SandboxSource, type SandboxSourceModule, diff --git a/docs/src/components/sandbox/registry.ts b/docs/src/components/sandbox/registry.ts index a6c5f83..649c7db 100644 --- a/docs/src/components/sandbox/registry.ts +++ b/docs/src/components/sandbox/registry.ts @@ -5,7 +5,10 @@ type SandboxManifestModule = { } const manifestModules = import.meta.glob( - "../../sandboxes/*/*/sandbox.ts", + [ + "../../sandboxes/*/*/sandbox.ts", + "../../../../packages/*/docs/sandboxes/*/sandbox.ts", + ], { eager: true } ) diff --git a/docs/src/components/sandbox/sandbox-env.d.ts b/docs/src/components/sandbox/sandbox-env.d.ts new file mode 100644 index 0000000..2a6eac5 --- /dev/null +++ b/docs/src/components/sandbox/sandbox-env.d.ts @@ -0,0 +1,4 @@ +declare module "*?raw" { + const source: string + export default source +} diff --git a/docs/src/components/sandbox/sandbox-frame.tsx b/docs/src/components/sandbox/sandbox-frame.tsx index c2eb7f9..ba26be9 100644 --- a/docs/src/components/sandbox/sandbox-frame.tsx +++ b/docs/src/components/sandbox/sandbox-frame.tsx @@ -1,16 +1,20 @@ import React from "react" import { createPortal } from "react-dom" +import type { DocLocale } from "../../content/types" + const minimumFrameHeight = 288 export function SandboxFrame({ children, fill = false, + locale, reloadKey, title, }: { children: React.ReactNode fill?: boolean + locale: DocLocale reloadKey: number title: string }) { @@ -56,6 +60,7 @@ export function SandboxFrame({ document.documentElement.style.colorScheme } syncDocumentTheme() + frameDocument.documentElement.lang = locale const themeObserver = new MutationObserver(syncDocumentTheme) themeObserver.observe(document.documentElement, { @@ -70,7 +75,7 @@ export function SandboxFrame({ themeObserver.disconnect() setMountNode(null) } - }, [reloadKey]) + }, [locale, reloadKey]) React.useEffect(() => { if (!mountNode || fill) return diff --git a/docs/src/components/sandbox/sandbox.tsx b/docs/src/components/sandbox/sandbox.tsx index 57dbfb3..1c4b6bf 100644 --- a/docs/src/components/sandbox/sandbox.tsx +++ b/docs/src/components/sandbox/sandbox.tsx @@ -1,13 +1,15 @@ import React from "react" import { Icon } from "@icones/react" -import { useTranslate } from "../../lib/locale" +import { useLocale, useTranslate } from "../../lib/locale" import { getSandboxDefinition } from "./registry" import { SandboxErrorBoundary } from "./sandbox-error-boundary" import { SandboxFrame } from "./sandbox-frame" import { highlightSource, type HighlightedSource } from "./highlight-source" import type { SandboxDefinition, + SandboxLocalizedText, + SandboxPreviewProps, SandboxPreviewModule, SandboxSource, } from "./types" @@ -19,7 +21,7 @@ const codeFontSizes = [12, 13, 14, 16] as const export type SandboxProps = { className?: string example: string - title?: string + title?: SandboxLocalizedText } export function Sandbox({ className, example, title }: SandboxProps) { @@ -51,7 +53,7 @@ export type SingleFileSandboxProps = { component: React.ComponentType filename?: string source: string - title?: string + title?: SandboxLocalizedText } export function SingleFileSandbox({ @@ -64,7 +66,9 @@ export function SingleFileSandbox({ const definition = React.useMemo( () => ({ defaultFile: filename, - entry: async () => ({ default: component }), + entry: async () => ({ + default: component as React.ComponentType, + }), files: { [filename]: source }, id: `single-file:${filename}`, title, @@ -82,9 +86,10 @@ function SandboxViewer({ }: { className?: string definition: SandboxDefinition - title?: string + title?: SandboxLocalizedText }) { const rootRef = React.useRef(null) + const locale = useLocale() const t = useTranslate() const filenames = React.useMemo( () => Object.keys(definition.files), @@ -105,7 +110,11 @@ function SandboxViewer({ const [reloadKey, setReloadKey] = React.useState(0) const [copied, setCopied] = React.useState(false) const [fullscreen, setFullscreen] = React.useState(false) - const resolvedTitle = title ?? definition.title ?? definition.id + const resolvedTitle = resolveSandboxText( + title ?? definition.title, + locale, + definition.id + ) React.useEffect(() => { const root = rootRef.current @@ -288,6 +297,7 @@ function SandboxViewer({ {view === "preview" ? ( @@ -297,7 +307,10 @@ function SandboxViewer({ {previewError.message} ) : previewModule ? ( - React.createElement(previewModule.default, { key: reloadKey }) + React.createElement(previewModule.default, { + key: reloadKey, + locale, + }) ) : ( {t({ zh: "正在加载预览…", us: "Loading preview…" })} @@ -350,6 +363,15 @@ function SandboxViewer({ ) } +function resolveSandboxText( + value: SandboxLocalizedText | undefined, + locale: "en-US" | "zh-Hans", + fallback: string +) { + if (typeof value === "string") return value + return value?.[locale] ?? fallback +} + function CodeFontSizeControl({ decreaseLabel, increaseLabel, diff --git a/docs/src/components/sandbox/types.ts b/docs/src/components/sandbox/types.ts index 46a5c47..ceb7dd5 100644 --- a/docs/src/components/sandbox/types.ts +++ b/docs/src/components/sandbox/types.ts @@ -1,7 +1,15 @@ import type { ComponentType } from "react" +import type { DocLocale } from "../../content/types" + +export type SandboxLocalizedText = string | Readonly> + +export type SandboxPreviewProps = { + locale: DocLocale +} + export type SandboxPreviewModule = { - default: ComponentType + default: ComponentType } export type SandboxSourceModule = { @@ -17,7 +25,7 @@ export type SandboxDefinition = { entry: () => Promise files: Readonly> id: string - title?: string + title?: SandboxLocalizedText } export function defineSandbox( diff --git a/docs/src/index.css b/docs/src/index.css index 2378135..eee6224 100644 --- a/docs/src/index.css +++ b/docs/src/index.css @@ -5,6 +5,8 @@ @import "@workspace/page/globals.css"; @import "@workspace/search/globals.css"; +@source "../../packages/*/docs/**/*.{mdx,ts,tsx}"; + @custom-variant dark (&:where(.dark, .dark *)); @layer base { diff --git a/docs/src/sandboxes/appearance/theme-toggle/App.tsx b/docs/src/sandboxes/appearance/theme-toggle/App.tsx deleted file mode 100644 index 94d427c..0000000 --- a/docs/src/sandboxes/appearance/theme-toggle/App.tsx +++ /dev/null @@ -1,22 +0,0 @@ -import React from "react" -import type { ThemeColorConfig } from "@workspace/appearance/theme" - -import { AccentPicker } from "./AccentPicker" - -export default function ThemeColorsExample() { - const [config, setConfig] = React.useState({ - accentColor: "indigo", - baseColor: "neutral", - }) - - return ( -
- - -
- ) -} diff --git a/docs/tsconfig.app.json b/docs/tsconfig.app.json index 6830b6f..446d8e2 100644 --- a/docs/tsconfig.app.json +++ b/docs/tsconfig.app.json @@ -22,5 +22,9 @@ "erasableSyntaxOnly": true, "noFallthroughCasesInSwitch": true }, - "include": ["src"] -} + "include": [ + "src", + "../packages/*/docs/sandboxes/**/*.ts", + "../packages/*/docs/sandboxes/**/*.tsx" + ] +} \ No newline at end of file diff --git a/docs/tsconfig.sandbox.json b/docs/tsconfig.sandbox.json new file mode 100644 index 0000000..1e2ce87 --- /dev/null +++ b/docs/tsconfig.sandbox.json @@ -0,0 +1,8 @@ +{ + "extends": "./tsconfig.app.json", + "compilerOptions": { + "types": [] + }, + "files": ["src/components/sandbox/sandbox-env.d.ts"], + "include": [] +} \ No newline at end of file diff --git a/docs/src/sandboxes/appearance/theme-toggle/AccentPicker.tsx b/packages/appearance/docs/sandboxes/theme-toggle/AccentPicker.tsx similarity index 93% rename from docs/src/sandboxes/appearance/theme-toggle/AccentPicker.tsx rename to packages/appearance/docs/sandboxes/theme-toggle/AccentPicker.tsx index 6d14402..6137bfd 100644 --- a/docs/src/sandboxes/appearance/theme-toggle/AccentPicker.tsx +++ b/packages/appearance/docs/sandboxes/theme-toggle/AccentPicker.tsx @@ -8,15 +8,17 @@ import type { ThemeColorConfig } from "@workspace/appearance/theme" export function AccentPicker({ config, onConfigChange, + title, variant, }: { config: ThemeColorConfig onConfigChange: (config: ThemeColorConfig) => void + title: string variant?: ThemeColorPickerVariant }) { return ( ({ + accentColor: "indigo", + baseColor: "neutral", + }) + + const title = locale === "zh-Hans" ? "强调色" : "Accent color" + + return ( + +
+ + +
+
+ ) +} diff --git a/docs/src/sandboxes/appearance/theme-toggle/sandbox.ts b/packages/appearance/docs/sandboxes/theme-toggle/sandbox.ts similarity index 52% rename from docs/src/sandboxes/appearance/theme-toggle/sandbox.ts rename to packages/appearance/docs/sandboxes/theme-toggle/sandbox.ts index 95be740..615d25a 100644 --- a/docs/src/sandboxes/appearance/theme-toggle/sandbox.ts +++ b/packages/appearance/docs/sandboxes/theme-toggle/sandbox.ts @@ -1,12 +1,13 @@ -import { defineSandbox } from "../../../components/sandbox" - -export default defineSandbox({ +export default { id: "appearance/theme-colors", - title: "Controlled theme colors", + title: { + "en-US": "Controlled theme colors", + "zh-Hans": "受控主题颜色", + }, entry: () => import("./App"), files: { "App.tsx": () => import("./App.tsx?raw"), "AccentPicker.tsx": () => import("./AccentPicker.tsx?raw"), }, defaultFile: "App.tsx", -}) +} satisfies import("../../../../../docs/src/components/sandbox/types").SandboxDefinition diff --git a/packages/appearance/docs/tsconfig.json b/packages/appearance/docs/tsconfig.json new file mode 100644 index 0000000..8801e35 --- /dev/null +++ b/packages/appearance/docs/tsconfig.json @@ -0,0 +1,7 @@ +{ + "extends": "../../../docs/tsconfig.sandbox.json", + "compilerOptions": { + "tsBuildInfoFile": "../../../docs/node_modules/.tmp/appearance-docs.tsbuildinfo" + }, + "include": ["sandboxes/**/*.ts", "sandboxes/**/*.tsx"] +} \ No newline at end of file