{
  "name": "editable",
  "type": "registry:ui",
  "dependencies": [
    "radix-ui"
  ],
  "registryDependencies": [
    "@diceui/use-as-ref",
    "@diceui/use-isomorphic-layout-effect",
    "@diceui/use-lazy-ref"
  ],
  "files": [
    {
      "path": "ui/editable.tsx",
      "type": "registry:ui",
      "content": "\"use client\";\n\nimport {\n  Direction as DirectionPrimitive,\n  Slot as SlotPrimitive,\n} from \"radix-ui\";\nimport * as React from \"react\";\n\nimport { useComposedRefs } from \"@/lib/compose-refs\";\nimport { cn } from \"@/lib/utils\";\nimport { VisuallyHiddenInput } from \"@/registry/bases/radix/components/visually-hidden-input\";\nimport { useAsRef } from \"@/registry/bases/radix/hooks/use-as-ref\";\nimport { useIsomorphicLayoutEffect } from \"@/registry/bases/radix/hooks/use-isomorphic-layout-effect\";\nimport { useLazyRef } from \"@/registry/bases/radix/hooks/use-lazy-ref\";\n\nconst ROOT_NAME = \"Editable\";\nconst LABEL_NAME = \"EditableLabel\";\nconst AREA_NAME = \"EditableArea\";\nconst PREVIEW_NAME = \"EditablePreview\";\nconst INPUT_NAME = \"EditableInput\";\nconst TRIGGER_NAME = \"EditableTrigger\";\nconst TOOLBAR_NAME = \"EditableToolbar\";\nconst CANCEL_NAME = \"EditableCancel\";\nconst SUBMIT_NAME = \"EditableSubmit\";\n\ntype Direction = \"ltr\" | \"rtl\";\n\ninterface DivProps extends React.ComponentProps<\"div\"> {\n  asChild?: boolean;\n}\n\ntype RootElement = React.ComponentRef<typeof Editable>;\ntype PreviewElement = React.ComponentRef<typeof EditablePreview>;\ntype SubmitElement = React.ComponentRef<typeof EditableSubmit>;\ntype InputElement = React.ComponentRef<typeof EditableInput>;\n\ninterface StoreState {\n  value: string;\n  editing: boolean;\n}\n\ninterface Store {\n  subscribe: (callback: () => void) => () => void;\n  getState: () => StoreState;\n  setState: <K extends keyof StoreState>(key: K, value: StoreState[K]) => void;\n  notify: () => void;\n}\n\nconst StoreContext = React.createContext<Store | null>(null);\n\nfunction useStoreContext(consumerName: string) {\n  const context = React.useContext(StoreContext);\n  if (!context) {\n    throw new Error(`\\`${consumerName}\\` must be used within \\`${ROOT_NAME}\\``);\n  }\n  return context;\n}\n\nfunction useStore<T>(\n  selector: (state: StoreState) => T,\n  ogStore?: Store | null,\n): T {\n  const contextStore = React.useContext(StoreContext);\n\n  const store = ogStore ?? contextStore;\n\n  if (!store) {\n    throw new Error(`\\`useStore\\` must be used within \\`${ROOT_NAME}\\``);\n  }\n\n  const getSnapshot = React.useCallback(\n    () => selector(store.getState()),\n    [store, selector],\n  );\n\n  return React.useSyncExternalStore(store.subscribe, getSnapshot, getSnapshot);\n}\n\ninterface EditableContextValue {\n  rootId: string;\n  inputId: string;\n  labelId: string;\n  defaultValue: string;\n  onCancel: () => void;\n  onEdit: () => void;\n  onSubmit: (value: string) => void;\n  onEnterKeyDown?: (event: KeyboardEvent) => void;\n  onEscapeKeyDown?: (event: KeyboardEvent) => void;\n  dir?: Direction;\n  maxLength?: number;\n  placeholder?: string;\n  triggerMode: \"click\" | \"dblclick\" | \"focus\";\n  autosize: boolean;\n  disabled?: boolean;\n  readOnly?: boolean;\n  required?: boolean;\n  invalid?: boolean;\n}\n\nconst EditableContext = React.createContext<EditableContextValue | null>(null);\n\nfunction useEditableContext(consumerName: string) {\n  const context = React.useContext(EditableContext);\n  if (!context) {\n    throw new Error(`\\`${consumerName}\\` must be used within \\`${ROOT_NAME}\\``);\n  }\n  return context;\n}\n\ninterface EditableProps extends Omit<DivProps, \"onSubmit\"> {\n  id?: string;\n  defaultValue?: string;\n  value?: string;\n  onValueChange?: (value: string) => void;\n  defaultEditing?: boolean;\n  editing?: boolean;\n  onEditingChange?: (editing: boolean) => void;\n  onCancel?: () => void;\n  onEdit?: () => void;\n  onSubmit?: (value: string) => void;\n  onEscapeKeyDown?: (event: KeyboardEvent) => void;\n  onEnterKeyDown?: (event: KeyboardEvent) => void;\n  dir?: Direction;\n  maxLength?: number;\n  name?: string;\n  placeholder?: string;\n  triggerMode?: EditableContextValue[\"triggerMode\"];\n  autosize?: boolean;\n  disabled?: boolean;\n  readOnly?: boolean;\n  required?: boolean;\n  invalid?: boolean;\n}\n\nfunction Editable(props: EditableProps) {\n  const {\n    value: valueProp,\n    defaultValue = \"\",\n    defaultEditing,\n    editing: editingProp,\n    onValueChange,\n    onEditingChange,\n    onCancel: onCancelProp,\n    onEdit: onEditProp,\n    onSubmit: onSubmitProp,\n    onEscapeKeyDown,\n    onEnterKeyDown,\n    dir: dirProp,\n    maxLength,\n    name,\n    placeholder,\n    triggerMode = \"click\",\n    asChild,\n    autosize = false,\n    disabled,\n    required,\n    readOnly,\n    invalid,\n    className,\n    id,\n    ref,\n    ...rootProps\n  } = props;\n\n  const instanceId = React.useId();\n  const rootId = id ?? instanceId;\n\n  const inputId = React.useId();\n  const labelId = React.useId();\n\n  const dir = DirectionPrimitive.useDirection(dirProp);\n\n  const previousValueRef = React.useRef(defaultValue);\n\n  const [formTrigger, setFormTrigger] = React.useState<RootElement | null>(\n    null,\n  );\n  const composedRef = useComposedRefs(ref, (node) => setFormTrigger(node));\n  const isFormControl = formTrigger ? !!formTrigger.closest(\"form\") : true;\n\n  const listenersRef = useLazyRef(() => new Set<() => void>());\n  const stateRef = useLazyRef<StoreState>(() => ({\n    value: valueProp ?? defaultValue,\n    editing: editingProp ?? defaultEditing ?? false,\n  }));\n\n  const propsRef = useAsRef({\n    onValueChange,\n    onEditingChange,\n    onCancel: onCancelProp,\n    onEdit: onEditProp,\n    onSubmit: onSubmitProp,\n    onEscapeKeyDown,\n    onEnterKeyDown,\n  });\n\n  const store = React.useMemo<Store>(() => {\n    return {\n      subscribe: (cb) => {\n        listenersRef.current.add(cb);\n        return () => listenersRef.current.delete(cb);\n      },\n      getState: () => stateRef.current,\n      setState: (key, value) => {\n        if (Object.is(stateRef.current[key], value)) return;\n\n        if (key === \"value\" && typeof value === \"string\") {\n          stateRef.current.value = value;\n          propsRef.current.onValueChange?.(value);\n        } else if (key === \"editing\" && typeof value === \"boolean\") {\n          stateRef.current.editing = value;\n          propsRef.current.onEditingChange?.(value);\n        } else {\n          stateRef.current[key] = value;\n        }\n\n        store.notify();\n      },\n      notify: () => {\n        for (const cb of listenersRef.current) {\n          cb();\n        }\n      },\n    };\n  }, [listenersRef, stateRef, propsRef]);\n\n  const value = useStore((state) => state.value, store);\n\n  useIsomorphicLayoutEffect(() => {\n    if (valueProp !== undefined) {\n      store.setState(\"value\", valueProp);\n    }\n  }, [valueProp]);\n\n  useIsomorphicLayoutEffect(() => {\n    if (editingProp !== undefined) {\n      store.setState(\"editing\", editingProp);\n    }\n  }, [editingProp]);\n\n  const onCancel = React.useCallback(() => {\n    const prevValue = previousValueRef.current;\n    store.setState(\"value\", prevValue);\n    store.setState(\"editing\", false);\n    propsRef.current.onCancel?.();\n  }, [store, propsRef]);\n\n  const onEdit = React.useCallback(() => {\n    const currentValue = store.getState().value;\n    previousValueRef.current = currentValue;\n    store.setState(\"editing\", true);\n    propsRef.current.onEdit?.();\n  }, [store, propsRef]);\n\n  const onSubmit = React.useCallback(\n    (newValue: string) => {\n      store.setState(\"value\", newValue);\n      store.setState(\"editing\", false);\n      propsRef.current.onSubmit?.(newValue);\n    },\n    [store, propsRef],\n  );\n\n  const contextValue = React.useMemo<EditableContextValue>(\n    () => ({\n      rootId,\n      inputId,\n      labelId,\n      defaultValue,\n      onSubmit,\n      onEdit,\n      onCancel,\n      onEscapeKeyDown,\n      onEnterKeyDown,\n      dir,\n      maxLength,\n      placeholder,\n      triggerMode,\n      autosize,\n      disabled,\n      readOnly,\n      required,\n      invalid,\n    }),\n    [\n      rootId,\n      inputId,\n      labelId,\n      defaultValue,\n      onSubmit,\n      onCancel,\n      onEdit,\n      onEscapeKeyDown,\n      onEnterKeyDown,\n      dir,\n      maxLength,\n      placeholder,\n      triggerMode,\n      autosize,\n      disabled,\n      required,\n      readOnly,\n      invalid,\n    ],\n  );\n\n  const RootPrimitive = asChild ? SlotPrimitive.Slot : \"div\";\n\n  return (\n    <StoreContext.Provider value={store}>\n      <EditableContext.Provider value={contextValue}>\n        <RootPrimitive\n          data-slot=\"editable\"\n          {...rootProps}\n          id={id}\n          ref={composedRef}\n          className={cn(\"flex min-w-0 flex-col gap-2\", className)}\n        />\n        {isFormControl && (\n          <VisuallyHiddenInput\n            type=\"hidden\"\n            control={formTrigger}\n            name={name}\n            value={value}\n            disabled={disabled}\n            readOnly={readOnly}\n            required={required}\n          />\n        )}\n      </EditableContext.Provider>\n    </StoreContext.Provider>\n  );\n}\n\ninterface EditableLabelProps extends React.ComponentProps<\"label\"> {\n  asChild?: boolean;\n}\n\nfunction EditableLabel(props: EditableLabelProps) {\n  const { asChild, className, children, ref, ...labelProps } = props;\n  const context = useEditableContext(LABEL_NAME);\n\n  const LabelPrimitive = asChild ? SlotPrimitive.Slot : \"label\";\n\n  return (\n    <LabelPrimitive\n      data-disabled={context.disabled ? \"\" : undefined}\n      data-invalid={context.invalid ? \"\" : undefined}\n      data-required={context.required ? \"\" : undefined}\n      data-slot=\"editable-label\"\n      {...labelProps}\n      ref={ref}\n      id={context.labelId}\n      htmlFor={context.inputId}\n      className={cn(\n        \"text-sm leading-none font-medium peer-disabled:cursor-not-allowed peer-disabled:opacity-70 data-required:after:ml-0.5 data-required:after:text-destructive data-required:after:content-['*']\",\n        className,\n      )}\n    >\n      {children}\n    </LabelPrimitive>\n  );\n}\n\ninterface EditableAreaProps extends React.ComponentProps<\"div\"> {\n  asChild?: boolean;\n}\n\nfunction EditableArea(props: EditableAreaProps) {\n  const { asChild, className, ref, ...areaProps } = props;\n  const context = useEditableContext(AREA_NAME);\n  const editing = useStore((state) => state.editing);\n\n  const AreaPrimitive = asChild ? SlotPrimitive.Slot : \"div\";\n\n  return (\n    <AreaPrimitive\n      role=\"group\"\n      data-disabled={context.disabled ? \"\" : undefined}\n      data-editing={editing ? \"\" : undefined}\n      data-slot=\"editable-area\"\n      dir={context.dir}\n      {...areaProps}\n      ref={ref}\n      className={cn(\n        \"relative inline-block min-w-0 data-disabled:cursor-not-allowed data-disabled:opacity-50\",\n        className,\n      )}\n    />\n  );\n}\n\ninterface EditablePreviewProps extends React.ComponentProps<\"div\"> {\n  asChild?: boolean;\n}\n\nfunction EditablePreview(props: EditablePreviewProps) {\n  const {\n    onClick: onClickProp,\n    onDoubleClick: onDoubleClickProp,\n    onFocus: onFocusProp,\n    onKeyDown: onKeyDownProp,\n    asChild,\n    className,\n    ref,\n    ...previewProps\n  } = props;\n\n  const context = useEditableContext(PREVIEW_NAME);\n  const value = useStore((state) => state.value);\n  const editing = useStore((state) => state.editing);\n\n  const propsRef = useAsRef({\n    onClick: onClickProp,\n    onDoubleClick: onDoubleClickProp,\n    onFocus: onFocusProp,\n    onKeyDown: onKeyDownProp,\n  });\n\n  const onTrigger = React.useCallback(() => {\n    if (context.disabled || context.readOnly) return;\n    context.onEdit();\n  }, [context.onEdit, context.disabled, context.readOnly]);\n\n  const onClick = React.useCallback(\n    (event: React.MouseEvent<PreviewElement>) => {\n      propsRef.current.onClick?.(event);\n      if (event.defaultPrevented || context.triggerMode !== \"click\") return;\n\n      onTrigger();\n    },\n    [propsRef, onTrigger, context.triggerMode],\n  );\n\n  const onDoubleClick = React.useCallback(\n    (event: React.MouseEvent<PreviewElement>) => {\n      propsRef.current.onDoubleClick?.(event);\n      if (event.defaultPrevented || context.triggerMode !== \"dblclick\") return;\n\n      onTrigger();\n    },\n    [propsRef, onTrigger, context.triggerMode],\n  );\n\n  const onFocus = React.useCallback(\n    (event: React.FocusEvent<PreviewElement>) => {\n      propsRef.current.onFocus?.(event);\n      if (event.defaultPrevented || context.triggerMode !== \"focus\") return;\n\n      onTrigger();\n    },\n    [propsRef, onTrigger, context.triggerMode],\n  );\n\n  const onKeyDown = React.useCallback(\n    (event: React.KeyboardEvent<PreviewElement>) => {\n      propsRef.current.onKeyDown?.(event);\n      if (event.defaultPrevented) return;\n\n      if (event.key === \"Enter\") {\n        const nativeEvent = event.nativeEvent;\n        if (context.onEnterKeyDown) {\n          context.onEnterKeyDown(nativeEvent);\n          if (nativeEvent.defaultPrevented) return;\n        }\n        onTrigger();\n      }\n    },\n    [propsRef, onTrigger, context.onEnterKeyDown],\n  );\n\n  const PreviewPrimitive = asChild ? SlotPrimitive.Slot : \"div\";\n\n  if (editing || context.readOnly) return null;\n\n  return (\n    <PreviewPrimitive\n      role=\"button\"\n      aria-disabled={context.disabled || context.readOnly}\n      data-empty={!value ? \"\" : undefined}\n      data-disabled={context.disabled ? \"\" : undefined}\n      data-readonly={context.readOnly ? \"\" : undefined}\n      data-slot=\"editable-preview\"\n      tabIndex={context.disabled || context.readOnly ? undefined : 0}\n      {...previewProps}\n      ref={ref}\n      onClick={onClick}\n      onDoubleClick={onDoubleClick}\n      onFocus={onFocus}\n      onKeyDown={onKeyDown}\n      className={cn(\n        \"cursor-text truncate rounded-sm border border-transparent py-1 text-base focus-visible:ring-1 focus-visible:ring-ring focus-visible:outline-hidden data-disabled:cursor-not-allowed data-disabled:opacity-50 data-empty:text-muted-foreground data-readonly:cursor-default md:text-sm\",\n        className,\n      )}\n    >\n      {value || context.placeholder}\n    </PreviewPrimitive>\n  );\n}\n\ninterface EditableInputProps extends React.ComponentProps<\"input\"> {\n  asChild?: boolean;\n  maxLength?: number;\n}\n\nfunction EditableInput(props: EditableInputProps) {\n  const {\n    onBlur: onBlurProp,\n    onChange: onChangeProp,\n    onKeyDown: onKeyDownProp,\n    asChild,\n    className,\n    disabled,\n    readOnly,\n    required,\n    maxLength,\n    ref,\n    ...inputProps\n  } = props;\n\n  const context = useEditableContext(INPUT_NAME);\n  const store = useStoreContext(INPUT_NAME);\n  const value = useStore((state) => state.value);\n  const editing = useStore((state) => state.editing);\n  const inputRef = React.useRef<InputElement>(null);\n  const composedRef = useComposedRefs(ref, inputRef);\n\n  const propsRef = useAsRef({\n    onBlur: onBlurProp,\n    onChange: onChangeProp,\n    onKeyDown: onKeyDownProp,\n  });\n\n  const isDisabled = disabled || context.disabled;\n  const isReadOnly = readOnly || context.readOnly;\n  const isRequired = required || context.required;\n\n  const onAutosize = React.useCallback(\n    (target: InputElement) => {\n      if (!context.autosize) return;\n\n      if (target instanceof HTMLTextAreaElement) {\n        target.style.height = \"0\";\n        target.style.height = `${target.scrollHeight}px`;\n      } else {\n        target.style.width = \"0\";\n        target.style.width = `${target.scrollWidth + 4}px`;\n      }\n    },\n    [context.autosize],\n  );\n\n  const onBlur = React.useCallback(\n    (event: React.FocusEvent<InputElement>) => {\n      if (isDisabled || isReadOnly) return;\n\n      propsRef.current.onBlur?.(event);\n      if (event.defaultPrevented) return;\n\n      const relatedTarget = event.relatedTarget;\n\n      const isAction =\n        relatedTarget instanceof HTMLElement &&\n        (relatedTarget.closest(`[data-slot=\"editable-trigger\"]`) ||\n          relatedTarget.closest(`[data-slot=\"editable-cancel\"]`));\n\n      if (!isAction) {\n        context.onSubmit(value);\n      }\n    },\n    [value, context.onSubmit, propsRef, isDisabled, isReadOnly],\n  );\n\n  const onChange = React.useCallback(\n    (event: React.ChangeEvent<InputElement>) => {\n      if (isDisabled || isReadOnly) return;\n\n      propsRef.current.onChange?.(event);\n      if (event.defaultPrevented) return;\n\n      store.setState(\"value\", event.target.value);\n      onAutosize(event.target);\n    },\n    [store, propsRef, onAutosize, isDisabled, isReadOnly],\n  );\n\n  const onKeyDown = React.useCallback(\n    (event: React.KeyboardEvent<InputElement>) => {\n      if (isDisabled || isReadOnly) return;\n\n      propsRef.current.onKeyDown?.(event);\n      if (event.defaultPrevented) return;\n\n      if (event.key === \"Escape\") {\n        const nativeEvent = event.nativeEvent;\n        if (context.onEscapeKeyDown) {\n          context.onEscapeKeyDown(nativeEvent);\n          if (nativeEvent.defaultPrevented) return;\n        }\n        context.onCancel();\n      } else if (event.key === \"Enter\") {\n        context.onSubmit(value);\n      }\n    },\n    [\n      value,\n      context.onSubmit,\n      context.onCancel,\n      context.onEscapeKeyDown,\n      propsRef,\n      isDisabled,\n      isReadOnly,\n    ],\n  );\n\n  useIsomorphicLayoutEffect(() => {\n    if (!editing || isDisabled || isReadOnly || !inputRef.current) return;\n\n    const frameId = window.requestAnimationFrame(() => {\n      if (!inputRef.current) return;\n\n      inputRef.current.focus();\n      inputRef.current.select();\n      onAutosize(inputRef.current);\n    });\n\n    return () => {\n      window.cancelAnimationFrame(frameId);\n    };\n  }, [editing, onAutosize, isDisabled, isReadOnly]);\n\n  const InputPrimitive = asChild ? SlotPrimitive.Slot : \"input\";\n\n  if (!editing && !isReadOnly) return null;\n\n  return (\n    <InputPrimitive\n      aria-required={isRequired}\n      aria-invalid={context.invalid}\n      data-slot=\"editable-input\"\n      dir={context.dir}\n      disabled={isDisabled}\n      readOnly={isReadOnly}\n      required={isRequired}\n      {...inputProps}\n      id={context.inputId}\n      aria-labelledby={context.labelId}\n      ref={composedRef}\n      maxLength={maxLength}\n      placeholder={context.placeholder}\n      value={value}\n      onBlur={onBlur}\n      onChange={onChange}\n      onKeyDown={onKeyDown}\n      className={cn(\n        \"flex rounded-sm border border-input bg-transparent py-1 text-base shadow-xs transition-colors file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground focus-visible:ring-1 focus-visible:ring-ring focus-visible:outline-hidden disabled:cursor-not-allowed disabled:opacity-50 md:text-sm\",\n        context.autosize ? \"w-auto\" : \"w-full\",\n        className,\n      )}\n    />\n  );\n}\n\ninterface EditableTriggerProps extends React.ComponentProps<\"button\"> {\n  asChild?: boolean;\n  forceMount?: boolean;\n}\n\nfunction EditableTrigger(props: EditableTriggerProps) {\n  const { asChild, forceMount = false, ref, ...triggerProps } = props;\n  const context = useEditableContext(TRIGGER_NAME);\n  const editing = useStore((state) => state.editing);\n\n  const onTrigger = React.useCallback(() => {\n    if (context.disabled || context.readOnly) return;\n    context.onEdit();\n  }, [context.disabled, context.readOnly, context.onEdit]);\n\n  const TriggerPrimitive = asChild ? SlotPrimitive.Slot : \"button\";\n\n  if (!forceMount && (editing || context.readOnly)) return null;\n\n  return (\n    <TriggerPrimitive\n      type=\"button\"\n      aria-controls={context.rootId}\n      aria-disabled={context.disabled || context.readOnly}\n      data-disabled={context.disabled ? \"\" : undefined}\n      data-readonly={context.readOnly ? \"\" : undefined}\n      data-slot=\"editable-trigger\"\n      {...triggerProps}\n      ref={ref}\n      onClick={context.triggerMode === \"click\" ? onTrigger : undefined}\n      onDoubleClick={context.triggerMode === \"dblclick\" ? onTrigger : undefined}\n    />\n  );\n}\n\ninterface EditableToolbarProps extends React.ComponentProps<\"div\"> {\n  asChild?: boolean;\n  orientation?: \"horizontal\" | \"vertical\";\n}\n\nfunction EditableToolbar(props: EditableToolbarProps) {\n  const {\n    asChild,\n    className,\n    orientation = \"horizontal\",\n    ref,\n    ...toolbarProps\n  } = props;\n  const context = useEditableContext(TOOLBAR_NAME);\n\n  const ToolbarPrimitive = asChild ? SlotPrimitive.Slot : \"div\";\n\n  return (\n    <ToolbarPrimitive\n      role=\"toolbar\"\n      aria-controls={context.rootId}\n      aria-orientation={orientation}\n      data-slot=\"editable-toolbar\"\n      dir={context.dir}\n      {...toolbarProps}\n      ref={ref}\n      className={cn(\n        \"flex items-center gap-2\",\n        orientation === \"vertical\" && \"flex-col\",\n        className,\n      )}\n    />\n  );\n}\n\ninterface EditableCancelProps extends React.ComponentProps<\"button\"> {\n  asChild?: boolean;\n}\n\nfunction EditableCancel(props: EditableCancelProps) {\n  const { onClick: onClickProp, asChild, ref, ...cancelProps } = props;\n  const context = useEditableContext(CANCEL_NAME);\n  const editing = useStore((state) => state.editing);\n\n  const propsRef = useAsRef({\n    onClick: onClickProp,\n  });\n\n  const onClick = React.useCallback(\n    (event: React.MouseEvent<HTMLButtonElement>) => {\n      if (context.disabled || context.readOnly) return;\n\n      propsRef.current.onClick?.(event);\n      if (event.defaultPrevented) return;\n\n      context.onCancel();\n    },\n    [propsRef, context.onCancel, context.disabled, context.readOnly],\n  );\n\n  const CancelPrimitive = asChild ? SlotPrimitive.Slot : \"button\";\n\n  if (!editing && !context.readOnly) return null;\n\n  return (\n    <CancelPrimitive\n      type=\"button\"\n      aria-controls={context.rootId}\n      data-slot=\"editable-cancel\"\n      {...cancelProps}\n      onClick={onClick}\n      ref={ref}\n    />\n  );\n}\n\ninterface EditableSubmitProps extends React.ComponentProps<\"button\"> {\n  asChild?: boolean;\n}\n\nfunction EditableSubmit(props: EditableSubmitProps) {\n  const { onClick: onClickProp, asChild, ref, ...submitProps } = props;\n  const context = useEditableContext(SUBMIT_NAME);\n  const value = useStore((state) => state.value);\n  const editing = useStore((state) => state.editing);\n\n  const propsRef = useAsRef({\n    onClick: onClickProp,\n  });\n\n  const onClick = React.useCallback(\n    (event: React.MouseEvent<SubmitElement>) => {\n      if (context.disabled || context.readOnly) return;\n\n      propsRef.current.onClick?.(event);\n      if (event.defaultPrevented) return;\n\n      context.onSubmit(value);\n    },\n    [propsRef, context.onSubmit, value, context.disabled, context.readOnly],\n  );\n\n  const SubmitPrimitive = asChild ? SlotPrimitive.Slot : \"button\";\n\n  if (!editing && !context.readOnly) return null;\n\n  return (\n    <SubmitPrimitive\n      type=\"button\"\n      aria-controls={context.rootId}\n      data-slot=\"editable-submit\"\n      {...submitProps}\n      ref={ref}\n      onClick={onClick}\n    />\n  );\n}\n\nexport {\n  Editable,\n  EditableArea,\n  EditableCancel,\n  EditableInput,\n  EditableLabel,\n  EditablePreview,\n  type EditableProps,\n  EditableSubmit,\n  EditableToolbar,\n  EditableTrigger,\n  useStore as useEditable,\n};\n",
      "target": ""
    },
    {
      "path": "components/visually-hidden-input.tsx",
      "type": "registry:component",
      "content": "\"use client\";\n\nimport * as React from \"react\";\n\ntype InputValue = string[] | string;\n\ninterface VisuallyHiddenInputProps<T = InputValue> extends Omit<\n  React.InputHTMLAttributes<HTMLInputElement>,\n  \"value\" | \"checked\" | \"onReset\"\n> {\n  value?: T;\n  checked?: boolean;\n  control: HTMLElement | null;\n  bubbles?: boolean;\n}\n\nfunction VisuallyHiddenInput<T = InputValue>(\n  props: VisuallyHiddenInputProps<T>,\n) {\n  const {\n    control,\n    value,\n    checked,\n    bubbles = true,\n    type = \"hidden\",\n    style,\n    ...inputProps\n  } = props;\n\n  const isCheckInput = React.useMemo(\n    () => type === \"checkbox\" || type === \"radio\" || type === \"switch\",\n    [type],\n  );\n  const inputRef = React.useRef<HTMLInputElement>(null);\n\n  const prevValueRef = React.useRef<{\n    value: T | boolean | undefined;\n    previous: T | boolean | undefined;\n  }>({\n    value: isCheckInput ? checked : value,\n    previous: isCheckInput ? checked : value,\n  });\n\n  const prevValue = React.useMemo(() => {\n    const currentValue = isCheckInput ? checked : value;\n    if (prevValueRef.current.value !== currentValue) {\n      prevValueRef.current.previous = prevValueRef.current.value;\n      prevValueRef.current.value = currentValue;\n    }\n    return prevValueRef.current.previous;\n  }, [isCheckInput, value, checked]);\n\n  const [controlSize, setControlSize] = React.useState<{\n    width?: number;\n    height?: number;\n  }>({});\n\n  React.useLayoutEffect(() => {\n    if (!control) {\n      setControlSize({});\n      return;\n    }\n\n    setControlSize({\n      width: control.offsetWidth,\n      height: control.offsetHeight,\n    });\n\n    if (typeof window === \"undefined\") return;\n\n    const resizeObserver = new ResizeObserver((entries) => {\n      if (!Array.isArray(entries) || !entries.length) return;\n\n      const entry = entries[0];\n      if (!entry) return;\n\n      let width: number;\n      let height: number;\n\n      if (\"borderBoxSize\" in entry) {\n        const borderSizeEntry = entry.borderBoxSize;\n        const borderSize = Array.isArray(borderSizeEntry)\n          ? borderSizeEntry[0]\n          : borderSizeEntry;\n        width = borderSize.inlineSize;\n        height = borderSize.blockSize;\n      } else {\n        width = control.offsetWidth;\n        height = control.offsetHeight;\n      }\n\n      setControlSize({ width, height });\n    });\n\n    resizeObserver.observe(control, { box: \"border-box\" });\n    return () => {\n      resizeObserver.disconnect();\n    };\n  }, [control]);\n\n  React.useEffect(() => {\n    const input = inputRef.current;\n    if (!input) return;\n\n    const inputProto = window.HTMLInputElement.prototype;\n    const propertyKey = isCheckInput ? \"checked\" : \"value\";\n    const eventType = isCheckInput ? \"click\" : \"input\";\n    const currentValue = isCheckInput ? checked : value;\n\n    const serializedCurrentValue = isCheckInput\n      ? checked\n      : typeof value === \"object\" && value !== null\n        ? JSON.stringify(value)\n        : value;\n\n    const descriptor = Object.getOwnPropertyDescriptor(inputProto, propertyKey);\n\n    const setter = descriptor?.set;\n\n    if (prevValue !== currentValue && setter) {\n      const event = new Event(eventType, { bubbles });\n      setter.call(input, serializedCurrentValue);\n      input.dispatchEvent(event);\n    }\n  }, [prevValue, value, checked, bubbles, isCheckInput]);\n\n  const composedStyle = React.useMemo<React.CSSProperties>(() => {\n    return {\n      ...style,\n      ...(controlSize.width !== undefined && controlSize.height !== undefined\n        ? controlSize\n        : {}),\n      border: 0,\n      clip: \"rect(0 0 0 0)\",\n      clipPath: \"inset(50%)\",\n      height: \"1px\",\n      margin: \"-1px\",\n      overflow: \"hidden\",\n      padding: 0,\n      position: \"absolute\",\n      whiteSpace: \"nowrap\",\n      width: \"1px\",\n    };\n  }, [style, controlSize]);\n\n  return (\n    <input\n      type={type}\n      {...inputProps}\n      ref={inputRef}\n      aria-hidden={isCheckInput}\n      tabIndex={-1}\n      defaultChecked={isCheckInput ? checked : undefined}\n      style={composedStyle}\n    />\n  );\n}\n\nexport { VisuallyHiddenInput };\n",
      "target": ""
    },
    {
      "path": "lib/compose-refs.ts",
      "type": "registry:lib",
      "content": "/**\n * @see https://github.com/radix-ui/primitives/blob/main/packages/react/compose-refs/src/compose-refs.tsx\n */\n\nimport * as React from \"react\";\n\ntype PossibleRef<T> = React.Ref<T> | undefined;\n\n/**\n * Set a given ref to a given value\n * This utility takes care of different types of refs: callback refs and RefObject(s)\n */\nfunction setRef<T>(ref: PossibleRef<T>, value: T) {\n  if (typeof ref === \"function\") {\n    return ref(value);\n  }\n\n  if (ref !== null && ref !== undefined) {\n    ref.current = value;\n  }\n}\n\n/**\n * A utility to compose multiple refs together\n * Accepts callback refs and RefObject(s)\n */\nfunction composeRefs<T>(...refs: PossibleRef<T>[]): React.RefCallback<T> {\n  return (node) => {\n    let hasCleanup = false;\n    const cleanups = refs.map((ref) => {\n      const cleanup = setRef(ref, node);\n      if (!hasCleanup && typeof cleanup === \"function\") {\n        hasCleanup = true;\n      }\n      return cleanup;\n    });\n\n    // React <19 will log an error to the console if a callback ref returns a\n    // value. We don't use ref cleanups internally so this will only happen if a\n    // user's ref callback returns a value, which we only expect if they are\n    // using the cleanup functionality added in React 19.\n    if (hasCleanup) {\n      return () => {\n        for (let i = 0; i < cleanups.length; i++) {\n          const cleanup = cleanups[i];\n          if (typeof cleanup === \"function\") {\n            cleanup();\n          } else {\n            setRef(refs[i], null);\n          }\n        }\n      };\n    }\n  };\n}\n\n/**\n * A custom hook that composes multiple refs\n * Accepts callback refs and RefObject(s)\n */\nfunction useComposedRefs<T>(...refs: PossibleRef<T>[]): React.RefCallback<T> {\n  // oxlint-disable-next-line react/exhaustive-deps -- we want to memoize by all values\n  return React.useCallback(composeRefs(...refs), refs);\n}\n\nexport { composeRefs, useComposedRefs };\n",
      "target": ""
    }
  ]
}