import { motion } from 'framer-motion'
import * as React from 'react'
import { useRef, useState } from 'react'
import { Checkbox } from '../ui/checkbox'
import { Input } from '../ui/input'
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '../ui/select'
import { Textarea } from '../ui/textarea'
import type { EditInputType } from '../types'
export type EditNavigation = 'next' | 'prev'
export type CellEditorProps = {
inputType: EditInputType
selectOptions?: { label: string; value: string }[]
initialValue: string
disabled?: boolean
/** Commit the edit; `nav` requests focus move to the adjacent editable cell. */
onCommit: (value: string | number | boolean, nav?: EditNavigation) => void
onCancel: () => void
}
function parseNumber(raw: string): string | number {
const trimmed = raw.trim()
if (trimmed === '') return ''
const n = Number(trimmed)
return Number.isFinite(n) ? n : raw
}
/**
* Renders the editor matching `meta.inputType` (spec §7.3) with the §7.4
* commit/cancel/navigation key handling.
*/
export function CellEditor({
inputType,
selectOptions,
initialValue,
disabled,
onCommit,
onCancel,
}: CellEditorProps) {
const [value, setValue] = useState(initialValue)
// Set once an explicit commit/cancel ran, so the trailing blur is ignored.
const doneRef = useRef(false)
const commit = (v: string | number | boolean, nav?: EditNavigation) => {
if (doneRef.current) return
doneRef.current = true
onCommit(v, nav)
}
const cancel = () => {
if (doneRef.current) return
doneRef.current = true
onCancel()
}
const handleNavKeys = (e: React.KeyboardEvent, current: string | number | boolean) => {
if (e.key === 'Escape') {
e.preventDefault()
e.stopPropagation()
cancel()
return true
}
if (e.key === 'Tab') {
e.preventDefault()
e.stopPropagation()
commit(current, e.shiftKey ? 'prev' : 'next')
return true
}
return false
}
const wrap = (children: React.ReactNode) => (
e.stopPropagation()}
onDoubleClick={(e) => e.stopPropagation()}
>
{children}
)
if (inputType === 'boolean') {
const checked = value === 'true'
return wrap(
,
)
}
if (inputType === 'select') {
return wrap(
,
)
}
if (inputType === 'number') {
return wrap(
setValue(e.target.value)}
onKeyDown={(e) => {
if (handleNavKeys(e, parseNumber(value))) return
if (e.key === 'Enter') {
e.preventDefault()
commit(parseNumber(value))
}
}}
onBlur={() => commit(parseNumber(value))}
onFocus={(e) => e.target.select()}
aria-label="Edit number value"
/>,
)
}
// text — auto-expanding textarea; Enter commits, Shift+Enter inserts a newline.
return wrap(