返回 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.isPageBeautifying ||
55 state.isDeckEditing ||
56 state.isStyleSwitchPageLocked ||
57 state.isSavingEdits ||
58 isPreviewSettling
59
60 return (
61 <>
62 <div
63 className={cn(
64 'flex min-w-0 flex-col gap-1 px-3 pb-1.5 pt-1 transition-opacity duration-200',
65 isPreviewSettling && 'pointer-events-none opacity-0'
66 )}
67 >
68 <div className="actions-tool flex min-w-0 items-center gap-2 px-1.5 py-0.5">
69 <PrimaryActions
70 disabled={toolbarDisabled}
71 isSavingEdits={state.isSavingEdits}
72 canUndo={state.canUndo}
73 canRedo={state.canRedo}
74 hasPendingEdits={state.hasPendingEdits}
75 />
76 <WorkspaceTabs
77 activeTab={state.activeTab}
78 disabled={toolbarDisabled}
79 onActivate={activateTab}
80 />
81 </div>
82
83 <DynamicToolRow state={state} disabled={toolbarDisabled} />
84 </div>
85
86 <AlertDialog open={Boolean(pendingTab)} onOpenChange={(open) => !open && cancelPendingTab()}>
87 <AlertDialogContent>
88 <AlertDialogTitle>{t('sessionDetail.workspaceSwitchSaveConfirmTitle')}</AlertDialogTitle>
89 <AlertDialogDescription>
90 {t('sessionDetail.workspaceSwitchSaveConfirmDescription', {
91 mode: pendingTabLabel
92 })}
93 </AlertDialogDescription>
94 <div className="flex justify-end gap-2">
95 <AlertDialogAction
96 disabled={savingBeforeTabSwitch}
97 className="border border-[#d7cbb7]/80 bg-[#fffdf8]/92 text-[#657058] hover:bg-[#f5efe4] disabled:cursor-not-allowed disabled:opacity-60"
98 onClick={(event) => {
99 event.preventDefault()
100 discardPendingTab()
101 }}
102 >
103 {t('common.dontSave')}
104 </AlertDialogAction>
105 <AlertDialogAction
106 disabled={savingBeforeTabSwitch}
107 className="bg-[#5d6b4d] text-white hover:bg-[#4d5a40] disabled:cursor-not-allowed disabled:opacity-60"
108 onClick={(event) => {
109 event.preventDefault()
110 void confirmPendingTab()
111 }}
112 >
113 {savingBeforeTabSwitch ? (
114 <Loader2 className="mr-1.5 h-3.5 w-3.5 animate-spin" />
115 ) : null}
116 {t('sessionDetail.workspaceSwitchSaveConfirmAction')}
117 </AlertDialogAction>
118 <AlertDialogCancel disabled={savingBeforeTabSwitch}>
119 {t('common.cancel')}
120 </AlertDialogCancel>
121 </div>
122 </AlertDialogContent>
123 </AlertDialog>
124 </>
125 )
126 }
127
127 lines Plain Text