Skip to content

Commit ae361d6

Browse files
committed
fix: 移动端等比缩放桌面组件预览
1 parent c34f6f1 commit ae361d6

1 file changed

Lines changed: 80 additions & 21 deletions

File tree

‎app/[lang]/components/component-gallery.tsx‎

Lines changed: 80 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
"use client"
22

3-
import { useState, type ReactNode } from "react"
3+
import { useEffect, useRef, useState, type ReactNode } from "react"
44
import {
55
CalendarDays,
66
Cloud,
@@ -129,7 +129,9 @@ export function NoteGenComponentGallery({ lang }: { lang: NoteGenReplicaLanguage
129129
)}
130130
>
131131
<div className="mx-auto w-full max-w-6xl">
132-
<NoteGenDesktopReplica lang={lang} autoCycle={false} />
132+
<DesktopScaledPreview baseWidth={1152}>
133+
<NoteGenDesktopReplica lang={lang} autoCycle={false} />
134+
</DesktopScaledPreview>
133135
</div>
134136
</GallerySection>
135137

@@ -143,7 +145,7 @@ export function NoteGenComponentGallery({ lang }: { lang: NoteGenReplicaLanguage
143145
)}
144146
>
145147
<div className="grid gap-4 lg:grid-cols-2">
146-
<ComponentCard title="NoteGenWindowTitleBar" description={text("完整窗口标题栏", "Full window title bar")}>
148+
<ComponentCard desktopWidth={560} title="NoteGenWindowTitleBar" description={text("完整窗口标题栏", "Full window title bar")}>
147149
<div className="overflow-hidden rounded-lg border bg-background">
148150
<NoteGenWindowTitleBar lang={lang} />
149151
<div className="flex h-24 items-center justify-center text-xs text-muted-foreground">
@@ -152,22 +154,22 @@ export function NoteGenComponentGallery({ lang }: { lang: NoteGenReplicaLanguage
152154
</div>
153155
</ComponentCard>
154156

155-
<ComponentCard title="NoteGenWorkspaceSwitcher" description={text("可交互的工作区入口", "Interactive workspace navigation")}>
157+
<ComponentCard desktopWidth={560} title="NoteGenWorkspaceSwitcher" description={text("可交互的工作区入口", "Interactive workspace navigation")}>
156158
<div className="flex min-h-32 items-center justify-center rounded-lg border bg-background">
157159
<NoteGenWorkspaceSwitcher lang={lang} value={workspace} onValueChange={setWorkspace} />
158160
</div>
159161
</ComponentCard>
160162

161163
{titleBarModes.map(([mode, cnLabel, enLabel]) => (
162-
<ComponentCard key={mode} title={`NoteGenWindowTitleBar · ${mode}`} description={lang === "en" ? enLabel : cnLabel}>
164+
<ComponentCard desktopWidth={560} key={mode} title={`NoteGenWindowTitleBar · ${mode}`} description={lang === "en" ? enLabel : cnLabel}>
163165
<div className="overflow-hidden rounded-lg border bg-background">
164166
<NoteGenWindowTitleBar lang={lang} mode={mode} />
165167
<div className="h-12" />
166168
</div>
167169
</ComponentCard>
168170
))}
169171

170-
<ComponentCard title="NoteGenReplicaToolbar" description={text("任意组合的工具按钮", "Freely composable tool buttons")}>
172+
<ComponentCard desktopWidth={560} title="NoteGenReplicaToolbar" description={text("任意组合的工具按钮", "Freely composable tool buttons")}>
171173
<div className="overflow-hidden rounded-lg border bg-background">
172174
<NoteGenReplicaToolbar label={text("示例工具栏", "Example toolbar")}>
173175
<NoteGenReplicaIconButton icon={FilePlus} label={text("新建文件", "New file")} />
@@ -182,7 +184,7 @@ export function NoteGenComponentGallery({ lang }: { lang: NoteGenReplicaLanguage
182184
</div>
183185
</ComponentCard>
184186

185-
<ComponentCard title="Frame · Panel · MenuGroup" description={text("用于自定义文档场景的结构原语", "Structural primitives for custom documentation scenes")}>
187+
<ComponentCard desktopWidth={560} title="Frame · Panel · MenuGroup" description={text("用于自定义文档场景的结构原语", "Structural primitives for custom documentation scenes")}>
186188
<NoteGenReplicaFrame fill className="h-28 shadow-none">
187189
<div className="grid h-full grid-cols-[38%_1fr]">
188190
<NoteGenReplicaPanel className="border-r bg-sidebar p-2">
@@ -209,13 +211,13 @@ export function NoteGenComponentGallery({ lang }: { lang: NoteGenReplicaLanguage
209211
description={text("每个工作区都由更小的列表、工具栏、内容区和状态组件组合而成。", "Each workspace is composed from smaller lists, toolbars, content areas, and state components.")}
210212
>
211213
<div className="grid gap-6">
212-
<ComponentCard title="NoteGenRecordWorkspace" description={text("记录列表与详情", "Capture list and detail")}><div className="h-[520px] overflow-hidden rounded-lg border"><NoteGenRecordWorkspace lang={lang} /></div></ComponentCard>
213-
<ComponentCard title="NoteGenEditorWorkspace" description={text("文件树、标签页与 Markdown 编辑器", "File tree, tabs, and Markdown editor")}><div className="h-[520px] overflow-hidden rounded-lg border"><NoteGenEditorWorkspace lang={lang} /></div></ComponentCard>
214+
<ComponentCard desktopWidth={1152} title="NoteGenRecordWorkspace" description={text("记录列表与详情", "Capture list and detail")}><div className="h-[520px] overflow-hidden rounded-lg border"><NoteGenRecordWorkspace lang={lang} /></div></ComponentCard>
215+
<ComponentCard desktopWidth={1152} title="NoteGenEditorWorkspace" description={text("文件树、标签页与 Markdown 编辑器", "File tree, tabs, and Markdown editor")}><div className="h-[520px] overflow-hidden rounded-lg border"><NoteGenEditorWorkspace lang={lang} /></div></ComponentCard>
214216
<div className="grid gap-6 xl:grid-cols-2">
215-
<ComponentCard title="NoteGenAgentPanel" description={text("消息、思考过程、工具调用与上下文", "Messages, reasoning, tool calls, and context")}><div className="h-[560px] overflow-hidden rounded-lg border"><NoteGenAgentPanel lang={lang} /></div></ComponentCard>
216-
<ComponentCard title="NoteGenCanvasWorkspace" description={text("画布项目、节点、连线和缩放工具", "Canvas projects, nodes, edges, and zoom tools")}><div className="h-[560px] overflow-hidden rounded-lg border"><NoteGenCanvasWorkspace lang={lang} /></div></ComponentCard>
217+
<ComponentCard desktopWidth={552} title="NoteGenAgentPanel" description={text("消息、思考过程、工具调用与上下文", "Messages, reasoning, tool calls, and context")}><div className="h-[560px] overflow-hidden rounded-lg border"><NoteGenAgentPanel lang={lang} /></div></ComponentCard>
218+
<ComponentCard desktopWidth={552} title="NoteGenCanvasWorkspace" description={text("画布项目、节点、连线和缩放工具", "Canvas projects, nodes, edges, and zoom tools")}><div className="h-[560px] overflow-hidden rounded-lg border"><NoteGenCanvasWorkspace lang={lang} /></div></ComponentCard>
217219
</div>
218-
<ComponentCard title="NoteGenAppShell" description={text("当前桌面外壳:平台窗口控制、记录工具与状态栏", "Current desktop shell with platform controls, capture toolbar, and status bar")}><NoteGenAppShell lang={lang}><div className="grid h-full grid-cols-[28%_42%_30%]"><div className="border-r"><NoteGenRecordWorkspace lang={lang} /></div><div className="border-r"><NoteGenEditorWorkspace lang={lang} /></div><NoteGenAgentPanel lang={lang} /></div></NoteGenAppShell></ComponentCard>
220+
<ComponentCard desktopWidth={1152} title="NoteGenAppShell" description={text("当前桌面外壳:平台窗口控制、记录工具与状态栏", "Current desktop shell with platform controls, capture toolbar, and status bar")}><NoteGenAppShell lang={lang}><div className="grid h-full grid-cols-[28%_42%_30%]"><div className="border-r"><NoteGenRecordWorkspace lang={lang} /></div><div className="border-r"><NoteGenEditorWorkspace lang={lang} /></div><NoteGenAgentPanel lang={lang} /></div></NoteGenAppShell></ComponentCard>
219221
</div>
220222
</GallerySection>
221223

@@ -237,11 +239,11 @@ export function NoteGenComponentGallery({ lang }: { lang: NoteGenReplicaLanguage
237239
description={text("用于文档说明搜索、录音、同步、冲突、图片预览和关闭确认等瞬时界面。", "Transient UI for documenting search, recording, sync, conflicts, image viewing, and close confirmation.")}
238240
>
239241
<div className="grid gap-6 lg:grid-cols-2">
240-
<ComponentCard title="NoteGenGlobalSearch" description={text("全局搜索面板", "Global search palette")}><NoteGenDialogBackdrop><NoteGenGlobalSearch lang={lang} /></NoteGenDialogBackdrop></ComponentCard>
241-
<ComponentCard title="NoteGenRecordingOverlay" description={text("录音对话框", "Recording dialog")}><NoteGenDialogBackdrop><NoteGenRecordingOverlay lang={lang} /></NoteGenDialogBackdrop></ComponentCard>
242-
<ComponentCard title="NoteGenSyncStatus" description={text("同步完成与冲突状态", "Sync and conflict states")}><div className="flex min-h-64 flex-col items-center justify-center gap-3 rounded-xl border bg-muted/25 p-4"><NoteGenSyncStatus lang={lang} /><NoteGenSyncStatus lang={lang} state="conflict" /></div></ComponentCard>
243-
<ComponentCard title="ImageViewer · Confirmation" description={text("图片预览与关闭确认", "Image viewer and close confirmation")}><NoteGenDialogBackdrop className="min-h-72"><NoteGenImageViewer lang={lang} /><div className="absolute bottom-4 right-4 z-20 w-64"><NoteGenConfirmationDialog lang={lang} /></div></NoteGenDialogBackdrop></ComponentCard>
244-
<ComponentCard title="Update · Activity" description={text("更新提示与写作活动", "Update prompt and writing activity")}><div className="grid min-h-72 gap-3 rounded-xl border bg-muted/25 p-4 sm:grid-cols-2"><NoteGenUpdatePrompt lang={lang} /><NoteGenActivityHeatmap lang={lang} /></div></ComponentCard>
242+
<ComponentCard desktopWidth={552} title="NoteGenGlobalSearch" description={text("全局搜索面板", "Global search palette")}><NoteGenDialogBackdrop><NoteGenGlobalSearch lang={lang} /></NoteGenDialogBackdrop></ComponentCard>
243+
<ComponentCard desktopWidth={552} title="NoteGenRecordingOverlay" description={text("录音对话框", "Recording dialog")}><NoteGenDialogBackdrop><NoteGenRecordingOverlay lang={lang} /></NoteGenDialogBackdrop></ComponentCard>
244+
<ComponentCard desktopWidth={552} title="NoteGenSyncStatus" description={text("同步完成与冲突状态", "Sync and conflict states")}><div className="flex min-h-64 flex-col items-center justify-center gap-3 rounded-xl border bg-muted/25 p-4"><NoteGenSyncStatus lang={lang} /><NoteGenSyncStatus lang={lang} state="conflict" /></div></ComponentCard>
245+
<ComponentCard desktopWidth={552} title="ImageViewer · Confirmation" description={text("图片预览与关闭确认", "Image viewer and close confirmation")}><NoteGenDialogBackdrop className="min-h-72"><NoteGenImageViewer lang={lang} /><div className="absolute bottom-4 right-4 z-20 w-64"><NoteGenConfirmationDialog lang={lang} /></div></NoteGenDialogBackdrop></ComponentCard>
246+
<ComponentCard desktopWidth={552} title="Update · Activity" description={text("更新提示与写作活动", "Update prompt and writing activity")}><div className="grid min-h-72 grid-cols-2 gap-3 rounded-xl border bg-muted/25 p-4"><NoteGenUpdatePrompt lang={lang} /><NoteGenActivityHeatmap lang={lang} /></div></ComponentCard>
245247
</div>
246248
</GallerySection>
247249

@@ -255,7 +257,7 @@ export function NoteGenComponentGallery({ lang }: { lang: NoteGenReplicaLanguage
255257
)}
256258
>
257259
<div className="grid gap-4 xl:grid-cols-[0.8fr_1.2fr]">
258-
<ComponentCard title={text("独立控件", "Individual controls")} description="Switch · Select · SegmentedControl">
260+
<ComponentCard desktopWidth={448} title={text("独立控件", "Individual controls")} description="Switch · Select · SegmentedControl">
259261
<div className="flex min-h-56 flex-col items-start justify-center gap-6 rounded-lg border bg-background p-6">
260262
<div className="flex w-full items-center justify-between gap-4">
261263
<span className="text-sm text-muted-foreground">NoteGenSettingSwitch</span>
@@ -283,7 +285,7 @@ export function NoteGenComponentGallery({ lang }: { lang: NoteGenReplicaLanguage
283285
</div>
284286
</ComponentCard>
285287

286-
<ComponentCard title="SettingsPage · SettingsSection · SettingRow" description={text("可嵌入文档的完整设置片段", "A complete settings fragment for documentation")}>
288+
<ComponentCard desktopWidth={680} title="SettingsPage · SettingsSection · SettingRow" description={text("可嵌入文档的完整设置片段", "A complete settings fragment for documentation")}>
287289
<div className="h-[420px] overflow-hidden rounded-lg border bg-background text-[11px]">
288290
<NoteGenSettingsPage
289291
icon={Settings}
@@ -325,7 +327,9 @@ export function NoteGenComponentGallery({ lang }: { lang: NoteGenReplicaLanguage
325327
"Search the sidebar, switch sections, and interact with theme and behavior controls. This complete scene can also be embedded directly in documentation."
326328
)}
327329
>
328-
<NoteGenSettingsDialogReplica lang={lang} className="h-[760px]" />
330+
<DesktopScaledPreview baseWidth={1152}>
331+
<NoteGenSettingsDialogReplica lang={lang} className="h-[760px]" />
332+
</DesktopScaledPreview>
329333
</GallerySection>
330334
</div>
331335

@@ -368,18 +372,73 @@ function ComponentCard({
368372
title,
369373
description,
370374
children,
375+
desktopWidth,
371376
}: {
372377
title: string
373378
description: string
374379
children: ReactNode
380+
desktopWidth?: number
375381
}) {
376382
return (
377383
<Card className="overflow-hidden">
378384
<CardHeader>
379385
<CardTitle className="font-mono text-sm">{title}</CardTitle>
380386
<CardDescription>{description}</CardDescription>
381387
</CardHeader>
382-
<CardContent>{children}</CardContent>
388+
<CardContent>
389+
{desktopWidth ? <DesktopScaledPreview baseWidth={desktopWidth}>{children}</DesktopScaledPreview> : children}
390+
</CardContent>
383391
</Card>
384392
)
385393
}
394+
395+
function DesktopScaledPreview({
396+
baseWidth,
397+
children,
398+
}: {
399+
baseWidth: number
400+
children: ReactNode
401+
}) {
402+
const viewportRef = useRef<HTMLDivElement>(null)
403+
const canvasRef = useRef<HTMLDivElement>(null)
404+
const [layout, setLayout] = useState({ scale: 1, height: 0, left: 0 })
405+
406+
useEffect(() => {
407+
const viewport = viewportRef.current
408+
const canvas = canvasRef.current
409+
if (!viewport || !canvas) return
410+
411+
const updateLayout = () => {
412+
const scale = Math.min(1, viewport.clientWidth / baseWidth)
413+
const scaledWidth = baseWidth * scale
414+
setLayout({
415+
scale,
416+
height: canvas.offsetHeight * scale,
417+
left: Math.max(0, (viewport.clientWidth - scaledWidth) / 2),
418+
})
419+
}
420+
421+
updateLayout()
422+
const observer = new ResizeObserver(updateLayout)
423+
observer.observe(viewport)
424+
observer.observe(canvas)
425+
return () => observer.disconnect()
426+
}, [baseWidth])
427+
428+
return (
429+
<div
430+
ref={viewportRef}
431+
className="relative w-full overflow-hidden"
432+
style={layout.height ? { height: layout.height } : undefined}
433+
data-notegen-desktop-preview
434+
>
435+
<div
436+
ref={canvasRef}
437+
className="absolute top-0 origin-top-left"
438+
style={{ width: baseWidth, left: layout.left, transform: `scale(${layout.scale})` }}
439+
>
440+
{children}
441+
</div>
442+
</div>
443+
)
444+
}

0 commit comments

Comments
 (0)