{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "e-signature",
  "title": "E-Signature",
  "description": "A PDF signature review block with field overlays and signing actions.",
  "dependencies": [
    "pdf-lib@^1.17.1",
    "signature_pad@^5.1.3",
    "@base-ui/react@^1.4.1"
  ],
  "registryDependencies": [
    "@extend/pdf-block-resizable-shell",
    "@extend/pdf-viewer",
    "utils",
    "button",
    "dialog",
    "scroll-area"
  ],
  "files": [
    {
      "path": "registry/new-york-v4/blocks/e-signature.tsx",
      "content": "\"use client\"\n\nimport * as React from \"react\"\nimport { ScrollArea as ScrollAreaPrimitive } from \"@base-ui/react/scroll-area\"\nimport type SignaturePad from \"signature_pad\"\n\nimport { cn } from \"@/lib/utils\"\nimport { Button } from \"@/components/ui/button\"\nimport {\n  Dialog,\n  DialogContent,\n  DialogDescription,\n  DialogFooter,\n  DialogHeader,\n  DialogTitle,\n} from \"@/components/ui/dialog\"\nimport {\n  ScrollArea as InlineScrollArea,\n  ScrollBar,\n} from \"@/components/ui/scroll-area\"\nimport { PdfBlockResizableShell } from \"@/components/extend/pdf-block-resizable-shell\"\nimport { PDFViewer } from \"@/components/extend/pdf-viewer\"\nimport { IconPlaceholder } from \"@/components/icon-placeholder\"\n\ntype BoundingBox = {\n  x: number\n  y: number\n  width: number\n  height: number\n}\ntype SignatureField = {\n  id: string\n  label: string\n  page: number\n  bbox: BoundingBox\n  imageDataUrl?: string\n}\nconst PAGE_WIDTH = 612\nconst PAGE_HEIGHT = 792\nconst DEFAULT_ZOOM = 1\nconst SIGNATURE_PAD_PADDING = 8\nconst SIGNATURE_PAD_BACKGROUND_COLOR = \"#ffffff\"\nconst SIGNATURE_PAD_PEN_COLOR = \"#000000\"\nconst DEFAULT_SIGNATURE_ASPECT_RATIO = 3\nconst INITIAL_FIELD: SignatureField = {\n  id: \"signature-1\",\n  label: \"Signature\",\n  page: 1,\n  bbox: { x: 300, y: 504, width: 250, height: 58 },\n}\nfunction bboxToStyle(bbox: BoundingBox): React.CSSProperties {\n  return {\n    left: `${(bbox.x / PAGE_WIDTH) * 100}%`,\n    top: `${(bbox.y / PAGE_HEIGHT) * 100}%`,\n    width: `${(bbox.width / PAGE_WIDTH) * 100}%`,\n    height: `${(bbox.height / PAGE_HEIGHT) * 100}%`,\n  }\n}\nfunction getSignatureAspectRatio(bbox?: BoundingBox): number {\n  if (!bbox || bbox.width <= 0 || bbox.height <= 0) {\n    return DEFAULT_SIGNATURE_ASPECT_RATIO\n  }\n  return bbox.width / bbox.height\n}\nfunction getSignatureGuideSize({\n  containerWidth,\n  containerHeight,\n  aspectRatio,\n}: {\n  containerWidth: number\n  containerHeight: number\n  aspectRatio: number\n}): {\n  width: number\n  height: number\n} {\n  const maxWidth = Math.max(containerWidth - SIGNATURE_PAD_PADDING * 2, 1)\n  const maxHeight = Math.max(containerHeight - SIGNATURE_PAD_PADDING * 2, 1)\n  if (maxWidth / maxHeight > aspectRatio) {\n    const height = maxHeight\n    return {\n      width: height * aspectRatio,\n      height,\n    }\n  }\n  const width = maxWidth\n  return {\n    width,\n    height: width / aspectRatio,\n  }\n}\nfunction getSignatureDataUrl(canvas: HTMLCanvasElement): string {\n  return canvas.toDataURL(\"image/png\")\n}\nfunction SignatureDialog({\n  open,\n  fieldBbox,\n  initialValue,\n  onOpenChange,\n  onConfirm,\n}: {\n  open: boolean\n  fieldBbox: BoundingBox\n  initialValue?: string\n  onOpenChange: (open: boolean) => void\n  onConfirm: (value: string) => void\n}) {\n  const canvasContainerRef = React.useRef<HTMLDivElement>(null)\n  const canvasRef = React.useRef<HTMLCanvasElement>(null)\n  const signaturePadRef = React.useRef<SignaturePad | null>(null)\n  const [isReady, setIsReady] = React.useState(false)\n  const [hasSignature, setHasSignature] = React.useState(false)\n  const [guideSize, setGuideSize] = React.useState<{\n    width: number\n    height: number\n  } | null>(null)\n  const signatureAspectRatio = React.useMemo(\n    () => getSignatureAspectRatio(fieldBbox),\n    [fieldBbox]\n  )\n  const [previousOpen, setPreviousOpen] = React.useState(open)\n  if (previousOpen !== open) {\n    setPreviousOpen(open)\n    if (!open) {\n      setGuideSize(null)\n      setIsReady(false)\n      setHasSignature(false)\n    }\n  }\n  const canInitialize = Boolean(\n    open && guideSize && guideSize.width > 1 && guideSize.height > 1\n  )\n  if (!canInitialize && isReady) setIsReady(false)\n  React.useEffect(() => {\n    if (!open) return\n    let frameId = 0\n    let resizeObserver: ResizeObserver | null = null\n    const updateGuideSize = (container?: HTMLDivElement | null) => {\n      const currentContainer = container ?? canvasContainerRef.current\n      if (\n        !currentContainer ||\n        currentContainer.clientWidth <= 0 ||\n        currentContainer.clientHeight <= 0\n      ) {\n        return false\n      }\n      const nextSize = getSignatureGuideSize({\n        containerWidth: currentContainer.clientWidth,\n        containerHeight: currentContainer.clientHeight,\n        aspectRatio: signatureAspectRatio,\n      })\n      setGuideSize((previousSize) => {\n        if (\n          previousSize &&\n          Math.abs(previousSize.width - nextSize.width) < 0.5 &&\n          Math.abs(previousSize.height - nextSize.height) < 0.5\n        ) {\n          return previousSize\n        }\n        return nextSize\n      })\n      return true\n    }\n    const connect = () => {\n      const container = canvasContainerRef.current\n      if (!updateGuideSize(container)) {\n        frameId = window.requestAnimationFrame(connect)\n        return\n      }\n      if (container) {\n        resizeObserver = new ResizeObserver(() => {\n          updateGuideSize(container)\n        })\n        resizeObserver.observe(container)\n      }\n    }\n    connect()\n    return () => {\n      window.cancelAnimationFrame(frameId)\n      resizeObserver?.disconnect()\n    }\n  }, [open, signatureAspectRatio])\n  React.useEffect(() => {\n    if (!open || !guideSize || guideSize.width <= 1 || guideSize.height <= 1) {\n      signaturePadRef.current?.off()\n      signaturePadRef.current = null\n      return\n    }\n    let cancelled = false\n    let resizeObserver: ResizeObserver | null = null\n    const syncCanvasSize = (canvas: HTMLCanvasElement) => {\n      const width = Math.max(canvas.offsetWidth, 1)\n      const height = Math.max(canvas.offsetHeight, 1)\n      const ratio = Math.max(window.devicePixelRatio || 1, 1)\n      canvas.style.width = `${width}px`\n      canvas.style.height = `${height}px`\n      canvas.width = Math.floor(width * ratio)\n      canvas.height = Math.floor(height * ratio)\n      const context = canvas.getContext(\"2d\")\n      context?.setTransform(1, 0, 0, 1, 0, 0)\n      context?.scale(ratio, ratio)\n      return { width, height, ratio }\n    }\n    const initialize = async () => {\n      const canvas = canvasRef.current\n      if (!canvas) return\n      const { default: SignaturePadConstructor } = await import(\"signature_pad\")\n      if (cancelled) return\n      const signaturePad = new SignaturePadConstructor(canvas, {\n        minWidth: 1,\n        maxWidth: 2,\n        penColor: SIGNATURE_PAD_PEN_COLOR,\n      })\n      signaturePadRef.current = signaturePad\n      const loadSignature = async (dataUrl?: string) => {\n        const size = syncCanvasSize(canvas)\n        if (dataUrl) {\n          await signaturePad.fromDataURL(dataUrl, size)\n          setHasSignature(true)\n          return\n        }\n        signaturePad.clear()\n        setHasSignature(false)\n      }\n      await loadSignature(initialValue)\n      if (cancelled) return\n      signaturePad.addEventListener(\"endStroke\", () => {\n        setHasSignature(true)\n      })\n      resizeObserver = new ResizeObserver(() => {\n        const currentCanvas = canvasRef.current\n        const currentSignaturePad = signaturePadRef.current\n        if (!currentCanvas || !currentSignaturePad) return\n        const previousSignature = currentSignaturePad.isEmpty()\n          ? undefined\n          : currentSignaturePad.toDataURL(\"image/png\")\n        void loadSignature(previousSignature)\n      })\n      resizeObserver.observe(canvas)\n      setIsReady(true)\n    }\n    const animationFrame = window.requestAnimationFrame(() => {\n      void initialize()\n    })\n    return () => {\n      cancelled = true\n      window.cancelAnimationFrame(animationFrame)\n      resizeObserver?.disconnect()\n      signaturePadRef.current?.off()\n      signaturePadRef.current = null\n      setIsReady(false)\n    }\n  }, [open, guideSize, initialValue])\n  return (\n    <Dialog open={open} onOpenChange={onOpenChange}>\n      <DialogContent className=\"max-w-xl\">\n        <DialogHeader>\n          <DialogTitle>Add signature</DialogTitle>\n          <DialogDescription>\n            Draw a signature to place it into the selected PDF field.\n          </DialogDescription>\n        </DialogHeader>\n        <InlineDialogPanel>\n          <div className=\"rounded-xl border bg-white p-3 text-slate-950 shadow-xs dark:bg-white dark:text-slate-950\">\n            <div\n              ref={canvasContainerRef}\n              className=\"flex h-56 w-full items-center justify-center overflow-hidden rounded-lg bg-white p-2 dark:bg-white\"\n            >\n              <div\n                className={cn(\n                  \"relative overflow-hidden rounded-[3px] border border-dashed border-blue-500/70 bg-white\",\n                  isReady ? \"cursor-crosshair\" : \"cursor-wait\"\n                )}\n                style={{\n                  width: guideSize ? `${guideSize.width}px` : undefined,\n                  height: guideSize ? `${guideSize.height}px` : undefined,\n                  opacity: guideSize ? 1 : 0,\n                  backgroundColor: SIGNATURE_PAD_BACKGROUND_COLOR,\n                }}\n              >\n                <canvas\n                  ref={canvasRef}\n                  className={cn(\n                    \"absolute inset-0 size-full touch-none\",\n                    !isReady && \"pointer-events-none\"\n                  )}\n                  style={{\n                    backgroundColor: SIGNATURE_PAD_BACKGROUND_COLOR,\n                    touchAction: \"none\",\n                  }}\n                />\n              </div>\n            </div>\n          </div>\n        </InlineDialogPanel>\n        <DialogFooter className=\"sm:justify-between\">\n          <Button\n            type=\"button\"\n            variant=\"outline\"\n            disabled={!isReady}\n            onClick={() => {\n              signaturePadRef.current?.clear()\n              setHasSignature(false)\n            }}\n          >\n            Clear\n          </Button>\n          <div className=\"flex flex-col-reverse gap-2 sm:flex-row\">\n            <Button\n              type=\"button\"\n              variant=\"outline\"\n              onClick={() => onOpenChange(false)}\n            >\n              Cancel\n            </Button>\n            <Button\n              type=\"button\"\n              disabled={!isReady || !hasSignature}\n              onClick={() => {\n                const canvas = canvasRef.current\n                if (!canvas) return\n                onConfirm(getSignatureDataUrl(canvas))\n                onOpenChange(false)\n              }}\n            >\n              Confirm\n            </Button>\n          </div>\n        </DialogFooter>\n      </DialogContent>\n    </Dialog>\n  )\n}\nfunction SignatureFieldOverlay({\n  field,\n  onOpen,\n}: {\n  field: SignatureField\n  onOpen: () => void\n}) {\n  return (\n    <button\n      type=\"button\"\n      className={cn(\n        \"absolute z-20 overflow-hidden rounded-[3px] border border-blue-500/70 transition-colors focus-visible:ring-2 focus-visible:ring-ring focus-visible:outline-none\",\n        field.imageDataUrl\n          ? \"bg-transparent shadow-none hover:bg-blue-500/5\"\n          : \"bg-blue-500/10 hover:bg-blue-500/15\"\n      )}\n      style={bboxToStyle(field.bbox)}\n      onClick={onOpen}\n    >\n      {field.imageDataUrl ? (\n        <img\n          src={field.imageDataUrl}\n          alt=\"\"\n          className=\"size-full object-fill\"\n          draggable={false}\n        />\n      ) : (\n        <span className=\"flex size-full items-center justify-center gap-1.5 px-2 text-[11px] font-medium text-blue-700 dark:text-blue-300\">\n          <IconPlaceholder\n            lucide=\"Pen\"\n            tabler=\"IconPencil\"\n            hugeicons=\"Pen01Icon\"\n            phosphor=\"PenIcon\"\n            remixicon=\"RiPencilLine\"\n            className=\"size-3.5\"\n          />\n          Signature\n        </span>\n      )}\n    </button>\n  )\n}\nasync function downloadSignedPdf({\n  file,\n  field,\n}: {\n  file: string\n  field: SignatureField\n}) {\n  const { PDFDocument } = await import(\"pdf-lib\")\n  const existingPdfBytes = await fetch(file).then((response) =>\n    response.arrayBuffer()\n  )\n  const pdfDocument = await PDFDocument.load(existingPdfBytes)\n  const page = pdfDocument.getPage(field.page - 1)\n  if (field.imageDataUrl) {\n    const signatureImage = await pdfDocument.embedPng(field.imageDataUrl)\n    const { width: pageWidth, height: pageHeight } = page.getSize()\n    const scaleX = pageWidth / PAGE_WIDTH\n    const scaleY = pageHeight / PAGE_HEIGHT\n    const fieldWidth = field.bbox.width * scaleX\n    const fieldHeight = field.bbox.height * scaleY\n    const fieldX = field.bbox.x * scaleX\n    const fieldY = pageHeight - (field.bbox.y + field.bbox.height) * scaleY\n    page.drawImage(signatureImage, {\n      x: fieldX,\n      y: fieldY,\n      width: fieldWidth,\n      height: fieldHeight,\n    })\n  }\n  const bytes = await pdfDocument.save()\n  const blob = new Blob([new Uint8Array(bytes)], { type: \"application/pdf\" })\n  const url = URL.createObjectURL(blob)\n  const anchor = document.createElement(\"a\")\n  anchor.href = url\n  anchor.download = \"signed-document.pdf\"\n  anchor.click()\n  URL.revokeObjectURL(url)\n}\nfunction SignatureFieldsPanel({\n  field,\n  className,\n  canExport,\n  isDownloading,\n  onSign,\n  onClear,\n  onDownload,\n}: {\n  field: SignatureField\n  className?: string\n  canExport: boolean\n  isDownloading: boolean\n  onSign: () => void\n  onClear: () => void\n  onDownload: () => void\n}) {\n  return (\n    <aside className={cn(\"flex min-h-0 flex-col bg-background\", className)}>\n      <InlineScrollArea2 className=\"min-h-0 flex-1\" scrollFade>\n        <div className=\"space-y-4 p-4\">\n          <div className=\"space-y-1\">\n            <h3 className=\"text-sm font-medium\">Signature fields</h3>\n            <p className=\"text-xs text-muted-foreground\">\n              Review fields, collect signatures, and export a signed PDF.\n            </p>\n          </div>\n          <div className=\"rounded-lg border bg-background p-3\">\n            <div className=\"flex items-start gap-3\">\n              <div className=\"grid size-9 shrink-0 place-items-center rounded-md bg-blue-500/10 text-blue-600 dark:text-blue-300\">\n                <IconPlaceholder\n                  lucide=\"FilePen\"\n                  tabler=\"IconFilePencil\"\n                  hugeicons=\"FilePenIcon\"\n                  phosphor=\"NotePencilIcon\"\n                  remixicon=\"RiFileEditLine\"\n                  className=\"size-4\"\n                />\n              </div>\n              <div className=\"min-w-0 flex-1\">\n                <div className=\"flex items-center justify-between gap-2\">\n                  <div className=\"text-sm font-medium\">{field.label}</div>\n                  <div\n                    className={cn(\n                      \"rounded-full px-2 py-0.5 text-xs\",\n                      field.imageDataUrl\n                        ? \"bg-emerald-500/10 text-emerald-600 dark:text-emerald-300\"\n                        : \"bg-muted text-muted-foreground\"\n                    )}\n                  >\n                    {field.imageDataUrl ? \"Signed\" : \"Unsigned\"}\n                  </div>\n                </div>\n                <div className=\"mt-2 text-xs text-muted-foreground\">\n                  {Math.round(field.bbox.width)} x{\" \"}\n                  {Math.round(field.bbox.height)} on page {field.page}\n                </div>\n                <div className=\"mt-3 flex gap-2\">\n                  <Button\n                    type=\"button\"\n                    size=\"sm\"\n                    variant={field.imageDataUrl ? \"outline\" : \"default\"}\n                    className=\"flex-1\"\n                    onClick={onSign}\n                  >\n                    <IconPlaceholder\n                      lucide=\"Pen\"\n                      tabler=\"IconPencil\"\n                      hugeicons=\"Pen01Icon\"\n                      phosphor=\"PenIcon\"\n                      remixicon=\"RiPencilLine\"\n                      className=\"size-4\"\n                    />\n                    {field.imageDataUrl ? \"Edit\" : \"Sign\"}\n                  </Button>\n                  {field.imageDataUrl ? (\n                    <Button\n                      type=\"button\"\n                      size=\"sm\"\n                      variant=\"outline\"\n                      onClick={onClear}\n                    >\n                      Clear\n                    </Button>\n                  ) : null}\n                </div>\n              </div>\n            </div>\n          </div>\n          <Button\n            type=\"button\"\n            className=\"w-full\"\n            disabled={!canExport || isDownloading}\n            onClick={onDownload}\n          >\n            <IconPlaceholder\n              lucide=\"Download\"\n              tabler=\"IconDownload\"\n              hugeicons=\"Download01Icon\"\n              phosphor=\"DownloadSimpleIcon\"\n              remixicon=\"RiDownload2Line\"\n              className=\"size-4\"\n            />\n            {isDownloading ? \"Exporting...\" : \"Export signed PDF\"}\n          </Button>\n        </div>\n      </InlineScrollArea2>\n    </aside>\n  )\n}\nexport function ESignatureBlock({ file }: { file?: string }) {\n  const [field, setField] = React.useState<SignatureField>(INITIAL_FIELD)\n  const [dialogOpen, setDialogOpen] = React.useState(false)\n  const [isDownloading, setIsDownloading] = React.useState(false)\n  const handleDownload = React.useCallback(async () => {\n    if (!file || !field.imageDataUrl) return\n    setIsDownloading(true)\n    try {\n      await downloadSignedPdf({ file, field })\n    } finally {\n      setIsDownloading(false)\n    }\n  }, [field, file])\n  return (\n    <>\n      <PdfBlockResizableShell\n        autoSaveId=\"pdf-block-e-signature\"\n        left={\n          <PDFViewer\n            src={file}\n            defaultZoom={DEFAULT_ZOOM}\n            toolbarActions={\n              <Button\n                type=\"button\"\n                size=\"sm\"\n                variant=\"outline\"\n                disabled={!file || !field.imageDataUrl || isDownloading}\n                onClick={handleDownload}\n              >\n                <IconPlaceholder\n                  lucide=\"Download\"\n                  tabler=\"IconDownload\"\n                  hugeicons=\"Download01Icon\"\n                  phosphor=\"DownloadSimpleIcon\"\n                  remixicon=\"RiDownload2Line\"\n                  className=\"size-4\"\n                />\n                Download\n              </Button>\n            }\n            renderPageOverlay={({ pageNumber }) =>\n              pageNumber === field.page ? (\n                <SignatureFieldOverlay\n                  field={field}\n                  onOpen={() => setDialogOpen(true)}\n                />\n              ) : null\n            }\n          />\n        }\n        right={\n          <SignatureFieldsPanel\n            field={field}\n            canExport={Boolean(file && field.imageDataUrl)}\n            isDownloading={isDownloading}\n            onSign={() => setDialogOpen(true)}\n            onClear={() =>\n              setField((previousField) => ({\n                ...previousField,\n                imageDataUrl: undefined,\n              }))\n            }\n            onDownload={handleDownload}\n          />\n        }\n      />\n      <SignatureDialog\n        open={dialogOpen}\n        fieldBbox={field.bbox}\n        initialValue={field.imageDataUrl}\n        onOpenChange={setDialogOpen}\n        onConfirm={(imageDataUrl) => {\n          setField((previousField) => ({ ...previousField, imageDataUrl }))\n        }}\n      />\n    </>\n  )\n}\nfunction InlineDialogPanel({\n  className,\n  children,\n  ...props\n}: React.ComponentProps<\"div\">) {\n  return (\n    <InlineScrollArea className=\"min-h-0\">\n      <div\n        data-slot=\"dialog-panel\"\n        className={cn(\"min-h-0\", className)}\n        {...props}\n      >\n        {children}\n      </div>\n    </InlineScrollArea>\n  )\n}\nfunction InlineScrollArea2({\n  className,\n  children,\n  orientation = \"both\",\n  scrollFade = false,\n  scrollbarGutter = false,\n  scrollbarOverflowOnly = false,\n  viewportClassName,\n  viewportProps,\n  viewportRef,\n  ...props\n}: InlineScrollAreaProps) {\n  const {\n    className: viewportPropsClassName,\n    ref: viewportPropsRef,\n    ...resolvedViewportProps\n  } = viewportProps ?? {}\n  const composedViewportRef = React.useMemo(\n    () => InlineComposeRefs(viewportPropsRef, viewportRef),\n    [viewportPropsRef, viewportRef]\n  )\n\n  if (\n    !viewportProps &&\n    !viewportRef &&\n    !viewportClassName &&\n    !scrollFade &&\n    !scrollbarGutter &&\n    !scrollbarOverflowOnly\n  ) {\n    return (\n      <InlineScrollArea\n        {...props}\n        className={cn(\n          \"size-full min-h-0\",\n          orientation === \"horizontal\" &&\n            \"[&>[data-orientation=vertical]]:hidden\",\n          className\n        )}\n      >\n        {children}\n        {orientation !== \"vertical\" ? (\n          <ScrollBar orientation=\"horizontal\" />\n        ) : null}\n      </InlineScrollArea>\n    )\n  }\n\n  return (\n    <ScrollAreaPrimitive.Root\n      className={cn(\n        \"size-full min-h-0\",\n        scrollbarOverflowOnly &&\n          \"[&:not(:has([data-slot=scroll-area-viewport][data-has-overflow-x]))_[data-orientation=horizontal]]:hidden [&:not(:has([data-slot=scroll-area-viewport][data-has-overflow-y]))_[data-orientation=vertical]]:hidden\",\n        className\n      )}\n      {...props}\n    >\n      <ScrollAreaPrimitive.Viewport\n        {...resolvedViewportProps}\n        ref={composedViewportRef}\n        className={cn(\n          \"h-full rounded-[inherit] outline-none focus-visible:ring-2 focus-visible:ring-ring\",\n          scrollFade &&\n            \"mask-t-from-[calc(100%-min(var(--fade-size),var(--scroll-area-overflow-y-start)))] mask-r-from-[calc(100%-min(var(--fade-size),var(--scroll-area-overflow-x-end)))] mask-b-from-[calc(100%-min(var(--fade-size),var(--scroll-area-overflow-y-end)))] mask-l-from-[calc(100%-min(var(--fade-size),var(--scroll-area-overflow-x-start)))] [--fade-size:1.5rem]\",\n          scrollbarGutter && orientation !== \"vertical\" && \"pb-3.5\",\n          scrollbarGutter && orientation !== \"horizontal\" && \"pe-3.5\",\n          viewportPropsClassName,\n          viewportClassName\n        )}\n        data-slot=\"scroll-area-viewport\"\n      >\n        {children}\n      </ScrollAreaPrimitive.Viewport>\n      {orientation !== \"horizontal\" ? (\n        <ScrollBar orientation=\"vertical\" />\n      ) : null}\n      {orientation !== \"vertical\" ? (\n        <ScrollBar orientation=\"horizontal\" />\n      ) : null}\n      {orientation === \"both\" ? <ScrollAreaPrimitive.Corner /> : null}\n    </ScrollAreaPrimitive.Root>\n  )\n}\ntype InlineScrollAreaProps = ScrollAreaPrimitive.Root.Props & {\n  orientation?: \"vertical\" | \"horizontal\" | \"both\"\n  scrollFade?: boolean\n  scrollbarGutter?: boolean\n  scrollbarOverflowOnly?: boolean\n  viewportClassName?: string\n  viewportProps?: ScrollAreaPrimitive.Viewport.Props\n  viewportRef?: React.Ref<HTMLDivElement>\n}\nfunction InlineComposeRefs<T>(...refs: Array<React.Ref<T> | undefined>) {\n  return (node: T | null) => {\n    for (const ref of refs) {\n      if (!ref) continue\n      if (typeof ref === \"function\") ref(node)\n      else ref.current = node\n    }\n  }\n}\n",
      "type": "registry:component",
      "target": "@components/blocks/e-signature.tsx"
    }
  ],
  "categories": [
    "documents",
    "blocks"
  ],
  "type": "registry:block"
}
