150 lines
6.2 KiB
TypeScript
150 lines
6.2 KiB
TypeScript
'use client'
|
|
|
|
import type { ConnectionState, PresenceUser } from '@/lib/collaboration/realtime'
|
|
import PresenceAvatars from './PresenceAvatars'
|
|
|
|
type ToolbarProps = {
|
|
onAddDialogue: () => void
|
|
onAddChoice: () => void
|
|
onAddVariable: () => void
|
|
onSave: () => void
|
|
onExport: () => void
|
|
onExportRenpy: () => void
|
|
onImport: () => void
|
|
onProjectSettings: () => void
|
|
onShare: () => void
|
|
connectionState?: ConnectionState
|
|
presenceUsers?: PresenceUser[]
|
|
}
|
|
|
|
const connectionLabel: Record<ConnectionState, string> = {
|
|
connecting: 'Connecting…',
|
|
connected: 'Connected',
|
|
disconnected: 'Disconnected',
|
|
reconnecting: 'Reconnecting…',
|
|
}
|
|
|
|
const connectionColor: Record<ConnectionState, string> = {
|
|
connecting: 'bg-yellow-400',
|
|
connected: 'bg-green-400',
|
|
disconnected: 'bg-red-400',
|
|
reconnecting: 'bg-yellow-400',
|
|
}
|
|
|
|
export default function Toolbar({
|
|
onAddDialogue,
|
|
onAddChoice,
|
|
onAddVariable,
|
|
onSave,
|
|
onExport,
|
|
onExportRenpy,
|
|
onImport,
|
|
onProjectSettings,
|
|
onShare,
|
|
connectionState,
|
|
presenceUsers,
|
|
}: ToolbarProps) {
|
|
return (
|
|
<div className="flex items-center justify-between border-b border-zinc-200 bg-zinc-50 px-4 py-2 dark:border-zinc-700 dark:bg-zinc-800">
|
|
<div className="flex items-center gap-2">
|
|
<span className="mr-2 text-sm font-medium text-zinc-500 dark:text-zinc-400">
|
|
Add Node:
|
|
</span>
|
|
<button
|
|
onClick={onAddDialogue}
|
|
className="rounded bg-blue-500 px-3 py-1.5 text-sm font-medium text-white hover:bg-blue-600 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 dark:focus:ring-offset-zinc-800"
|
|
>
|
|
Dialogue
|
|
</button>
|
|
<button
|
|
onClick={onAddChoice}
|
|
className="rounded bg-green-500 px-3 py-1.5 text-sm font-medium text-white hover:bg-green-600 focus:outline-none focus:ring-2 focus:ring-green-500 focus:ring-offset-2 dark:focus:ring-offset-zinc-800"
|
|
>
|
|
Choice
|
|
</button>
|
|
<button
|
|
onClick={onAddVariable}
|
|
className="rounded bg-orange-500 px-3 py-1.5 text-sm font-medium text-white hover:bg-orange-600 focus:outline-none focus:ring-2 focus:ring-orange-500 focus:ring-offset-2 dark:focus:ring-offset-zinc-800"
|
|
>
|
|
Variable
|
|
</button>
|
|
</div>
|
|
|
|
<div className="flex items-center gap-2">
|
|
{presenceUsers && presenceUsers.length > 0 && (
|
|
<div className="mr-2">
|
|
<PresenceAvatars users={presenceUsers} />
|
|
</div>
|
|
)}
|
|
{connectionState && (
|
|
<div className="flex items-center gap-1.5 mr-2" title={connectionLabel[connectionState]}>
|
|
<span className={`inline-block h-2.5 w-2.5 rounded-full ${connectionColor[connectionState]}${connectionState === 'reconnecting' ? ' animate-pulse' : ''}`} />
|
|
<span className="text-xs text-zinc-500 dark:text-zinc-400">
|
|
{connectionLabel[connectionState]}
|
|
</span>
|
|
</div>
|
|
)}
|
|
<button
|
|
onClick={onProjectSettings}
|
|
className="rounded border border-zinc-300 bg-white px-3 py-1.5 text-sm font-medium text-zinc-700 hover:bg-zinc-50 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 dark:border-zinc-600 dark:bg-zinc-700 dark:text-zinc-200 dark:hover:bg-zinc-600 dark:focus:ring-offset-zinc-800"
|
|
>
|
|
Project Settings
|
|
</button>
|
|
<button
|
|
onClick={onShare}
|
|
className="rounded border border-zinc-300 bg-white px-3 py-1.5 text-sm font-medium text-zinc-700 hover:bg-zinc-50 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 dark:border-zinc-600 dark:bg-zinc-700 dark:text-zinc-200 dark:hover:bg-zinc-600 dark:focus:ring-offset-zinc-800"
|
|
>
|
|
Share
|
|
</button>
|
|
<button
|
|
onClick={onSave}
|
|
disabled={isSaving}
|
|
className="flex items-center gap-1.5 rounded border border-zinc-300 bg-white px-3 py-1.5 text-sm font-medium text-zinc-700 hover:bg-zinc-50 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 dark:border-zinc-600 dark:bg-zinc-700 dark:text-zinc-200 dark:hover:bg-zinc-600 dark:focus:ring-offset-zinc-800"
|
|
>
|
|
{isSaving && (
|
|
<svg
|
|
className="h-4 w-4 animate-spin"
|
|
xmlns="http://www.w3.org/2000/svg"
|
|
fill="none"
|
|
viewBox="0 0 24 24"
|
|
>
|
|
<circle
|
|
className="opacity-25"
|
|
cx="12"
|
|
cy="12"
|
|
r="10"
|
|
stroke="currentColor"
|
|
strokeWidth="4"
|
|
/>
|
|
<path
|
|
className="opacity-75"
|
|
fill="currentColor"
|
|
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
|
|
/>
|
|
</svg>
|
|
)}
|
|
{isSaving ? 'Saving...' : 'Save'}
|
|
</button>
|
|
<button
|
|
onClick={onExport}
|
|
className="rounded border border-zinc-300 bg-white px-3 py-1.5 text-sm font-medium text-zinc-700 hover:bg-zinc-50 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 dark:border-zinc-600 dark:bg-zinc-700 dark:text-zinc-200 dark:hover:bg-zinc-600 dark:focus:ring-offset-zinc-800"
|
|
>
|
|
Export
|
|
</button>
|
|
<button
|
|
onClick={onExportRenpy}
|
|
className="rounded border border-purple-400 bg-purple-50 px-3 py-1.5 text-sm font-medium text-purple-700 hover:bg-purple-100 focus:outline-none focus:ring-2 focus:ring-purple-500 focus:ring-offset-2 dark:border-purple-500 dark:bg-purple-900/30 dark:text-purple-300 dark:hover:bg-purple-900/50 dark:focus:ring-offset-zinc-800"
|
|
>
|
|
Export to Ren'Py
|
|
</button>
|
|
<button
|
|
onClick={onImport}
|
|
className="rounded border border-zinc-300 bg-white px-3 py-1.5 text-sm font-medium text-zinc-700 hover:bg-zinc-50 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 dark:border-zinc-600 dark:bg-zinc-700 dark:text-zinc-200 dark:hover:bg-zinc-600 dark:focus:ring-offset-zinc-800"
|
|
>
|
|
Import
|
|
</button>
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|