返回 oh-my-ppt
WorkspaceRibbon.tsx
根目录 / src / renderer / src / components / session-detail / workspace / WorkspaceRibbon.tsx
1 import { useEffect, useState } from 'react'
2 import { Loader2 } from 'lucide-react'
3 import { cn } from '@renderer/lib/utils'
4 import { useT } from '@renderer/i18n'
5 import { useWorkspaceRibbonController } from '../hooks/useWorkspaceRibbonController'
6 import { DynamicToolRow } from './toolbar/DynamicToolRow'
7 import { PrimaryActions } from './toolbar/PrimaryActions'
8 import { WorkspaceTabs } from './toolbar/WorkspaceTabs'
9 import {
10 AlertDialog,
11 AlertDialogAction,
12 AlertDialogCancel,
13 AlertDialogContent,
14 AlertDialogDescription,
15 AlertDialogTitle
16 } from '../../ui/AlertDialog'
17
18 export function WorkspaceRibbon({
19 isSavingEdits
20 }: {
21 isSavingEdits: boolean
22 }): React.JSX.Element | null {
23 const t = useT()
24 const [isPreviewSettling, setIsPreviewSettling] = useState(false)
25 const {
26 selectedPageKey,
27 state,
28 activateTab,
29 pendingTab,
30 pendingTabLabel,
31 savingBeforeTabSwitch,
32 cancelPendingTab,
33 discardPendingTab,
34 confirmPendingTab
35 } = useWorkspaceRibbonController(isSavingEdits)
36
37 useEffect(() => {
38 if (!selectedPageKey) {
39 setIsPreviewSettling(false)
40 return
41 }
42 setIsPreviewSettling(true)
43 const timer = window.setTimeout(() => {
44 setIsPreviewSettling(false)
45 }, 500)
46 return () => window.clearTimeout(timer)
47 }, [selectedPageKey])
48
49 if (!selectedPageKey) return null
50
51 const toolbarDisabled =
52 state.isGenerating ||
53 state.isPageEditing ||
54 state.isDeckEditing ||
55 state.isStyleSwitchPageLocked ||
56 state.isSavingEdits ||
57 isPreviewSettling
58
59 return (
60 <>
61 <div
62 className={cn(
63 'flex min-w-0 flex-col gap-1 px-3 pb-1.5 pt-1 transition-opacity duration-200',
64 isPreviewSettling && 'pointer-events-none opacity-0'
65 )}
66 >
67 <div className="actions-tool flex min-w-0 items-center gap-2 px-1.5 py-0.5">
68 <PrimaryActions
69 disabled={toolbarDisabled}
70 isSavingEdits={state.isSavingEdits}
71 canUndo={state.canUndo}
72 canRedo={state.canRedo}
73 hasPendingEdits={state.hasPendingEdits}
74 />
75 <WorkspaceTabs
76 activeTab={state.activeTab}
77 disabled={toolbarDisabled}
78 onActivate={activateTab}
79 />
80 </div>
81
82 <DynamicToolRow state={state} disabled={toolbarDisabled} />
83 </div>
84
85 <AlertDialog open={Boolean(pendingTab)} onOpenChange={(open) => !open && cancelPendingTab()}>
86 <AlertDialogContent>
87 <AlertDialogTitle>{t('sessionDetail.workspaceSwitchSaveConfirmTitle')}</AlertDialogTitle>
88 <AlertDialogDescription>
89 {t('sessionDetail.workspaceSwitchSaveConfirmDescription', {
90 mode: pendingTabLabel
91 })}
92 </AlertDialogDescription>
93 <div className="flex justify-end gap-2">
94 <AlertDialogAction
95 disabled={savingBeforeTabSwitch}
96 className="border border-[#d7cbb7]/80 bg-[#fffdf8]/92 text-[#657058] hover:bg-[#f5efe4] disabled:cursor-not-allowed disabled:opacity-60"
97 onClick={(event) => {
98 event.preventDefault()
99 discardPendingTab()
100 }}
101 >
102 {t('common.dontSave')}
103 </AlertDialogAction>
104 <AlertDialogAction
105 disabled={savingBeforeTabSwitch}
106 className="bg-[#5d6b4d] text-white hover:bg-[#4d5a40] disabled:cursor-not-allowed disabled:opacity-60"
107 onClick={(event) => {
108 event.preventDefault()
109 void confirmPendingTab()
110 }}
111 >
112 {savingBeforeTabSwitch ? (
113 <Loader2 className="mr-1.5 h-3.5 w-3.5 animate-spin" />
114 ) : null}
115 {t('sessionDetail.workspaceSwitchSaveConfirmAction')}
116 </AlertDialogAction>
117 <AlertDialogCancel disabled={savingBeforeTabSwitch}>
118 {t('common.cancel')}
119 </AlertDialogCancel>
120 </div>
121 </AlertDialogContent>
122 </AlertDialog>
123 </>
124 )
125 }
126
126 lines Plain Text