import { CheckIcon, ChevronLeftIcon, ChevronRightIcon, ClipboardIcon, CopyIcon, CpuIcon, FileCode2Icon, LocateFixedIcon, ReplaceAllIcon, ScissorsIcon, Trash2Icon, } from 'lucide-react'; import { useState } from 'react'; import { MACHINES } from '../data.ts'; import type { MachineHoop, MachinePreset } from '../data.ts'; import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuSeparator, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger, } from '@/components/ui/dropdown-menu.tsx'; import { cn } from '@/utils.ts'; const MACHINE_CLASSES = ['home', 'multi-needle', 'commercial'] as const; export interface ActiveMachine { id: string; hoopId: string; budgetMode: boolean; } export interface EditorContextActions { cut: () => void; copy: () => void; paste: () => void; goToDefinition: () => void; changeAll: () => void; formatDocument: () => void; } interface Actions { active: ActiveMachine | null; budgetMode: boolean; onApply: (machine: MachinePreset, hoop: MachineHoop) => void; onFabric: (fabric: string) => void; onBudgetModeChange: (enabled: boolean) => void; onRemove: () => void; } function MachineItems({ active, budgetMode, onApply, onFabric, onBudgetModeChange, onRemove, }: Actions) { return ( <> {MACHINE_CLASSES.map((cls) => { const machines = MACHINES.filter((machine) => machine.cls === cls); if (!machines.length) return null; return (
{cls === 'multi-needle' ? 'Multi-needle' : cls}
{machines.map((machine) => ( {active?.id === machine.id && } {machine.brand} {machine.model} {machine.hoops.map((hoop) => ( onApply(machine, hoop)}> {active?.id === machine.id && active.hoopId === hoop.id ? ( ) : ( )} {hoop.label} ))} ))}
); })} Fabric {['woven', 'knit', 'stretch', 'denim', 'fleece'].map((fabric) => ( onFabric(fabric)}> {fabric} ))} onBudgetModeChange(!budgetMode)}> {budgetMode && } Budget mode {active && ( Remove machine block )} ); } export function MachineMenu(props: Actions) { const chip = props.active ? `${MACHINES.find((machine) => machine.id === props.active?.id)?.model ?? props.active.id} ยท ${props.active.hoopId === 'keep' ? 'custom hoop' : props.active.hoopId}` : 'Machine'; return ( {chip} ); } /** A compact context menu that drills down by brand, then machine and hoop. */ export function MachineContextMenu({ x, y, onClose, ...props }: Actions & { x: number; y: number; onClose: () => void; editorActions?: EditorContextActions }) { const [selectedBrand, setSelectedBrand] = useState(null); const [selectedMachineId, setSelectedMachineId] = useState(null); const selectedMachine = MACHINES.find((machine) => machine.id === selectedMachineId) ?? null; const brands = [...new Set(MACHINES.map((machine) => machine.brand))]; const brandedMachines = selectedBrand ? MACHINES.filter((machine) => machine.brand === selectedBrand) : []; return (
event.stopPropagation()} > {props.editorActions && ( <> {( [ ['Cut', ScissorsIcon, props.editorActions.cut], ['Copy', CopyIcon, props.editorActions.copy], ['Paste', ClipboardIcon, props.editorActions.paste], ['Go to Definition', LocateFixedIcon, props.editorActions.goToDefinition], ['Change All Occurrences', ReplaceAllIcon, props.editorActions.changeAll], ['Format Document', FileCode2Icon, props.editorActions.formatDocument], ] as const ).map(([label, Icon, action]) => ( ))}
)}
Machine presets
{selectedMachine ? ( <>
{selectedMachine.brand} {selectedMachine.model}
{selectedMachine.hoops.map((hoop) => ( ))} ) : selectedBrand ? ( <>
{selectedBrand}
{brandedMachines.map((machine) => ( ))} ) : ( brands.map((brand) => { const machines = MACHINES.filter((machine) => machine.brand === brand); const hasActiveMachine = machines.some((machine) => machine.id === props.active?.id); return ( ); }) )}
{['woven', 'knit', 'stretch', 'denim', 'fleece'].map((fabric) => ( ))}
); }