{
  "name": "spreadsheet-resize-dialog",
  "type": "registry:component",
  "dependencies": [
    "@unsanity/spreadsheet"
  ],
  "registryDependencies": [
    "dialog",
    "button",
    "input"
  ],
  "files": [
    {
      "path": "components/spreadsheet/spreadsheet-resize-dialog.tsx",
      "content": "\"use client\";\n\nimport { useId, useLayoutEffect, useState } from \"react\";\nimport {\n  clampSpreadsheetSize,\n  colIndexToLetter,\n  fromActive,\n  measureColumnFitWidth,\n  measureRowFitHeight,\n  RESIZE_DIALOG_DEFAULT_COL_PX,\n  RESIZE_DIALOG_DEFAULT_ROW_PX,\n  useSpreadsheetStore,\n} from \"@unsanity/spreadsheet\";\nimport { Button } from \"@/components/ui/button\";\nimport {\n  Dialog,\n  DialogContent,\n  DialogFooter,\n  DialogHeader,\n  DialogTitle,\n} from \"@/components/ui/dialog\";\nimport { Input } from \"@/components/ui/input\";\n\ntype ResizeAxis = \"col\" | \"row\";\n\nexport type SpreadsheetResizeDialogProps = {\n  axis: ResizeAxis;\n  open: boolean;\n  onOpenChange: (open: boolean) => void;\n  indices: number[];\n};\n\nexport function SpreadsheetResizeDialog({\n  axis,\n  open,\n  onOpenChange,\n  indices,\n}: SpreadsheetResizeDialogProps) {\n  const data = useSpreadsheetStore(fromActive((s) => s.data));\n  const cellStyles = useSpreadsheetStore(fromActive((s) => s.cellStyles));\n  const colDefs = useSpreadsheetStore(fromActive((s) => s.colDefs));\n  const rowHeights = useSpreadsheetStore(fromActive((s) => s.rowHeights));\n  const applyColumnWidths = useSpreadsheetStore((s) => s.applyColumnWidths);\n  const applyRowHeights = useSpreadsheetStore((s) => s.applyRowHeights);\n\n  const [mode, setMode] = useState<\"specify\" | \"fit\">(\"specify\");\n  const [px, setPx] = useState(\"\");\n  const specifyId = useId();\n  const fitId = useId();\n\n  const defaultPx =\n    axis === \"col\" ? RESIZE_DIALOG_DEFAULT_COL_PX : RESIZE_DIALOG_DEFAULT_ROW_PX;\n  const dimensionLabel = axis === \"col\" ? \"column width\" : \"row height\";\n  const radioName = `${axis}-resize-mode`;\n\n  useLayoutEffect(() => {\n    if (!open) return;\n    setMode(\"specify\");\n    const sortedIdx = [...indices].sort((a, b) => a - b);\n    const first = sortedIdx[0];\n    if (axis === \"col\") {\n      const currentW =\n        first !== undefined\n          ? colDefs[first]?.width ?? RESIZE_DIALOG_DEFAULT_COL_PX\n          : RESIZE_DIALOG_DEFAULT_COL_PX;\n      setPx(String(Math.round(currentW)));\n    } else {\n      const currentH =\n        first !== undefined\n          ? rowHeights[first] ?? RESIZE_DIALOG_DEFAULT_ROW_PX\n          : RESIZE_DIALOG_DEFAULT_ROW_PX;\n      setPx(String(Math.round(currentH)));\n    }\n  }, [open, indices, colDefs, rowHeights, axis]);\n\n  const sorted = [...indices].sort((a, b) => a - b);\n  const title =\n    sorted.length === 0\n      ? axis === \"col\"\n        ? \"Resize column\"\n        : \"Resize row\"\n      : sorted.length === 1\n        ? axis === \"col\"\n          ? \"Resize column\"\n          : \"Resize row\"\n        : axis === \"col\"\n          ? `Resize columns ${colIndexToLetter(sorted[0]!)} – ${colIndexToLetter(sorted[sorted.length - 1]!)}`\n          : `Resize rows ${sorted[0]! + 1} – ${sorted[sorted.length - 1]! + 1}`;\n\n  function apply() {\n    if (sorted.length === 0) return;\n    if (axis === \"col\") {\n      if (mode === \"fit\") {\n        const updates = sorted.map((colIndex) => ({\n          colIndex,\n          width: measureColumnFitWidth(data, cellStyles, colIndex),\n        }));\n        applyColumnWidths(updates);\n      } else {\n        const parsed = parseInt(px, 10);\n        const n = clampSpreadsheetSize(\n          Number.isFinite(parsed) ? parsed : RESIZE_DIALOG_DEFAULT_COL_PX\n        );\n        applyColumnWidths(sorted.map((colIndex) => ({ colIndex, width: n })));\n      }\n    } else if (mode === \"fit\") {\n      const updates = sorted.map((rowIndex) => ({\n        rowIndex,\n        height: measureRowFitHeight(data, cellStyles, colDefs, rowIndex),\n      }));\n      applyRowHeights(updates);\n    } else {\n      const parsed = parseInt(px, 10);\n      const n = clampSpreadsheetSize(\n        Number.isFinite(parsed) ? parsed : RESIZE_DIALOG_DEFAULT_ROW_PX\n      );\n      applyRowHeights(sorted.map((rowIndex) => ({ rowIndex, height: n })));\n    }\n    onOpenChange(false);\n  }\n\n  return (\n    <Dialog open={open} onOpenChange={onOpenChange}>\n      <DialogContent showCloseButton className=\"sm:max-w-md\">\n        <DialogHeader>\n          <DialogTitle>{title}</DialogTitle>\n        </DialogHeader>\n        <div className=\"flex flex-col gap-3 py-1\">\n          <label className=\"flex cursor-pointer items-start gap-2 text-sm\">\n            <input\n              id={specifyId}\n              type=\"radio\"\n              name={radioName}\n              className=\"mt-1\"\n              checked={mode === \"specify\"}\n              onChange={() => setMode(\"specify\")}\n            />\n            <span className=\"flex flex-1 flex-col gap-1\">\n              <span>Specify {dimensionLabel} (pixels)</span>\n              <Input\n                type=\"number\"\n                min={2}\n                max={2000}\n                className=\"max-w-[10rem]\"\n                placeholder={String(defaultPx)}\n                value={px}\n                disabled={mode !== \"specify\"}\n                onChange={(e) => setPx(e.target.value)}\n              />\n            </span>\n          </label>\n          <label\n            htmlFor={fitId}\n            className=\"flex cursor-pointer items-center gap-2 text-sm\"\n          >\n            <input\n              id={fitId}\n              type=\"radio\"\n              name={radioName}\n              checked={mode === \"fit\"}\n              onChange={() => setMode(\"fit\")}\n            />\n            Fit to data\n          </label>\n        </div>\n        <DialogFooter>\n          <Button type=\"button\" variant=\"outline\" onClick={() => onOpenChange(false)}>\n            Cancel\n          </Button>\n          <Button type=\"button\" onClick={apply}>\n            OK\n          </Button>\n        </DialogFooter>\n      </DialogContent>\n    </Dialog>\n  );\n}\n\nexport function SpreadsheetColumnResizeDialog(\n  props: Omit<SpreadsheetResizeDialogProps, \"axis\">\n) {\n  return <SpreadsheetResizeDialog {...props} axis=\"col\" />;\n}\n\nexport function SpreadsheetRowResizeDialog(\n  props: Omit<SpreadsheetResizeDialogProps, \"axis\">\n) {\n  return <SpreadsheetResizeDialog {...props} axis=\"row\" />;\n}\n",
      "type": "registry:component"
    }
  ]
}
