{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "table-cells",
  "title": "Table Cells",
  "description": "Table Cells from the Get IT Done design system.",
  "dependencies": [
    "lucide-react@^0.379.0"
  ],
  "registryDependencies": [
    "https://zero-design.itdone.de/r/button.json",
    "https://zero-design.itdone.de/r/chip.json",
    "https://zero-design.itdone.de/r/input.json",
    "https://zero-design.itdone.de/r/labels-context.json",
    "https://zero-design.itdone.de/r/popover.json",
    "https://zero-design.itdone.de/r/progress.json",
    "https://zero-design.itdone.de/r/recipes.json",
    "https://zero-design.itdone.de/r/select.json",
    "https://zero-design.itdone.de/r/switch.json",
    "https://zero-design.itdone.de/r/table.json",
    "https://zero-design.itdone.de/r/textarea.json",
    "https://zero-design.itdone.de/r/tooltip.json",
    "https://zero-design.itdone.de/r/utils.json",
    "https://zero-design.itdone.de/r/theme.json"
  ],
  "files": [
    {
      "path": "registry/ui/table-cells.tsx",
      "content": "'use client';\n\nimport { Lock, MessageSquareText, Star } from 'lucide-react';\nimport * as React from 'react';\n\nimport { focusRing } from '@/lib/recipes';\nimport { cn } from '@/lib/utils';\nimport { Button, type ButtonProps } from '@/components/ui/button';\nimport { Chip, type ChipProps } from '@/components/ui/chip';\nimport { Input } from '@/components/ui/input';\nimport { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover';\nimport { Progress, type ProgressProps } from '@/components/ui/progress';\nimport { Textarea } from '@/components/ui/textarea';\nimport { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/components/ui/tooltip';\nimport { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select';\nimport { Switch, type SwitchProps } from '@/components/ui/switch';\nimport { markTableCell, TableCell, type TableCellProps } from '@/components/ui/table';\nimport { useLabels } from '@/components/ui/labels-context';\n\n// Dedicated Figma Table cell styles (set 1222:107305): Trend positive|negative, Progress bar, Star ratings, Action icons, Payment method.\n// Each wraps TableCell, so State=Selected/Active/Focused/Error/Disabled come from its props and Hover from the row.\n\nexport interface TableTrendCellProps extends Omit<TableCellProps, 'children' | 'tag'> {\n  // Main figure shown before the chip, e.g. `$1,800`.\n  value: React.ReactNode;\n  // Change shown in the chip, e.g. `20%`.\n  change: React.ReactNode;\n  trend: 'positive' | 'negative';\n}\n\n// Figma Style=Trend positive|negative: figure in subtle text-small, then a dot chip in the success or error tone.\nconst TableTrendCell = React.forwardRef<HTMLTableCellElement, TableTrendCellProps>(\n  ({ value, change, trend, className, ...props }, ref) => {\n    const l = useLabels();\n    return (\n    <TableCell ref={ref} className={cn('px-6', className)} {...props}>\n      <div className=\"flex items-center gap-3\">\n        <span className=\"typography-text-small text-text-subtle [[data-state=selected]_&]:text-text-subtle-on-muted\">{value}</span>\n        <Chip variant={trend === 'positive' ? 'success' : 'destructive'} size=\"small\" dotLeading data-trend={trend}>\n          <span className=\"sr-only\">{trend === 'positive' ? l.trendUp : l.trendDown}</span>\n          {change}\n        </Chip>\n      </div>\n    </TableCell>\n    );\n  },\n);\nTableTrendCell.displayName = 'TableTrendCell';\nmarkTableCell(TableTrendCell);\n\nexport interface TableProgressCellProps extends Omit<TableCellProps, 'children' | 'tag'> {\n  value: number;\n  max?: number;\n  // Accessible name of the progress bar, e.g. the row name.\n  label: string;\n  variant?: ProgressProps['variant'];\n}\n\n// Figma Style=Progress bar: flexible 8px bar with the percentage on the right.\nconst TableProgressCell = React.forwardRef<HTMLTableCellElement, TableProgressCellProps>(\n  ({ value, max, label, variant, className, ...props }, ref) => (\n    <TableCell ref={ref} className={cn('px-6', className)} {...props}>\n      <Progress value={value} max={max} variant={variant} label=\"right\" aria-label={label} />\n    </TableCell>\n  ),\n);\nTableProgressCell.displayName = 'TableProgressCell';\nmarkTableCell(TableProgressCell);\n\nexport interface TableRatingCellProps extends Omit<TableCellProps, 'children' | 'tag'> {\n  // Filled stars, 0 to `max`.\n  value: number;\n  max?: number;\n  // Accessible name prefix, default `Rating`.\n  label?: string;\n}\n\n// Figma Style=Star ratings: five 20px stars, filled up to the value. Read only.\nconst TableRatingCell = React.forwardRef<HTMLTableCellElement, TableRatingCellProps>(\n  ({ value, max = 5, label: labelProp, className, ...props }, ref) => {\n    const l = useLabels();\n    const label = labelProp ?? l.rating;\n    const filled = Math.min(Math.max(Math.round(value), 0), max);\n    return (\n      <TableCell ref={ref} className={className} {...props}>\n        <span role=\"img\" aria-label={l.ratingOf(label, filled, max)} className=\"inline-flex items-center gap-1\">\n          {Array.from({ length: max }).map((_, i) => (\n            <Star\n              key={i}\n              aria-hidden=\"true\"\n              data-filled={i < filled ? '' : undefined}\n              className={cn('h-icon-md w-icon-md shrink-0', i < filled ? 'fill-current text-icon-warning' : 'text-icon-disabled')}\n            />\n          ))}\n        </span>\n      </TableCell>\n    );\n  },\n);\nTableRatingCell.displayName = 'TableRatingCell';\nmarkTableCell(TableRatingCell);\n\nexport interface TableAction {\n  label: string;\n  icon: React.ReactNode;\n  onClick?: () => void;\n  disabled?: boolean;\n}\n\nexport interface TableActionsCellProps extends Omit<TableCellProps, 'children' | 'tag'> {\n  actions: TableAction[];\n}\n\n// Figma Style=Action icons: ghost icon buttons (40px, 20px glyph). Every action needs a label for assistive tech.\nconst TableActionsCell = React.forwardRef<HTMLTableCellElement, TableActionsCellProps>(\n  ({ actions, className, ...props }, ref) => {\n    const l = useLabels();\n    return (\n    <TableCell ref={ref} className={className} {...props}>\n      <div role=\"group\" aria-label={l.rowActions} className={cn('flex items-center gap-1', props.align === 'right' && 'justify-end')}>\n        {actions.map((a) => (\n          <Button\n            key={a.label}\n            type=\"button\"\n            variant=\"ghost\"\n            size=\"no-size\"\n            aria-label={a.label}\n            disabled={a.disabled || props.disabled}\n            onClick={a.onClick}\n            className=\"h-control-md w-control-md shrink-0 rounded-button text-icon-subtle hover:text-icon-default [&>svg]:h-icon-md [&>svg]:w-icon-md\"\n          >\n            {a.icon}\n          </Button>\n        ))}\n      </div>\n    </TableCell>\n  );\n  },\n);\nTableActionsCell.displayName = 'TableActionsCell';\nmarkTableCell(TableActionsCell);\n\nexport interface TablePaymentCellProps extends Omit<TableCellProps, 'children' | 'tag'> {\n  // Card or bank logo slot, e.g. an inline SVG. Decorative: the name identifies the method.\n  icon: React.ReactNode;\n  name: React.ReactNode;\n  supportingText?: React.ReactNode;\n}\n\n// Figma Style=Payment method: 46x32 method icon, then title and supporting line.\nconst TablePaymentCell = React.forwardRef<HTMLTableCellElement, TablePaymentCellProps>(\n  ({ icon, name, supportingText, className, ...props }, ref) => (\n    <TableCell ref={ref} className={cn('px-6', className)} {...props}>\n      <div className=\"flex items-center gap-3\">\n        <span aria-hidden=\"true\" className=\"inline-flex h-control-md w-12 shrink-0 items-center justify-center overflow-hidden rounded-item border border-solid border-border-default bg-background\">\n          {icon}\n        </span>\n        <span className=\"flex flex-col items-start typography-text-small\">\n          <span className=\"text-text-default\">{name}</span>\n          {supportingText && <span className=\"text-text-subtle [[data-state=selected]_&]:text-text-subtle-on-muted\">{supportingText}</span>}\n        </span>\n      </div>\n    </TableCell>\n  ),\n);\nTablePaymentCell.displayName = 'TablePaymentCell';\nmarkTableCell(TablePaymentCell);\n\nexport interface TableLockTextCellProps extends Omit<TableCellProps, 'tag'> {\n  // Text after the lock, e.g. `Required, cannot be turned off`.\n  children: React.ReactNode;\n}\n\n// Figma Style=Lock text: lock glyph (icon-xs, subtle) and a subtle extra-small text, gap space/1.\nconst TableLockTextCell = React.forwardRef<HTMLTableCellElement, TableLockTextCellProps>(({ children, className, ...props }, ref) => (\n  <TableCell ref={ref} className={className} {...props}>\n    <span className=\"inline-flex items-center gap-1\">\n      <Lock aria-hidden=\"true\" data-slot=\"lock\" className=\"h-icon-xs w-icon-xs shrink-0 text-icon-subtle\" />\n      <span className=\"typography-text-extra-small text-text-subtle [[data-state=selected]_&]:text-text-subtle-on-muted\">{children}</span>\n    </span>\n  </TableCell>\n));\nTableLockTextCell.displayName = 'TableLockTextCell';\nmarkTableCell(TableLockTextCell);\n\nexport interface TableToggleCellProps\n  extends Omit<TableCellProps, 'children' | 'tag' | 'onChange'>,\n    Pick<SwitchProps, 'checked' | 'defaultChecked' | 'onCheckedChange' | 'readOnly' | 'name' | 'size'> {\n  // Accessible name of the switch, e.g. `Email notifications for Alex`.\n  label: string;\n}\n\n// Figma Toggle only cell: a switch (controlled via `checked` + `onCheckedChange`, or uncontrolled via `defaultChecked`). A disabled cell disables the switch.\nconst TableToggleCell = React.forwardRef<HTMLTableCellElement, TableToggleCellProps>(\n  ({ label, checked, defaultChecked, onCheckedChange, readOnly, name, size = 'sm', className, ...props }, ref) => (\n    <TableCell ref={ref} className={className} {...props}>\n      <Switch\n        aria-label={label}\n        size={size}\n        name={name}\n        checked={checked}\n        defaultChecked={defaultChecked}\n        onCheckedChange={onCheckedChange}\n        readOnly={readOnly}\n        disabled={props.disabled}\n      />\n    </TableCell>\n  ),\n);\nTableToggleCell.displayName = 'TableToggleCell';\nmarkTableCell(TableToggleCell);\n\nexport interface TableButtonCellProps extends Omit<TableCellProps, 'children' | 'tag' | 'onClick'> {\n  // Visible button text, e.g. `Open`.\n  children: React.ReactNode;\n  onClick?: React.MouseEventHandler<HTMLButtonElement>;\n  // Outline is the Figma default, ghost is the quiet alternative.\n  variant?: Extract<ButtonProps['variant'], 'outline' | 'ghost'>;\n  // Accessible name when the visible text alone is ambiguous across rows, e.g. `Open Nordwind Handel GmbH`.\n  'aria-label'?: string;\n}\n\n// Figma Style=Button: outlined small button. A disabled cell disables the button.\nconst TableButtonCell = React.forwardRef<HTMLTableCellElement, TableButtonCellProps>(\n  ({ children, onClick, variant = 'outline', 'aria-label': ariaLabel, className, ...props }, ref) => (\n    <TableCell ref={ref} className={className} {...props}>\n      <Button type=\"button\" variant={variant} size=\"sm\" aria-label={ariaLabel} disabled={props.disabled} onClick={onClick}>\n        {children}\n      </Button>\n    </TableCell>\n  ),\n);\nTableButtonCell.displayName = 'TableButtonCell';\nmarkTableCell(TableButtonCell);\n\nexport interface TableSelectCellOption {\n  value: string;\n  label: React.ReactNode;\n}\n\nexport interface TableSelectCellProps extends Omit<TableCellProps, 'children' | 'tag' | 'onChange' | 'defaultValue'> {\n  options: TableSelectCellOption[];\n  value?: string;\n  defaultValue?: string;\n  onValueChange?: (value: string) => void;\n  placeholder?: string;\n  // Accessible name of the dropdown, e.g. `Level for Spanish`. Required: the cell has no visible label.\n  label: string;\n  name?: string;\n}\n\n// Figma Style=Select: a compact dropdown in the cell, no label or hint. A disabled cell disables the dropdown. An invalid cell marks it invalid.\nconst TableSelectCell = React.forwardRef<HTMLTableCellElement, TableSelectCellProps>(\n  ({ options, value, defaultValue, onValueChange, placeholder, label, name, className, ...props }, ref) => (\n    <TableCell ref={ref} className={className} {...props}>\n      <Select value={value} defaultValue={defaultValue} onValueChange={onValueChange} name={name} disabled={props.disabled}>\n        <SelectTrigger compact aria-label={label} aria-invalid={props.invalid || undefined}>\n          <SelectValue placeholder={placeholder} />\n        </SelectTrigger>\n        <SelectContent>\n          {options.map((option) => (\n            <SelectItem key={option.value} value={option.value}>\n              {option.label}\n            </SelectItem>\n          ))}\n        </SelectContent>\n      </Select>\n    </TableCell>\n  ),\n);\nTableSelectCell.displayName = 'TableSelectCell';\nmarkTableCell(TableSelectCell);\n\nexport interface TableLeadBadgeCellProps extends Omit<TableCellProps, 'tag'> {\n  // Title text of the row, e.g. a notification name.\n  children: React.ReactNode;\n  // Chip text after the title, e.g. `New` or `Required`.\n  badge?: string;\n  // Chip tone. Blue is `info` (default), the warning tone suits `Required`.\n  badgeVariant?: Extract<ChipProps['variant'], 'info' | 'warning' | 'success' | 'destructive'>;\n  // Subtle line under the title.\n  supportingText?: React.ReactNode;\n}\n\n// Figma Style=Lead text badge: medium title with a small tinted chip, then an optional subtle supporting line.\nconst TableLeadBadgeCell = React.forwardRef<HTMLTableCellElement, TableLeadBadgeCellProps>(\n  ({ children, badge, badgeVariant = 'info', supportingText, className, ...props }, ref) => (\n    <TableCell ref={ref} className={className} {...props}>\n      <div className=\"flex flex-col items-start\">\n        <span className=\"inline-flex items-center gap-2\">\n          <span className=\"typography-ui-medium text-text-default\">{children}</span>\n          {badge ? <Chip size=\"small\" variant={badgeVariant} tinted label={badge} /> : null}\n        </span>\n        {supportingText ? <span className=\"typography-text-extra-small text-text-subtle [[data-state=selected]_&]:text-text-subtle-on-muted\">{supportingText}</span> : null}\n      </div>\n    </TableCell>\n  ),\n);\nTableLeadBadgeCell.displayName = 'TableLeadBadgeCell';\nmarkTableCell(TableLeadBadgeCell);\n\nexport interface TableEditCellProps extends Omit<TableCellProps, 'children' | 'tag' | 'onChange' | 'defaultValue'> {\n  // Committed value shown in display mode.\n  value: string;\n  // Name of the field, used for the accessible names of the display button and the input, e.g. `Name`.\n  label: string;\n  // Called with the draft when Enter or blur saves a changed value.\n  onCommit?: (value: string) => void;\n  // Called when Escape or a no-op leaves edit mode without saving.\n  onCancel?: () => void;\n  // Shown in display mode when the value is empty.\n  placeholder?: string;\n  // Controlled edit mode. Omit it to let the cell manage the mode itself.\n  editing?: boolean;\n  defaultEditing?: boolean;\n  onEditingChange?: (editing: boolean) => void;\n  // Text shown under the input while the draft is invalid. Sets the cell to the error state.\n  errorMessage?: string;\n  // Native input attributes for the editor, e.g. `inputMode`, `maxLength`, `type`.\n  inputProps?: Omit<React.InputHTMLAttributes<HTMLInputElement>, 'value' | 'defaultValue' | 'onChange' | 'aria-label'>;\n  // `click` (default) shows the value as a button that opens an input. `always` renders a typeable input at all times, for entry grids such as time sheets. `editing`, `defaultEditing`, `onEditingChange` and `onCancel` do nothing in `always` mode.\n  mode?: 'click' | 'always';\n  // `always` mode: also commit this many ms after the last keystroke. Omit to commit on blur and Enter only.\n  commitDelay?: number;\n  // `always` mode: where Enter moves focus after committing. Shift+Enter moves the opposite way. `none` keeps focus. Default `down`.\n  nextCellOnEnter?: TableEditCellDirection | 'none';\n  // `always` mode: what Shift+Enter does. `reverse` (default) moves the opposite way, `same` behaves like Enter.\n  shiftEnter?: 'reverse' | 'same';\n  // `always` mode: called after Enter with the direction used and whether a next editable cell took focus. When `moved` is false the cell was the last one, so the consumer can add a row.\n  onNavigate?: (direction: TableEditCellDirection, moved: boolean) => void;\n  // `always` mode: decides per candidate cell whether Enter may land there. Return false to skip it, e.g. a read-only total or a derived column. Cells with a `data-edit-skip` attribute and disabled inputs are always skipped.\n  isCellNavigable?: (cell: HTMLTableCellElement, target: HTMLElement) => boolean;\n  // `always` mode: whether left and right continue in the next or previous row at the row end. `false` stops at the row end and reports `moved: false` to `onNavigate`. Up and down are not affected. Default `true`.\n  wrapRows?: boolean;\n  // `always` mode: small secondary note editor in a popover next to the input.\n  note?: TableEditCellNote;\n}\n\nexport type TableEditCellDirection = 'down' | 'up' | 'right' | 'left';\n\nexport interface TableEditCellNote {\n  // Committed note.\n  value?: string;\n  // Called with the note when the popover field commits (Enter or blur) and the text changed.\n  onCommit: (note: string) => void;\n  // Controlled open state of the editor. Works without a note, e.g. to open it from Enter in the hours field: set it to true from the consumer's key handler. Omit it to let the cell manage the state.\n  open?: boolean;\n  // Initial open state when `open` is not controlled. Default false.\n  defaultOpen?: boolean;\n  // Called when the editor opens or closes, by the user or after a submit.\n  onOpenChange?: (open: boolean) => void;\n  // Where focus starts when the editor opens. `field` is the first field of the render slot (or the note field when there is no slot), `note` is the note field itself. Default `field`.\n  initialFocus?: 'field' | 'note';\n  // Ctrl or Cmd+Enter in the editor (and Enter in a single-line note) commits, closes and moves to the next cell like Enter in the cell does, with the same direction, skipping and row end rules. Shift reverses the direction. When there is no next cell, or `nextCellOnEnter` is `none`, focus returns to the note button. Set `false` for the 0.20 behavior (focus always returns to the note button). Default `true`.\n  submitNavigates?: boolean;\n  // What Escape does with a draft that was not committed yet. `commit` flushes it, so typing that was already visible is kept. `discard` closes and restores the last committed note. Default `commit` with `commitOnChange`, otherwise `discard`.\n  escape?: 'discard' | 'commit';\n  // Tab order of the note button. `-1` takes it out of the Tab sequence; it stays clickable and opens through the controlled `open`, e.g. from Enter in the field.\n  tabIndex?: number;\n  // Placeholder of the note field.\n  placeholder?: string;\n  // Native attributes for the note field, e.g. `maxLength`. With `multiline` they apply to the textarea.\n  inputProps?: Omit<React.InputHTMLAttributes<HTMLInputElement>, 'value' | 'defaultValue' | 'onChange' | 'aria-label'>;\n  // Multi-line note in a textarea. Enter adds a line, Ctrl or Cmd+Enter commits and closes. Default single line (Enter commits and closes).\n  multiline?: boolean;\n  // Also commit while typing, `commitDelay` ms after the last keystroke, not only on close. Escape then only closes: what was already committed stays.\n  commitOnChange?: boolean;\n  // Debounce of `commitOnChange` in ms. Default 400.\n  commitDelay?: number;\n  // Shows the note inside the cell. `line` is a truncated line under the input, `indicator` a dot on the note button with the full text in a tooltip. Both also describe the note button for screen readers. Default `none`.\n  preview?: 'none' | 'line' | 'indicator';\n  // Custom preview content under the input, e.g. a From and To range like `09:00-11:30`. Called with the committed note on every render. Return null, undefined or false to show nothing. It replaces the `line` text, works with every `preview` value and also shows when the note is empty. It describes the note button for screen readers.\n  renderPreview?: (note: string) => React.ReactNode;\n  // Extra fields in the editor above the note field, e.g. From and To time inputs. The consumer owns their state and recalculates the cell value in its own callbacks. Keep the fields labelled; focus starts in the first one.\n  render?: (context: TableEditCellNoteContext) => React.ReactNode;\n}\n\nexport interface TableEditCellNoteContext {\n  // Name of the cell, e.g. for labelling extra fields.\n  label: string;\n  // Current note draft and its setter, for fields that write into the note.\n  draft: string;\n  setDraft: (note: string) => void;\n  // Commits the draft now when it changed.\n  commit: () => void;\n  // Commits and closes the editor. Focus returns to the note button.\n  close: () => void;\n  // Commits, closes and moves to the next cell like Enter in the cell. Falls back to the note button when there is no next cell.\n  submit: () => void;\n}\n\nconst EDIT_TARGET = '[data-table-edit-target]';\n\ninterface EditTargetOptions {\n  isNavigable?: (cell: HTMLTableCellElement, target: HTMLElement) => boolean;\n  wrapRows: boolean;\n}\n\n// Finds the next editable cell input in the table, wrapping to the next or previous row for left and right unless wrapRows is off. Skips cells marked data-edit-skip, disabled inputs and cells the consumer rejects.\nconst findNextEditTarget = (cell: HTMLTableCellElement, direction: TableEditCellDirection, { isNavigable, wrapRows }: EditTargetOptions): HTMLElement | null => {\n  const row = cell.parentElement as HTMLTableRowElement | null;\n  if (!row) return null;\n  const column = cell.cellIndex;\n  const inCell = (target: HTMLTableCellElement | undefined | null) => {\n    if (!target || target.hasAttribute('data-edit-skip')) return null;\n    const found = target.querySelector<HTMLElement>(EDIT_TARGET);\n    if (!found || (found as HTMLInputElement).disabled) return null;\n    if (isNavigable && !isNavigable(target, found)) return null;\n    return found;\n  };\n  if (direction === 'down' || direction === 'up') {\n    let next = (direction === 'down' ? row.nextElementSibling : row.previousElementSibling) as HTMLTableRowElement | null;\n    while (next) {\n      const found = inCell(next.cells?.[column]);\n      if (found) return found;\n      next = (direction === 'down' ? next.nextElementSibling : next.previousElementSibling) as HTMLTableRowElement | null;\n    }\n    return null;\n  }\n  const step = direction === 'right' ? 1 : -1;\n  let current: HTMLTableRowElement | null = row;\n  let index = column + step;\n  while (current) {\n    while (index >= 0 && index < current.cells.length) {\n      const found = inCell(current.cells[index]);\n      if (found) return found;\n      index += step;\n    }\n    if (!wrapRows) return null;\n    current = (step === 1 ? current.nextElementSibling : current.previousElementSibling) as HTMLTableRowElement | null;\n    index = step === 1 ? 0 : (current?.cells.length ?? 0) - 1;\n  }\n  return null;\n};\n\nconst reverseDirection: Record<TableEditCellDirection, TableEditCellDirection> = { down: 'up', up: 'down', right: 'left', left: 'right' };\n\nconst TableEditCellAlways = React.forwardRef<HTMLTableCellElement, TableEditCellProps>(\n  ({ value, label, onCommit, placeholder, errorMessage, inputProps, disabled, invalid, focused, className, commitDelay, nextCellOnEnter = 'down', shiftEnter = 'reverse', onNavigate, isCellNavigable, wrapRows = true, note, mode: _mode, editing: _editing, defaultEditing: _defaultEditing, onEditingChange: _onEditingChange, onCancel: _onCancel, ...props }, ref) => {\n    const l = useLabels();\n    const [draft, setDraft] = React.useState(value);\n    const [noteDraft, setNoteDraft] = React.useState(note?.value ?? '');\n    const [noteOpenState, setNoteOpenState] = React.useState(note?.defaultOpen ?? false);\n    const noteOpenControlled = note?.open !== undefined;\n    const noteOpen = noteOpenControlled ? Boolean(note?.open) : noteOpenState;\n    const [noteTipOpen, setNoteTipOpen] = React.useState(false);\n    const noteDraftRef = React.useRef(note?.value ?? '');\n    const noteSent = React.useRef(note?.value ?? '');\n    const noteTimer = React.useRef<ReturnType<typeof setTimeout>>();\n    const noteCancelled = React.useRef(false);\n    const noteInputRef = React.useRef<HTMLInputElement | HTMLTextAreaElement | null>(null);\n    // Next cell input that takes focus once the popover has closed, instead of the note button.\n    const noteHandoff = React.useRef<HTMLElement | null>(null);\n    const noteSettled = React.useRef(false);\n    const wasNoteOpen = React.useRef(noteOpen);\n    const noteId = React.useId();\n    const cellRef = React.useRef<HTMLTableCellElement | null>(null);\n    const committed = React.useRef(value);\n    const timer = React.useRef<ReturnType<typeof setTimeout>>();\n    const errorId = React.useId();\n\n    // A new committed value from outside replaces the draft.\n    React.useEffect(() => {\n      committed.current = value;\n      setDraft(value);\n    }, [value]);\n\n    React.useEffect(() => {\n      noteSent.current = note?.value ?? '';\n      if (!noteOpen) {\n        noteDraftRef.current = note?.value ?? '';\n        setNoteDraft(note?.value ?? '');\n      }\n    }, [note?.value, noteOpen]);\n\n    React.useEffect(\n      () => () => {\n        clearTimeout(timer.current);\n        clearTimeout(noteTimer.current);\n      },\n      [],\n    );\n\n    const commit = (next: string) => {\n      clearTimeout(timer.current);\n      if (next === committed.current) return;\n      committed.current = next;\n      onCommit?.(next);\n    };\n\n    const hasError = Boolean(invalid || errorMessage);\n    const noteText = note?.value ?? '';\n    const commitNote = () => {\n      clearTimeout(noteTimer.current);\n      const next = noteDraftRef.current;\n      if (next === noteSent.current) return;\n      noteSent.current = next;\n      note?.onCommit(next);\n    };\n    const changeNote = (next: string) => {\n      noteDraftRef.current = next;\n      setNoteDraft(next);\n      clearTimeout(noteTimer.current);\n      if (note?.commitOnChange) noteTimer.current = setTimeout(commitNote, note.commitDelay ?? 400);\n    };\n    const setNoteOpen = (next: boolean) => {\n      if (!noteOpenControlled) setNoteOpenState(next);\n      note?.onOpenChange?.(next);\n    };\n    // Closing keeps the draft (commit) or drops it (discard). Escape follows note.escape, every other way out commits.\n    const settleNote = () => {\n      noteSettled.current = true;\n      if (noteCancelled.current) {\n        clearTimeout(noteTimer.current);\n        noteDraftRef.current = noteSent.current;\n        setNoteDraft(noteSent.current);\n      } else commitNote();\n      noteCancelled.current = false;\n    };\n    const closeNote = () => {\n      commitNote();\n      noteSettled.current = true;\n      setNoteOpen(false);\n    };\n    const escapeMode = note?.escape ?? (note?.commitOnChange ? 'commit' : 'discard');\n    // The editor was closed from outside (controlled open): settle it like a user close, before the draft is reset to the committed note.\n    React.useLayoutEffect(() => {\n      if (wasNoteOpen.current && !noteOpen && !noteSettled.current) settleNote();\n      if (noteOpen) noteSettled.current = false;\n      wasNoteOpen.current = noteOpen;\n    });\n\n    const resolveNext = (shiftKey: boolean) => {\n      if (nextCellOnEnter === 'none') return null;\n      const direction = shiftKey && shiftEnter === 'reverse' ? reverseDirection[nextCellOnEnter] : nextCellOnEnter;\n      const target = cellRef.current ? findNextEditTarget(cellRef.current, direction, { isNavigable: isCellNavigable, wrapRows }) : null;\n      return { direction, target };\n    };\n    const focusTarget = (target: HTMLElement) => {\n      target.focus();\n      if (target instanceof HTMLInputElement) target.select();\n    };\n    // Commits, closes and hands focus to the next cell. Without a next cell focus returns to the note button.\n    const submitNote = (shiftKey: boolean) => {\n      commitNote();\n      noteSettled.current = true;\n      const next = note?.submitNavigates === false ? null : resolveNext(shiftKey);\n      noteHandoff.current = next?.target ?? null;\n      setNoteOpen(false);\n      if (next) onNavigate?.(next.direction, Boolean(next.target));\n    };\n    const preview = note?.preview ?? 'none';\n    const previewId = `${noteId}-preview`;\n    const customPreview = note?.renderPreview?.(noteText);\n    const hasCustomPreview = customPreview != null && customPreview !== false;\n    const describesNote = hasCustomPreview || (Boolean(noteText) && preview !== 'none');\n\n    return (\n      <TableCell\n        ref={(node) => {\n          cellRef.current = node;\n          if (typeof ref === 'function') ref(node);\n          else if (ref) ref.current = node;\n        }}\n        className={className}\n        disabled={disabled}\n        focused={focused}\n        invalid={hasError}\n        data-mode=\"always\"\n        {...props}\n      >\n        <div className=\"flex min-w-0 flex-col gap-1\">\n          <div className=\"flex min-w-0 items-center gap-1\">\n            <Input\n              {...inputProps}\n              compact\n              data-table-edit-target=\"\"\n              value={draft}\n              disabled={disabled}\n              placeholder={placeholder}\n              aria-label={label}\n              aria-invalid={hasError || undefined}\n              aria-describedby={errorMessage ? errorId : undefined}\n              onChange={(event) => {\n                const next = event.target.value;\n                setDraft(next);\n                clearTimeout(timer.current);\n                if (commitDelay != null && commitDelay > 0) timer.current = setTimeout(() => commit(next), commitDelay);\n              }}\n              onFocus={inputProps?.onFocus}\n              onBlur={(event) => {\n                inputProps?.onBlur?.(event);\n                commit(event.target.value);\n              }}\n              onKeyDown={(event) => {\n                inputProps?.onKeyDown?.(event);\n                if (event.defaultPrevented || event.key !== 'Enter' || event.nativeEvent.isComposing) return;\n                event.preventDefault();\n                commit(draft);\n                const next = resolveNext(event.shiftKey);\n                if (!next) return;\n                if (next.target) focusTarget(next.target);\n                onNavigate?.(next.direction, Boolean(next.target));\n              }}\n            />\n            {note ? (\n              <TooltipProvider delayDuration={300}>\n                <Popover\n                  open={noteOpen}\n                  onOpenChange={(next) => {\n                    if (!next) settleNote();\n                    setNoteOpen(next);\n                  }}\n                >\n                  <Tooltip open={preview === 'indicator' && noteText !== '' && !noteOpen && noteTipOpen} onOpenChange={setNoteTipOpen}>\n                    <TooltipTrigger asChild>\n                      <PopoverTrigger asChild>\n                        <button\n                          type=\"button\"\n                          tabIndex={note.tabIndex}\n                          disabled={disabled}\n                          data-has-note={noteText ? '' : undefined}\n                          aria-label={noteText ? l.editNote(label) : l.addNote(label)}\n                          aria-describedby={describesNote ? previewId : undefined}\n                          className={cn(\n                            'relative inline-flex h-control-sm w-control-sm shrink-0 items-center justify-center rounded-control transition-colors',\n                            'hover:bg-muted active:bg-muted-strong disabled:cursor-not-allowed disabled:opacity-disabled',\n                            noteText ? 'text-icon-brand' : 'text-icon-subtle hover:text-icon-default',\n                            focusRing,\n                          )}\n                        >\n                          <MessageSquareText aria-hidden=\"true\" className=\"h-icon-sm w-icon-sm\" />\n                          {preview === 'indicator' && noteText ? <span aria-hidden=\"true\" data-note-indicator=\"\" className=\"absolute right-0 top-0 h-2 w-2 rounded-full border border-solid border-background bg-current\" /> : null}\n                        </button>\n                      </PopoverTrigger>\n                    </TooltipTrigger>\n                    {preview === 'indicator' && noteText ? (\n                      <TooltipContent className=\"max-w-64 whitespace-pre-line break-words\">\n                        {noteText}\n                      </TooltipContent>\n                    ) : null}\n                  </Tooltip>\n                  <PopoverContent\n                    align=\"end\"\n                    aria-label={l.noteField(label)}\n                    className={cn('flex flex-col gap-2 p-3', note.render || note.multiline ? 'w-72' : 'w-64')}\n                    onEscapeKeyDown={() => {\n                      noteCancelled.current = escapeMode === 'discard';\n                    }}\n                    onOpenAutoFocus={(event) => {\n                      if (note.initialFocus !== 'note' || !noteInputRef.current) return;\n                      event.preventDefault();\n                      const field = noteInputRef.current;\n                      field.focus();\n                      field.setSelectionRange?.(field.value.length, field.value.length);\n                    }}\n                    onCloseAutoFocus={(event) => {\n                      const target = noteHandoff.current;\n                      noteHandoff.current = null;\n                      if (!target) return;\n                      event.preventDefault();\n                      focusTarget(target);\n                    }}\n                    onKeyDown={(event) => {\n                      if (event.defaultPrevented || event.nativeEvent.isComposing) return;\n                      if (event.key === 'Enter' && (event.ctrlKey || event.metaKey)) {\n                        event.preventDefault();\n                        submitNote(event.shiftKey);\n                      }\n                    }}\n                  >\n                    {note.render?.({ label, draft: noteDraft, setDraft: changeNote, commit: commitNote, close: closeNote, submit: () => submitNote(false) })}\n                    {note.multiline ? (\n                      <Textarea\n                        {...(note.inputProps as React.TextareaHTMLAttributes<HTMLTextAreaElement>)}\n                        ref={noteInputRef as React.Ref<HTMLTextAreaElement>}\n                        minRows={3}\n                        value={noteDraft}\n                        placeholder={note.placeholder}\n                        aria-label={l.noteField(label)}\n                        onChange={(event) => changeNote(event.target.value)}\n                        onKeyDown={(event) => {\n                          note.inputProps?.onKeyDown?.(event as unknown as React.KeyboardEvent<HTMLInputElement>);\n                        }}\n                      />\n                    ) : (\n                      <Input\n                        {...note.inputProps}\n                        ref={noteInputRef as React.Ref<HTMLInputElement>}\n                        compact\n                        value={noteDraft}\n                        placeholder={note.placeholder}\n                        aria-label={l.noteField(label)}\n                        onChange={(event) => changeNote(event.target.value)}\n                        onKeyDown={(event) => {\n                          note.inputProps?.onKeyDown?.(event);\n                          if (event.defaultPrevented || event.nativeEvent.isComposing) return;\n                          if (event.key === 'Enter' && !event.ctrlKey && !event.metaKey) {\n                            event.preventDefault();\n                            submitNote(event.shiftKey);\n                          }\n                        }}\n                      />\n                    )}\n                  </PopoverContent>\n                </Popover>\n              </TooltipProvider>\n            ) : null}\n          </div>\n          {note && hasCustomPreview ? (\n            <div id={previewId} data-note-preview=\"\" data-note-preview-custom=\"\" className=\"typography-text-extra-small truncate text-text-subtle [[data-state=selected]_&]:text-text-subtle-on-muted\">\n              {customPreview}\n            </div>\n          ) : null}\n          {note && !hasCustomPreview && noteText && preview === 'line' ? (\n            <p id={previewId} data-note-preview=\"\" className=\"typography-text-extra-small truncate text-text-subtle [[data-state=selected]_&]:text-text-subtle-on-muted\">\n              {noteText}\n            </p>\n          ) : null}\n          {note && !hasCustomPreview && noteText && preview === 'indicator' ? (\n            <span id={previewId} className=\"sr-only\">\n              {noteText}\n            </span>\n          ) : null}\n          {errorMessage ? (\n            <p id={errorId} className=\"typography-text-extra-small text-text-destructive\">\n              {errorMessage}\n            </p>\n          ) : null}\n        </div>\n      </TableCell>\n    );\n  },\n);\nTableEditCellAlways.displayName = 'TableEditCellAlways';\n\nconst TableEditCellClick = React.forwardRef<HTMLTableCellElement, TableEditCellProps>(\n  ({ value, label, onCommit, onCancel, placeholder, editing: editingProp, defaultEditing = false, onEditingChange, errorMessage, inputProps, disabled, invalid, focused, className, mode: _mode, commitDelay: _commitDelay, nextCellOnEnter: _nextCellOnEnter, onNavigate: _onNavigate, isCellNavigable: _isCellNavigable, wrapRows: _wrapRows, note: _note, ...props }, ref) => {\n    const l = useLabels();\n    const controlled = editingProp !== undefined;\n    const [innerEditing, setInnerEditing] = React.useState(defaultEditing);\n    const editing = controlled ? editingProp : innerEditing;\n    const [draft, setDraft] = React.useState(value);\n    const inputRef = React.useRef<HTMLInputElement>(null);\n    const buttonRef = React.useRef<HTMLButtonElement>(null);\n    const restoreFocus = React.useRef(false);\n    const errorId = React.useId();\n\n    const setEditing = (next: boolean) => {\n      if (!controlled) setInnerEditing(next);\n      onEditingChange?.(next);\n    };\n\n    React.useEffect(() => {\n      if (editing) {\n        setDraft(value);\n        inputRef.current?.focus();\n        inputRef.current?.select();\n      } else if (restoreFocus.current) {\n        restoreFocus.current = false;\n        buttonRef.current?.focus();\n      }\n      // The draft only resets when edit mode opens or closes.\n      // eslint-disable-next-line react-hooks/exhaustive-deps\n    }, [editing]);\n\n    const finish = (save: boolean, refocus: boolean) => {\n      if (!editing) return;\n      restoreFocus.current = refocus;\n      if (save && draft !== value) onCommit?.(draft);\n      else onCancel?.();\n      setEditing(false);\n    };\n\n    const hasError = Boolean(invalid || errorMessage);\n    return (\n      <TableCell ref={ref} className={className} disabled={disabled} focused={focused || editing} invalid={hasError} data-editing={editing || undefined} {...props}>\n        {editing ? (\n          <div className=\"flex min-w-0 flex-col gap-1\">\n            <Input\n              {...inputProps}\n              ref={inputRef}\n              compact\n              value={draft}\n              aria-label={label}\n              aria-invalid={hasError || undefined}\n              aria-describedby={errorMessage ? errorId : undefined}\n              onChange={(event) => setDraft(event.target.value)}\n              onKeyDown={(event) => {\n                inputProps?.onKeyDown?.(event);\n                if (event.defaultPrevented) return;\n                if (event.key === 'Enter') {\n                  event.preventDefault();\n                  finish(true, true);\n                } else if (event.key === 'Escape') {\n                  event.preventDefault();\n                  event.stopPropagation();\n                  finish(false, true);\n                }\n              }}\n              onBlur={(event) => {\n                inputProps?.onBlur?.(event);\n                finish(true, false);\n              }}\n            />\n            {errorMessage ? (\n              <p id={errorId} className=\"typography-text-extra-small text-text-destructive\">\n                {errorMessage}\n              </p>\n            ) : null}\n          </div>\n        ) : (\n          <button\n            ref={buttonRef}\n            type=\"button\"\n            data-table-edit-target=\"\"\n            disabled={disabled}\n            aria-label={l.editCell(label, value || placeholder || '')}\n            onClick={() => setEditing(true)}\n            className={cn(\n              'block w-full min-w-0 cursor-text truncate rounded-input text-left typography-text-small transition-colors hover:bg-muted disabled:cursor-not-allowed',\n              focusRing,\n              value ? 'text-text-default' : 'text-text-subtle [[data-state=selected]_&]:text-text-subtle-on-muted',\n            )}\n          >\n            {value || placeholder}\n          </button>\n        )}\n      </TableCell>\n    );\n  },\n);\nTableEditCellClick.displayName = 'TableEditCellClick';\n\n// Inline edit cell. Default `click` mode: the value as a button, Enter, Space or a click opens an input; Enter or blur saves, Escape cancels, focus returns to the cell button. `always` mode: a typeable input at all times that commits on blur, Enter or after `commitDelay`, moves focus on Enter and can carry a note editor.\nconst TableEditCell = React.forwardRef<HTMLTableCellElement, TableEditCellProps>((props, ref) =>\n  props.mode === 'always' ? <TableEditCellAlways ref={ref} {...props} /> : <TableEditCellClick ref={ref} {...props} />,\n);\nTableEditCell.displayName = 'TableEditCell';\nmarkTableCell(TableEditCell);\n\nexport {\n  TableActionsCell,\n  TableButtonCell,\n  TableEditCell,\n  TableLeadBadgeCell,\n  TableLockTextCell,\n  TablePaymentCell,\n  TableProgressCell,\n  TableRatingCell,\n  TableSelectCell,\n  TableToggleCell,\n  TableTrendCell,\n};\n",
      "type": "registry:ui"
    }
  ],
  "type": "registry:ui"
}