Files
lms-web-fe/src/lms-renderer/features/workspace/ModuleListToolbar.tsx
T
2026-08-03 14:06:06 +09:00

124 lines
3.9 KiB
TypeScript

import { Download, MapPinned, Play, RefreshCw, Square } from 'lucide-react'
import type React from 'react'
export type ModuleFilter = 'all' | 'running' | 'stopped' | 'uninstalled'
type ModuleListToolbarProps = {
dark: boolean
filter: ModuleFilter
loading: boolean
installingModuleName: string | null
uninstalledCount: number
runningCount: number
stoppedCount: number
onFilterChange: (filter: ModuleFilter) => void
onInstallAll: () => void
onStartAll: () => void
onStopAll: () => void
onOpenWorldEditor: () => void
}
const FILTER_TABS: Array<{ id: ModuleFilter; label: string }> = [
{ id: 'all', label: '전체' },
{ id: 'running', label: '실행중' },
{ id: 'stopped', label: '정지됨' },
{ id: 'uninstalled', label: '미설치' }
]
export default function ModuleListToolbar({
dark,
filter,
loading,
installingModuleName,
uninstalledCount,
runningCount,
stoppedCount,
onFilterChange,
onInstallAll,
onStartAll,
onStopAll,
onOpenWorldEditor
}: ModuleListToolbarProps): React.JSX.Element {
const installingAll = installingModuleName === '__all__'
const startingAll = installingModuleName === '__start_all__'
const stoppingAll = installingModuleName === '__stop_all__'
const bulkActions = [
{ label: '맵설정', Icon: MapPinned, busy: false, disabled: false, onClick: onOpenWorldEditor },
{
label: '전체 설치',
Icon: Download,
busy: installingAll,
disabled: loading || installingModuleName !== null || uninstalledCount === 0,
onClick: onInstallAll
},
{
label: '전체 실행',
Icon: Play,
busy: startingAll,
disabled: loading || installingModuleName !== null || stoppedCount === 0,
onClick: onStartAll
},
{
label: '전체 중지',
Icon: Square,
busy: stoppingAll,
disabled: loading || installingModuleName !== null || runningCount === 0,
onClick: onStopAll
}
]
return (
<div className={['flex h-10 items-end justify-between px-3', dark ? 'bg-[#252525]' : 'bg-[#f7f8fb]'].join(' ')}>
<div className="flex items-end">
{FILTER_TABS.map(({ id, label }) => {
const active = filter === id
return (
<button
key={id}
type="button"
onClick={() => onFilterChange(id)}
className={[
'h-8 min-w-[76px] cursor-pointer border-x border-t px-3 text-[12px] font-bold transition first:rounded-tl-md last:rounded-tr-md',
active
? dark
? 'border-white/10 bg-[#2b2b2b] text-white'
: 'border-[#e1e6ef] bg-white text-[#111520]'
: dark
? 'border-white/8 bg-[#202020] text-white/45 hover:text-white/70'
: 'border-[#e1e6ef] bg-[#eef1f6] text-[#687284] hover:text-[#111520]'
].join(' ')}
>
{label}
</button>
)
})}
</div>
<div className="flex items-center gap-2 pb-2">
{bulkActions.map(({ label, Icon, busy, disabled, onClick }) => (
<button
key={label}
type="button"
disabled={disabled}
onClick={() => {
if (!onClick) return
onClick()
}}
className={[
'flex h-7 items-center gap-1.5 rounded-md border px-3 text-[12px] font-bold transition',
disabled ? 'cursor-default opacity-45' : 'cursor-pointer',
dark
? 'border-white/15 bg-white/[0.04] text-white/70 hover:border-white/25 hover:bg-white/10 hover:text-white'
: 'border-[#cfd6e4] bg-white text-[#4f596b] hover:bg-[#eef1f7] hover:text-[#111520]'
].join(' ')}
>
{busy ? <RefreshCw size={14} className="animate-spin" /> : <Icon size={14} />}
{label}
</button>
))}
</div>
</div>
)
}