{
  "name": "time-picker",
  "type": "registry:ui",
  "dependencies": [
    "cn",
    "radix-ui"
  ],
  "registryDependencies": [
    "popover",
    "@diceui/use-as-ref",
    "@diceui/use-isomorphic-layout-effect",
    "@diceui/use-lazy-ref"
  ],
  "css": {
    "@utility scrollbar-none": {
      "scrollbar-width": "none",
      "&::-webkit-scrollbar": {
        "display": "none"
      }
    }
  },
  "files": [
    {
      "path": "ui/time-picker.tsx",
      "type": "registry:ui",
      "content": "\"use client\";\n\nimport { cn } from \"cn\";\nimport { Slot as SlotPrimitive } from \"radix-ui\";\nimport * as React from \"react\";\n\nimport { useComposedRefs } from \"@/lib/compose-refs\";\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\";\nimport {\n  Popover,\n  PopoverAnchor,\n  PopoverContent,\n  PopoverTrigger,\n} from \"@/registry/bases/radix/ui/popover\";\nimport { IconPlaceholder } from \"@/registry/icons/icon-placeholder\";\n\nconst ROOT_NAME = \"TimePicker\";\nconst LABEL_NAME = \"TimePickerLabel\";\nconst INPUT_GROUP_NAME = \"TimePickerInputGroup\";\nconst INPUT_NAME = \"TimePickerInput\";\nconst TRIGGER_NAME = \"TimePickerTrigger\";\nconst CONTENT_NAME = \"TimePickerContent\";\nconst COLUMN_NAME = \"TimePickerColumn\";\nconst COLUMN_ITEM_NAME = \"TimePickerColumnItem\";\nconst HOUR_NAME = \"TimePickerHour\";\nconst MINUTE_NAME = \"TimePickerMinute\";\nconst SECOND_NAME = \"TimePickerSecond\";\nconst PERIOD_NAME = \"TimePickerPeriod\";\nconst CLEAR_NAME = \"TimePickerClear\";\n\nconst DEFAULT_STEP = 1;\nconst DEFAULT_SEGMENT_PLACEHOLDER = \"--\";\nconst DEFAULT_LOCALE = undefined;\nconst SEGMENTS: Segment[] = [\"hour\", \"minute\", \"second\", \"period\"];\nconst PERIODS = [\"AM\", \"PM\"] as const;\n\ntype Segment = \"hour\" | \"minute\" | \"second\" | \"period\";\ntype SegmentFormat = \"numeric\" | \"2-digit\";\ntype Period = (typeof PERIODS)[number];\n\ninterface DivProps extends React.ComponentProps<\"div\"> {\n  asChild?: boolean;\n}\n\ninterface ButtonProps extends React.ComponentProps<\"button\"> {\n  asChild?: boolean;\n}\n\ntype PopoverContentProps = React.ComponentProps<typeof PopoverContent>;\n\ntype InputGroupElement = React.ComponentRef<typeof TimePickerInputGroup>;\ntype InputElement = React.ComponentRef<typeof TimePickerInput>;\ntype TriggerElement = React.ComponentRef<typeof TimePickerTrigger>;\ntype ColumnElement = React.ComponentRef<typeof TimePickerColumn>;\ntype ColumnItemElement = React.ComponentRef<typeof TimePickerColumnItem>;\n\ninterface TimeValue {\n  hour?: number;\n  minute?: number;\n  second?: number;\n  period?: Period;\n}\n\ninterface ItemData {\n  value: number | string;\n  ref: React.RefObject<ColumnItemElement | null>;\n  selected: boolean;\n}\n\ninterface ColumnData {\n  id: string;\n  ref: React.RefObject<ColumnElement | null>;\n  getSelectedItemRef: () => React.RefObject<ColumnItemElement | null> | null;\n  getItems: () => ItemData[];\n}\n\nfunction focusFirst(\n  candidates: React.RefObject<ColumnItemElement | null>[],\n  preventScroll = false,\n) {\n  const PREVIOUSLY_FOCUSED_ELEMENT = document.activeElement;\n  for (const candidateRef of candidates) {\n    const candidate = candidateRef.current;\n    if (!candidate) continue;\n    if (candidate === PREVIOUSLY_FOCUSED_ELEMENT) return;\n    candidate.focus({ preventScroll });\n    if (document.activeElement !== PREVIOUSLY_FOCUSED_ELEMENT) return;\n  }\n}\n\nfunction sortNodes<T extends { ref: React.RefObject<Element | null> }>(\n  items: T[],\n): T[] {\n  return items.sort((a, b) => {\n    const elementA = a.ref.current;\n    const elementB = b.ref.current;\n    if (!elementA || !elementB) return 0;\n    const position = elementA.compareDocumentPosition(elementB);\n    if (position & Node.DOCUMENT_POSITION_FOLLOWING) {\n      return -1;\n    }\n    if (position & Node.DOCUMENT_POSITION_PRECEDING) {\n      return 1;\n    }\n    return 0;\n  });\n}\n\nfunction getIs12Hour(locale?: string): boolean {\n  const testDate = new Date(2000, 0, 1, 13, 0, 0);\n  const formatted = new Intl.DateTimeFormat(locale, {\n    hour: \"numeric\",\n  }).format(testDate);\n\n  return /am|pm/i.test(formatted) || !formatted.includes(\"13\");\n}\n\nfunction parseTimeString(timeString: string | undefined): TimeValue | null {\n  if (!timeString) return null;\n\n  const parts = timeString.split(\":\");\n  if (parts.length < 2) return null;\n\n  const result: TimeValue = {};\n\n  if (parts[0] && parts[0] !== DEFAULT_SEGMENT_PLACEHOLDER) {\n    const hour = Number.parseInt(parts[0], 10);\n    if (!Number.isNaN(hour) && hour >= 0 && hour <= 23) {\n      result.hour = hour;\n    }\n  }\n\n  if (parts[1] && parts[1] !== DEFAULT_SEGMENT_PLACEHOLDER) {\n    const minute = Number.parseInt(parts[1], 10);\n    if (!Number.isNaN(minute) && minute >= 0 && minute <= 59) {\n      result.minute = minute;\n    }\n  }\n\n  if (parts[2] && parts[2] !== DEFAULT_SEGMENT_PLACEHOLDER) {\n    const second = Number.parseInt(parts[2], 10);\n    if (!Number.isNaN(second) && second >= 0 && second <= 59) {\n      result.second = second;\n    }\n  }\n  if (\n    result.hour === undefined &&\n    result.minute === undefined &&\n    result.second === undefined\n  ) {\n    return null;\n  }\n\n  return result;\n}\n\nfunction formatTimeValue(value: TimeValue, showSeconds: boolean): string {\n  const hourStr =\n    value.hour !== undefined\n      ? value.hour.toString().padStart(2, \"0\")\n      : DEFAULT_SEGMENT_PLACEHOLDER;\n  const minuteStr =\n    value.minute !== undefined\n      ? value.minute.toString().padStart(2, \"0\")\n      : DEFAULT_SEGMENT_PLACEHOLDER;\n  const secondStr =\n    value.second !== undefined\n      ? value.second.toString().padStart(2, \"0\")\n      : DEFAULT_SEGMENT_PLACEHOLDER;\n\n  if (showSeconds) {\n    return `${hourStr}:${minuteStr}:${secondStr}`;\n  }\n  return `${hourStr}:${minuteStr}`;\n}\n\nfunction to12Hour(hour24: number): { hour: number; period: Period } {\n  const period: Period = hour24 >= 12 ? \"PM\" : \"AM\";\n  const hour = hour24 % 12 || 12;\n  return { hour, period };\n}\n\nfunction to24Hour(hour12: number, period: Period): number {\n  if (hour12 === 12) {\n    return period === \"PM\" ? 12 : 0;\n  }\n  return period === \"PM\" ? hour12 + 12 : hour12;\n}\n\nfunction clamp(value: number, min: number, max: number) {\n  return Math.min(Math.max(value, min), max);\n}\n\ninterface StoreState {\n  value: string;\n  open: boolean;\n  openedViaFocus: 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\ntype SegmentPlaceholder =\n  | string\n  | {\n      hour?: string;\n      minute?: string;\n      second?: string;\n      period?: string;\n    };\n\ninterface TimePickerContextValue {\n  id: string;\n  inputGroupId: string;\n  labelId: string;\n  triggerId: string;\n  inputGroupRef: React.RefObject<InputGroupElement | null>;\n  triggerRef: React.RefObject<TriggerElement | null>;\n  openOnFocus: boolean;\n  inputGroupClickAction: \"focus\" | \"open\";\n  onInputGroupChange: (inputGroup: InputGroupElement | null) => void;\n  disabled: boolean;\n  readOnly: boolean;\n  required: boolean;\n  invalid: boolean;\n  showSeconds: boolean;\n  is12Hour: boolean;\n  minuteStep: number;\n  secondStep: number;\n  hourStep: number;\n  segmentPlaceholder: {\n    hour: string;\n    minute: string;\n    second: string;\n    period: string;\n  };\n  min?: string;\n  max?: string;\n}\n\nconst TimePickerContext = React.createContext<TimePickerContextValue | null>(\n  null,\n);\n\nfunction useTimePickerContext(consumerName: string) {\n  const context = React.useContext(TimePickerContext);\n  if (!context) {\n    throw new Error(`\\`${consumerName}\\` must be used within \\`${ROOT_NAME}\\``);\n  }\n  return context;\n}\n\ninterface TimePickerProps extends DivProps {\n  value?: string;\n  defaultValue?: string;\n  onValueChange?: (value: string) => void;\n  open?: boolean;\n  defaultOpen?: boolean;\n  onOpenChange?: (open: boolean) => void;\n  openOnFocus?: boolean;\n  inputGroupClickAction?: \"focus\" | \"open\";\n  min?: string;\n  max?: string;\n  hourStep?: number;\n  minuteStep?: number;\n  secondStep?: number;\n  segmentPlaceholder?: SegmentPlaceholder;\n  locale?: string;\n  name?: string;\n  disabled?: boolean;\n  invalid?: boolean;\n  readOnly?: boolean;\n  required?: boolean;\n  showSeconds?: boolean;\n}\n\nfunction TimePicker(props: TimePickerProps) {\n  const {\n    value: valueProp,\n    defaultValue,\n    onValueChange,\n    open,\n    defaultOpen,\n    onOpenChange,\n    openOnFocus = false,\n    inputGroupClickAction = \"focus\",\n    min,\n    max,\n    hourStep = DEFAULT_STEP,\n    minuteStep = DEFAULT_STEP,\n    secondStep = DEFAULT_STEP,\n    segmentPlaceholder = DEFAULT_SEGMENT_PLACEHOLDER,\n    locale = DEFAULT_LOCALE,\n    name,\n    asChild,\n    disabled = false,\n    invalid = false,\n    readOnly = false,\n    required = false,\n    showSeconds = false,\n    className,\n    children,\n    id,\n    ...rootProps\n  } = props;\n\n  const instanceId = React.useId();\n  const rootId = id ?? instanceId;\n  const inputGroupId = React.useId();\n  const labelId = React.useId();\n  const triggerId = React.useId();\n\n  const inputGroupRef = React.useRef<InputGroupElement>(null);\n  const triggerRef = React.useRef<TriggerElement>(null);\n\n  const [inputGroup, setInputGroup] = React.useState<InputGroupElement | null>(\n    null,\n  );\n  const isFormControl = inputGroup ? !!inputGroup.closest(\"form\") : true;\n\n  const listenersRef = useLazyRef(() => new Set<() => void>());\n  const stateRef = useLazyRef<StoreState>(() => ({\n    value: valueProp ?? defaultValue ?? \"\",\n    open: open ?? defaultOpen ?? false,\n    openedViaFocus: false,\n  }));\n\n  const propsRef = useAsRef({ onValueChange, onOpenChange });\n\n  const store: Store = React.useMemo(() => {\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 === \"open\" && typeof value === \"boolean\") {\n          stateRef.current.open = value;\n          propsRef.current.onOpenChange?.(value);\n          if (!value) {\n            stateRef.current.openedViaFocus = false;\n          }\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 (open !== undefined) {\n      store.setState(\"open\", open);\n    }\n  }, [open]);\n\n  const storeOpen = useStore((state) => state.open, store);\n\n  const onPopoverOpenChange = React.useCallback(\n    (newOpen: boolean) => store.setState(\"open\", newOpen),\n    [store],\n  );\n\n  const is12Hour = React.useMemo(() => getIs12Hour(locale), [locale]);\n\n  const normalizedPlaceholder = React.useMemo(() => {\n    if (typeof segmentPlaceholder === \"string\") {\n      return {\n        hour: segmentPlaceholder,\n        minute: segmentPlaceholder,\n        second: segmentPlaceholder,\n        period: segmentPlaceholder,\n      };\n    }\n    return {\n      hour: segmentPlaceholder.hour ?? DEFAULT_SEGMENT_PLACEHOLDER,\n      minute: segmentPlaceholder.minute ?? DEFAULT_SEGMENT_PLACEHOLDER,\n      second: segmentPlaceholder.second ?? DEFAULT_SEGMENT_PLACEHOLDER,\n      period: segmentPlaceholder.period ?? DEFAULT_SEGMENT_PLACEHOLDER,\n    };\n  }, [segmentPlaceholder]);\n\n  const rootContext = React.useMemo<TimePickerContextValue>(\n    () => ({\n      id: rootId,\n      inputGroupId,\n      labelId,\n      triggerId,\n      inputGroupRef,\n      triggerRef,\n      openOnFocus,\n      inputGroupClickAction,\n      onInputGroupChange: setInputGroup,\n      disabled,\n      readOnly,\n      required,\n      invalid,\n      showSeconds,\n      is12Hour,\n      minuteStep,\n      secondStep,\n      hourStep,\n      segmentPlaceholder: normalizedPlaceholder,\n      min,\n      max,\n    }),\n    [\n      rootId,\n      inputGroupId,\n      labelId,\n      triggerId,\n      openOnFocus,\n      inputGroupClickAction,\n      disabled,\n      readOnly,\n      required,\n      invalid,\n      showSeconds,\n      is12Hour,\n      minuteStep,\n      secondStep,\n      hourStep,\n      normalizedPlaceholder,\n      min,\n      max,\n    ],\n  );\n\n  const RootPrimitive = asChild ? SlotPrimitive.Slot : \"div\";\n\n  return (\n    <>\n      <StoreContext.Provider value={store}>\n        <TimePickerContext.Provider value={rootContext}>\n          <Popover open={storeOpen} onOpenChange={onPopoverOpenChange}>\n            <RootPrimitive\n              data-slot=\"time-picker\"\n              data-disabled={disabled ? \"\" : undefined}\n              data-invalid={invalid ? \"\" : undefined}\n              {...rootProps}\n              className={cn(\"relative\", className)}\n            >\n              {children}\n            </RootPrimitive>\n          </Popover>\n        </TimePickerContext.Provider>\n      </StoreContext.Provider>\n      {isFormControl && (\n        <VisuallyHiddenInput\n          type=\"hidden\"\n          control={inputGroup}\n          name={name}\n          value={value}\n          disabled={disabled}\n          readOnly={readOnly}\n          required={required}\n        />\n      )}\n    </>\n  );\n}\n\ninterface TimePickerLabelProps extends React.ComponentProps<\"label\"> {\n  asChild?: boolean;\n}\n\nfunction TimePickerLabel(props: TimePickerLabelProps) {\n  const { asChild, className, ...labelProps } = props;\n\n  const { labelId } = useTimePickerContext(LABEL_NAME);\n\n  const LabelPrimitive = asChild ? SlotPrimitive.Slot : \"label\";\n\n  return (\n    <LabelPrimitive\n      data-slot=\"time-picker-label\"\n      {...labelProps}\n      htmlFor={labelId}\n      className={cn(\n        \"text-sm leading-none font-medium peer-disabled:cursor-not-allowed peer-disabled:opacity-70\",\n        className,\n      )}\n    />\n  );\n}\n\ninterface TimePickerInputGroupContextValue {\n  onInputRegister: (\n    segment: Segment,\n    ref: React.RefObject<InputElement | null>,\n  ) => void;\n  onInputUnregister: (segment: Segment) => void;\n  getNextInput: (\n    currentSegment: Segment,\n  ) => React.RefObject<InputElement | null> | null;\n}\n\nconst TimePickerInputGroupContext =\n  React.createContext<TimePickerInputGroupContextValue | null>(null);\n\nfunction useTimePickerInputGroupContext(consumerName: string) {\n  const context = React.useContext(TimePickerInputGroupContext);\n  if (!context) {\n    throw new Error(\n      `\\`${consumerName}\\` must be used within \\`${INPUT_GROUP_NAME}\\``,\n    );\n  }\n  return context;\n}\n\nfunction TimePickerInputGroup(props: DivProps) {\n  const {\n    onPointerDown: onPointerDownProp,\n    onClick: onClickProp,\n    asChild,\n    className,\n    style,\n    ref,\n    ...inputGroupProps\n  } = props;\n\n  const {\n    inputGroupId,\n    labelId,\n    onInputGroupChange,\n    disabled,\n    readOnly,\n    invalid,\n    segmentPlaceholder,\n    inputGroupRef,\n    triggerRef,\n    inputGroupClickAction,\n  } = useTimePickerContext(INPUT_GROUP_NAME);\n\n  const store = useStoreContext(INPUT_GROUP_NAME);\n\n  const composedRef = useComposedRefs(ref, inputGroupRef, onInputGroupChange);\n\n  const inputRefsMap = React.useRef<\n    Map<Segment, React.RefObject<InputElement | null>>\n  >(new Map());\n\n  const onInputRegister = React.useCallback(\n    (segment: Segment, ref: React.RefObject<InputElement | null>) => {\n      inputRefsMap.current.set(segment, ref);\n    },\n    [],\n  );\n\n  const onInputUnregister = React.useCallback((segment: Segment) => {\n    inputRefsMap.current.delete(segment);\n  }, []);\n\n  const getNextInput = React.useCallback(\n    (currentSegment: Segment): React.RefObject<InputElement | null> | null => {\n      const segmentOrder: Segment[] = [\"hour\", \"minute\", \"second\", \"period\"];\n      const currentIndex = segmentOrder.indexOf(currentSegment);\n\n      if (currentIndex === -1 || currentIndex === segmentOrder.length - 1) {\n        return null;\n      }\n\n      for (let i = currentIndex + 1; i < segmentOrder.length; i++) {\n        const nextSegment = segmentOrder[i];\n        if (nextSegment) {\n          const nextRef = inputRefsMap.current.get(nextSegment);\n          if (nextRef?.current) {\n            return nextRef;\n          }\n        }\n      }\n\n      return null;\n    },\n    [],\n  );\n\n  const onPointerDown = React.useCallback(\n    (event: React.PointerEvent<InputGroupElement>) => {\n      onPointerDownProp?.(event);\n      if (disabled || readOnly || event.defaultPrevented) return;\n\n      const target = event.target as HTMLElement;\n\n      if (target.tagName === \"INPUT\" || target.closest(\"input\")) {\n        return;\n      }\n\n      if (triggerRef.current?.contains(target)) {\n        return;\n      }\n\n      event.preventDefault();\n    },\n    [onPointerDownProp, disabled, readOnly, triggerRef],\n  );\n\n  const onClick = React.useCallback(\n    (event: React.MouseEvent<InputGroupElement>) => {\n      onClickProp?.(event);\n      if (disabled || readOnly || event.defaultPrevented) return;\n\n      const target = event.target as HTMLElement;\n\n      if (target.tagName === \"INPUT\" || target.closest(\"input\")) {\n        return;\n      }\n\n      if (triggerRef.current?.contains(target)) {\n        return;\n      }\n\n      if (inputGroupClickAction === \"open\") {\n        store.setState(\"open\", true);\n      } else {\n        const activeElement = document.activeElement;\n        const isInputAlreadyFocused =\n          activeElement &&\n          activeElement.tagName === \"INPUT\" &&\n          inputGroupRef.current?.contains(activeElement);\n\n        if (!isInputAlreadyFocused) {\n          for (const segment of SEGMENTS) {\n            const inputRef = inputRefsMap.current.get(segment);\n            if (inputRef?.current) {\n              inputRef.current.focus();\n              inputRef.current.select();\n              break;\n            }\n          }\n        }\n      }\n    },\n    [\n      onClickProp,\n      disabled,\n      readOnly,\n      inputGroupClickAction,\n      store,\n      triggerRef,\n      inputGroupRef,\n    ],\n  );\n\n  const inputGroupContextValue =\n    React.useMemo<TimePickerInputGroupContextValue>(\n      () => ({\n        onInputRegister,\n        onInputUnregister,\n        getNextInput,\n      }),\n      [onInputRegister, onInputUnregister, getNextInput],\n    );\n\n  const InputGroupPrimitive = asChild ? SlotPrimitive.Slot : \"div\";\n\n  return (\n    <TimePickerInputGroupContext.Provider value={inputGroupContextValue}>\n      <PopoverAnchor asChild>\n        <InputGroupPrimitive\n          role=\"group\"\n          id={inputGroupId}\n          aria-labelledby={labelId}\n          data-slot=\"time-picker-input-group\"\n          data-disabled={disabled ? \"\" : undefined}\n          data-invalid={invalid ? \"\" : undefined}\n          {...inputGroupProps}\n          className={cn(\n            \"flex h-10 w-full cursor-text items-center gap-0.5 rounded-md border border-input bg-background px-3 py-2 shadow-xs transition-shadow outline-none\",\n            \"has-[input:focus]:border-ring has-[input:focus]:ring-[3px] has-[input:focus]:ring-ring/50\",\n            invalid && \"border-destructive ring-destructive/20\",\n            disabled && \"cursor-not-allowed opacity-50\",\n            className,\n          )}\n          style={\n            {\n              \"--time-picker-hour-input-width\": `${segmentPlaceholder.hour.length}ch`,\n              \"--time-picker-minute-input-width\": `${segmentPlaceholder.minute.length}ch`,\n              \"--time-picker-second-input-width\": `${segmentPlaceholder.second.length}ch`,\n              \"--time-picker-period-input-width\": `${Math.max(segmentPlaceholder.period.length, 2) + 0.5}ch`,\n              ...style,\n            } as React.CSSProperties\n          }\n          ref={composedRef}\n          onPointerDown={onPointerDown}\n          onClick={onClick}\n        />\n      </PopoverAnchor>\n    </TimePickerInputGroupContext.Provider>\n  );\n}\n\ninterface TimePickerInputProps extends Omit<\n  React.ComponentProps<\"input\">,\n  \"type\" | \"value\"\n> {\n  segment: Segment;\n}\n\nfunction TimePickerInput(props: TimePickerInputProps) {\n  const {\n    segment,\n    onBlur: onBlurProp,\n    onChange: onChangeProp,\n    onClick: onClickProp,\n    onFocus: onFocusProp,\n    onKeyDown: onKeyDownProp,\n    disabled: disabledProp,\n    readOnly: readOnlyProp,\n    className,\n    style,\n    ref,\n    ...inputProps\n  } = props;\n\n  const {\n    is12Hour,\n    showSeconds,\n    disabled,\n    readOnly,\n    segmentPlaceholder,\n    openOnFocus,\n  } = useTimePickerContext(INPUT_NAME);\n  const store = useStoreContext(INPUT_NAME);\n  const inputGroupContext = useTimePickerInputGroupContext(INPUT_NAME);\n\n  const isDisabled = disabledProp || disabled;\n  const isReadOnly = readOnlyProp || readOnly;\n\n  const value = useStore((state) => state.value);\n  const timeValue = parseTimeString(value);\n\n  const inputRef = React.useRef<HTMLInputElement>(null);\n  const composedRef = useComposedRefs(ref, inputRef);\n\n  useIsomorphicLayoutEffect(() => {\n    if (segment) {\n      inputGroupContext.onInputRegister(segment as Segment, inputRef);\n      return () => inputGroupContext.onInputUnregister(segment as Segment);\n    }\n  }, [inputGroupContext, segment]);\n\n  const getSegmentValue = React.useCallback(() => {\n    if (!timeValue) {\n      if (!segment) return \"\";\n      return segmentPlaceholder[segment];\n    }\n    switch (segment) {\n      case \"hour\": {\n        if (timeValue.hour === undefined) return segmentPlaceholder.hour;\n        if (is12Hour) {\n          return to12Hour(timeValue.hour).hour.toString().padStart(2, \"0\");\n        }\n        return timeValue.hour.toString().padStart(2, \"0\");\n      }\n      case \"minute\":\n        if (timeValue.minute === undefined) return segmentPlaceholder.minute;\n        return timeValue.minute.toString().padStart(2, \"0\");\n      case \"second\":\n        if (timeValue.second === undefined) return segmentPlaceholder.second;\n        return timeValue.second.toString().padStart(2, \"0\");\n      case \"period\": {\n        if (!timeValue || timeValue.hour === undefined)\n          return segmentPlaceholder.period;\n        return to12Hour(timeValue.hour).period;\n      }\n      default:\n        return \"\";\n    }\n  }, [timeValue, segment, is12Hour, segmentPlaceholder]);\n\n  const [editValue, setEditValue] = React.useState(getSegmentValue());\n  const [isEditing, setIsEditing] = React.useState(false);\n  const [pendingDigit, setPendingDigit] = React.useState<string | null>(null);\n\n  React.useEffect(() => {\n    if (!isEditing) {\n      setEditValue(getSegmentValue());\n      setPendingDigit(null);\n    }\n  }, [getSegmentValue, isEditing]);\n\n  const updateTimeValue = React.useCallback(\n    (newSegmentValue: string | undefined, shouldCreateIfEmpty = false) => {\n      const placeholder = segment\n        ? segmentPlaceholder[segment]\n        : DEFAULT_SEGMENT_PLACEHOLDER;\n      if (!newSegmentValue || newSegmentValue === placeholder) return;\n      if (!timeValue && !shouldCreateIfEmpty) return;\n\n      const currentTime = timeValue ?? {};\n      const newTime = { ...currentTime };\n\n      switch (segment) {\n        case \"hour\": {\n          const displayHour = Number.parseInt(newSegmentValue, 10);\n          if (!Number.isNaN(displayHour)) {\n            if (is12Hour) {\n              const clampedHour = clamp(displayHour, 1, 12);\n              let currentPeriod: Period;\n              if (timeValue?.period !== undefined) {\n                currentPeriod = timeValue.period;\n              } else if (timeValue?.hour !== undefined) {\n                currentPeriod = to12Hour(timeValue.hour).period;\n              } else {\n                const now = new Date();\n                currentPeriod = to12Hour(now.getHours()).period;\n              }\n              const hour24 = to24Hour(clampedHour, currentPeriod);\n              newTime.hour = hour24;\n              if (timeValue?.period !== undefined) {\n                newTime.period = timeValue.period;\n              }\n            } else {\n              newTime.hour = clamp(displayHour, 0, 23);\n            }\n          }\n          break;\n        }\n        case \"minute\": {\n          const minute = Number.parseInt(newSegmentValue, 10);\n          if (!Number.isNaN(minute)) {\n            newTime.minute = clamp(minute, 0, 59);\n          }\n          break;\n        }\n        case \"second\": {\n          const second = Number.parseInt(newSegmentValue, 10);\n          if (!Number.isNaN(second)) {\n            newTime.second = clamp(second, 0, 59);\n          }\n          break;\n        }\n        case \"period\": {\n          if (newSegmentValue === \"AM\" || newSegmentValue === \"PM\") {\n            newTime.period = newSegmentValue;\n            if (timeValue && timeValue.hour !== undefined) {\n              const currentDisplay = to12Hour(timeValue.hour);\n              newTime.hour = to24Hour(currentDisplay.hour, newSegmentValue);\n            }\n          }\n          break;\n        }\n      }\n\n      const newValue = formatTimeValue(newTime, showSeconds);\n      store.setState(\"value\", newValue);\n    },\n    [timeValue, segment, is12Hour, showSeconds, store, segmentPlaceholder],\n  );\n\n  const onBlur = React.useCallback(\n    (event: React.FocusEvent<InputElement>) => {\n      onBlurProp?.(event);\n      if (event.defaultPrevented) return;\n\n      setIsEditing(false);\n\n      const placeholder = segment\n        ? segmentPlaceholder[segment]\n        : DEFAULT_SEGMENT_PLACEHOLDER;\n      if (editValue && editValue !== placeholder && editValue.length > 0) {\n        let valueToUpdate = editValue;\n\n        if (segment !== \"period\") {\n          if (editValue.length === 2) {\n            valueToUpdate = editValue;\n          } else if (editValue.length === 1) {\n            const numValue = Number.parseInt(editValue, 10);\n            if (!Number.isNaN(numValue)) {\n              valueToUpdate = numValue.toString().padStart(2, \"0\");\n            }\n          }\n        }\n\n        updateTimeValue(valueToUpdate, true);\n\n        queueMicrotask(() => {\n          const currentTimeValue = parseTimeString(store.getState().value);\n          if (currentTimeValue) {\n            const now = new Date();\n            const newTime = { ...currentTimeValue };\n            let needsUpdate = false;\n\n            if (newTime.hour === undefined) {\n              newTime.hour = now.getHours();\n              needsUpdate = true;\n            }\n\n            if (newTime.minute === undefined) {\n              newTime.minute = now.getMinutes();\n              needsUpdate = true;\n            }\n\n            if (showSeconds && newTime.second === undefined) {\n              newTime.second = now.getSeconds();\n              needsUpdate = true;\n            }\n\n            if (needsUpdate) {\n              const newValue = formatTimeValue(newTime, showSeconds);\n              store.setState(\"value\", newValue);\n            }\n          }\n        });\n      }\n\n      setEditValue(getSegmentValue());\n      setPendingDigit(null);\n    },\n    [\n      onBlurProp,\n      editValue,\n      updateTimeValue,\n      getSegmentValue,\n      segment,\n      segmentPlaceholder,\n      showSeconds,\n      store,\n    ],\n  );\n\n  const onChange = React.useCallback(\n    (event: React.ChangeEvent<InputElement>) => {\n      onChangeProp?.(event);\n      if (event.defaultPrevented) return;\n\n      let newValue = event.target.value;\n\n      const placeholder = segment\n        ? segmentPlaceholder[segment]\n        : DEFAULT_SEGMENT_PLACEHOLDER;\n      if (\n        editValue === placeholder &&\n        newValue.length > 0 &&\n        newValue !== placeholder\n      ) {\n        newValue = newValue.replace(new RegExp(`^${placeholder}`), \"\");\n      }\n\n      if (segment === \"period\") {\n        const firstChar = newValue.charAt(0).toUpperCase();\n        let newPeriod: Period | null = null;\n\n        if (firstChar === \"A\" || firstChar === \"1\") {\n          newPeriod = \"AM\";\n        } else if (firstChar === \"P\" || firstChar === \"2\") {\n          newPeriod = \"PM\";\n        }\n\n        if (newPeriod) {\n          setEditValue(newPeriod);\n          updateTimeValue(newPeriod, true);\n          queueMicrotask(() => {\n            inputRef.current?.select();\n          });\n        }\n        return;\n      }\n\n      if (segment === \"hour\" || segment === \"minute\" || segment === \"second\") {\n        newValue = newValue.replace(/\\D/g, \"\");\n      }\n\n      if (newValue.length > 2) {\n        newValue = newValue.slice(0, 2);\n      }\n      if (segment === \"hour\" || segment === \"minute\" || segment === \"second\") {\n        const numValue = Number.parseInt(newValue, 10);\n\n        if (!Number.isNaN(numValue) && newValue.length > 0) {\n          if (pendingDigit !== null && newValue.length === 1) {\n            const twoDigitValue = pendingDigit + newValue;\n            const combinedNum = Number.parseInt(twoDigitValue, 10);\n\n            if (!Number.isNaN(combinedNum)) {\n              const paddedValue = combinedNum.toString().padStart(2, \"0\");\n              setEditValue(paddedValue);\n              updateTimeValue(paddedValue, true);\n              setPendingDigit(null);\n\n              queueMicrotask(() => {\n                if (segment) {\n                  const nextInputRef = inputGroupContext.getNextInput(segment);\n                  if (nextInputRef?.current) {\n                    nextInputRef.current.focus();\n                    nextInputRef.current.select();\n                  }\n                }\n              });\n              return;\n            }\n          }\n\n          const maxFirstDigit = segment === \"hour\" ? (is12Hour ? 1 : 2) : 5;\n\n          const firstDigit = Number.parseInt(newValue[0] ?? \"0\", 10);\n          const shouldAutoAdvance = firstDigit > maxFirstDigit;\n\n          if (newValue.length === 1) {\n            if (shouldAutoAdvance) {\n              const paddedValue = numValue.toString().padStart(2, \"0\");\n              setEditValue(paddedValue);\n              updateTimeValue(paddedValue, true);\n              setPendingDigit(null);\n\n              queueMicrotask(() => {\n                if (segment) {\n                  const nextInputRef = inputGroupContext.getNextInput(segment);\n                  if (nextInputRef?.current) {\n                    nextInputRef.current.focus();\n                    nextInputRef.current.select();\n                  }\n                }\n              });\n            } else {\n              const paddedValue = numValue.toString().padStart(2, \"0\");\n              setEditValue(paddedValue);\n              setPendingDigit(newValue);\n              queueMicrotask(() => {\n                inputRef.current?.select();\n              });\n            }\n          } else if (newValue.length === 2) {\n            const paddedValue = numValue.toString().padStart(2, \"0\");\n            setEditValue(paddedValue);\n            updateTimeValue(paddedValue, true);\n            setPendingDigit(null);\n\n            queueMicrotask(() => {\n              if (segment) {\n                const nextInputRef = inputGroupContext.getNextInput(segment);\n                if (nextInputRef?.current) {\n                  nextInputRef.current.focus();\n                  nextInputRef.current.select();\n                }\n              }\n            });\n          }\n        } else if (newValue.length === 0) {\n          setEditValue(\"\");\n          setPendingDigit(null);\n        }\n      }\n    },\n    [\n      segment,\n      updateTimeValue,\n      onChangeProp,\n      editValue,\n      is12Hour,\n      inputGroupContext,\n      pendingDigit,\n      segmentPlaceholder,\n    ],\n  );\n\n  const onClick = React.useCallback(\n    (event: React.MouseEvent<InputElement>) => {\n      onClickProp?.(event);\n      if (event.defaultPrevented) return;\n\n      event.currentTarget.select();\n    },\n    [onClickProp],\n  );\n\n  const onFocus = React.useCallback(\n    (event: React.FocusEvent<InputElement>) => {\n      onFocusProp?.(event);\n      if (event.defaultPrevented) return;\n\n      setIsEditing(true);\n      setPendingDigit(null);\n\n      if (openOnFocus && !store.getState().open) {\n        store.setState(\"openedViaFocus\", true);\n        store.setState(\"open\", true);\n      }\n\n      queueMicrotask(() => event.target.select());\n    },\n    [onFocusProp, openOnFocus, store],\n  );\n\n  const onKeyDown = React.useCallback(\n    (event: React.KeyboardEvent<InputElement>) => {\n      onKeyDownProp?.(event);\n      if (event.defaultPrevented) return;\n\n      if (event.key === \"ArrowLeft\" || event.key === \"ArrowRight\") {\n        event.preventDefault();\n\n        const goToPrevious = event.key === \"ArrowLeft\";\n        const inputGroup = inputRef.current?.closest(\n          '[data-slot=\"time-picker-input-group\"]',\n        );\n\n        if (inputGroup && inputRef.current) {\n          const allInputs = Array.from(\n            inputGroup.querySelectorAll('input[type=\"text\"]'),\n          ) as HTMLInputElement[];\n          const currentIdx = allInputs.indexOf(inputRef.current);\n\n          if (currentIdx !== -1) {\n            const targetIdx = goToPrevious\n              ? Math.max(0, currentIdx - 1)\n              : Math.min(allInputs.length - 1, currentIdx + 1);\n\n            const targetInput = allInputs[targetIdx];\n            if (targetInput && targetInput !== inputRef.current) {\n              targetInput.focus();\n              targetInput.select();\n            }\n          }\n        }\n        return;\n      }\n\n      if (event.key === \"Backspace\" || event.key === \"Delete\") {\n        const input = inputRef.current;\n        if (\n          input &&\n          input.selectionStart === 0 &&\n          input.selectionEnd === input.value.length\n        ) {\n          event.preventDefault();\n          const placeholder = segment\n            ? segmentPlaceholder[segment]\n            : DEFAULT_SEGMENT_PLACEHOLDER;\n          setEditValue(placeholder);\n          setPendingDigit(null);\n\n          if (timeValue) {\n            const newTime = { ...timeValue };\n            switch (segment) {\n              case \"hour\":\n                delete newTime.hour;\n                break;\n              case \"minute\":\n                delete newTime.minute;\n                break;\n              case \"second\":\n                delete newTime.second;\n                break;\n              case \"period\":\n                delete newTime.period;\n                break;\n            }\n\n            if (\n              newTime.hour !== undefined ||\n              newTime.minute !== undefined ||\n              newTime.second !== undefined ||\n              newTime.period !== undefined\n            ) {\n              const newValue = formatTimeValue(newTime, showSeconds);\n              store.setState(\"value\", newValue);\n            } else {\n              store.setState(\"value\", \"\");\n            }\n          } else {\n            store.setState(\"value\", \"\");\n          }\n\n          queueMicrotask(() => {\n            inputRef.current?.select();\n          });\n          return;\n        }\n      }\n\n      if (segment === \"period\") {\n        const key = event.key.toLowerCase();\n        if (key === \"a\" || key === \"p\" || key === \"1\" || key === \"2\") {\n          event.preventDefault();\n          let newPeriod: Period;\n          if (key === \"a\" || key === \"1\") {\n            newPeriod = \"AM\";\n          } else {\n            newPeriod = \"PM\";\n          }\n          setEditValue(newPeriod);\n          updateTimeValue(newPeriod, true);\n          queueMicrotask(() => {\n            inputRef.current?.select();\n          });\n        } else if (event.key === \"ArrowUp\" || event.key === \"ArrowDown\") {\n          event.preventDefault();\n          const placeholder = segmentPlaceholder.period;\n          const currentPeriod =\n            editValue === placeholder || editValue === \"\" ? \"AM\" : editValue;\n          const newPeriod =\n            currentPeriod === \"AM\" || currentPeriod === \"A\" ? \"PM\" : \"AM\";\n          setEditValue(newPeriod);\n          updateTimeValue(newPeriod, true);\n          queueMicrotask(() => {\n            inputRef.current?.select();\n          });\n        }\n        return;\n      }\n\n      if (event.key === \"Tab\") {\n        const placeholder = segment\n          ? segmentPlaceholder[segment]\n          : DEFAULT_SEGMENT_PLACEHOLDER;\n        if (editValue && editValue.length > 0 && editValue !== placeholder) {\n          if (editValue.length === 2) {\n            updateTimeValue(editValue, true);\n          } else if (editValue.length === 1) {\n            const numValue = Number.parseInt(editValue, 10);\n            if (!Number.isNaN(numValue)) {\n              const paddedValue = numValue.toString().padStart(2, \"0\");\n              updateTimeValue(paddedValue, true);\n            }\n          }\n        }\n        return;\n      }\n\n      if (event.key === \"Enter\") {\n        event.preventDefault();\n        const placeholder = segment\n          ? segmentPlaceholder[segment]\n          : DEFAULT_SEGMENT_PLACEHOLDER;\n        if (editValue && editValue.length > 0 && editValue !== placeholder) {\n          if (editValue.length === 2) {\n            updateTimeValue(editValue, true);\n          } else if (editValue.length === 1) {\n            const numValue = Number.parseInt(editValue, 10);\n            if (!Number.isNaN(numValue)) {\n              const paddedValue = numValue.toString().padStart(2, \"0\");\n              updateTimeValue(paddedValue, true);\n            }\n          }\n        }\n        queueMicrotask(() => {\n          inputRef.current?.select();\n        });\n      }\n\n      if (event.key === \"Escape\") {\n        event.preventDefault();\n        setEditValue(getSegmentValue());\n        inputRef.current?.blur();\n      }\n\n      if (event.key === \"ArrowUp\") {\n        event.preventDefault();\n        const placeholder = segment\n          ? segmentPlaceholder[segment]\n          : DEFAULT_SEGMENT_PLACEHOLDER;\n        if (editValue === placeholder || editValue === \"\") {\n          const defaultValue = segment === \"hour\" ? (is12Hour ? 12 : 0) : 0;\n          const formattedValue = defaultValue.toString().padStart(2, \"0\");\n          setEditValue(formattedValue);\n          updateTimeValue(formattedValue, true);\n          queueMicrotask(() => {\n            inputRef.current?.select();\n          });\n          return;\n        }\n        const currentValue = Number.parseInt(editValue, 10);\n        if (!Number.isNaN(currentValue)) {\n          let newValue: number;\n          switch (segment) {\n            case \"hour\":\n              if (is12Hour) {\n                newValue = currentValue === 12 ? 1 : currentValue + 1;\n              } else {\n                newValue = currentValue === 23 ? 0 : currentValue + 1;\n              }\n              break;\n            case \"minute\":\n            case \"second\":\n              newValue = currentValue === 59 ? 0 : currentValue + 1;\n              break;\n            default:\n              return;\n          }\n          const formattedValue = newValue.toString().padStart(2, \"0\");\n          setEditValue(formattedValue);\n          updateTimeValue(formattedValue, true);\n          queueMicrotask(() => {\n            inputRef.current?.select();\n          });\n        }\n      }\n\n      if (event.key === \"ArrowDown\") {\n        event.preventDefault();\n        const placeholder = segment\n          ? segmentPlaceholder[segment]\n          : DEFAULT_SEGMENT_PLACEHOLDER;\n        if (editValue === placeholder || editValue === \"\") {\n          const defaultValue = segment === \"hour\" ? (is12Hour ? 12 : 23) : 59;\n          const formattedValue = defaultValue.toString().padStart(2, \"0\");\n          setEditValue(formattedValue);\n          updateTimeValue(formattedValue, true);\n          queueMicrotask(() => {\n            inputRef.current?.select();\n          });\n          return;\n        }\n        const currentValue = Number.parseInt(editValue, 10);\n        if (!Number.isNaN(currentValue)) {\n          let newValue: number;\n          switch (segment) {\n            case \"hour\":\n              if (is12Hour) {\n                newValue = currentValue === 1 ? 12 : currentValue - 1;\n              } else {\n                newValue = currentValue === 0 ? 23 : currentValue - 1;\n              }\n              break;\n            case \"minute\":\n            case \"second\":\n              newValue = currentValue === 0 ? 59 : currentValue - 1;\n              break;\n            default:\n              return;\n          }\n          const formattedValue = newValue.toString().padStart(2, \"0\");\n          setEditValue(formattedValue);\n          updateTimeValue(formattedValue, true);\n          queueMicrotask(() => {\n            inputRef.current?.select();\n          });\n        }\n      }\n    },\n    [\n      onKeyDownProp,\n      editValue,\n      segment,\n      is12Hour,\n      getSegmentValue,\n      updateTimeValue,\n      showSeconds,\n      timeValue,\n      store,\n      segmentPlaceholder,\n    ],\n  );\n\n  const displayValue = isEditing ? editValue : getSegmentValue();\n\n  const segmentWidth = segment\n    ? `var(--time-picker-${segment}-input-width)`\n    : \"2ch\";\n\n  return (\n    <input\n      type=\"text\"\n      inputMode={segment === \"period\" ? \"text\" : \"numeric\"}\n      autoComplete=\"off\"\n      autoCorrect=\"off\"\n      autoCapitalize=\"off\"\n      spellCheck={false}\n      translate=\"no\"\n      {...inputProps}\n      disabled={isDisabled}\n      readOnly={isReadOnly}\n      className={cn(\n        \"inline-flex h-full items-center justify-center border-0 bg-transparent text-center text-sm tabular-nums transition-colors outline-none focus:bg-transparent disabled:cursor-not-allowed disabled:opacity-50\",\n        className,\n      )}\n      style={{ width: segmentWidth, ...style }}\n      ref={composedRef}\n      value={displayValue}\n      onBlur={onBlur}\n      onChange={onChange}\n      onClick={onClick}\n      onFocus={onFocus}\n      onKeyDown={onKeyDown}\n    />\n  );\n}\n\nfunction TimePickerTrigger(props: ButtonProps) {\n  const {\n    className,\n    children,\n    disabled: disabledProp,\n    ref,\n    ...triggerProps\n  } = props;\n\n  const { triggerId, disabled, triggerRef } =\n    useTimePickerContext(TRIGGER_NAME);\n\n  const isDisabled = disabledProp || disabled;\n\n  const composedRef = useComposedRefs(ref, triggerRef);\n\n  return (\n    <PopoverTrigger\n      type=\"button\"\n      id={triggerId}\n      data-slot=\"time-picker-trigger\"\n      disabled={isDisabled}\n      ref={composedRef}\n      {...triggerProps}\n      className={cn(\n        \"ml-auto flex items-center text-muted-foreground transition-colors hover:text-foreground disabled:pointer-events-none [&>svg:not([class*='size-'])]:size-4\",\n        className,\n      )}\n    >\n      {children ?? (\n        <IconPlaceholder\n          lucide=\"Clock\"\n          tabler=\"IconClock\"\n          hugeicons=\"Clock01Icon\"\n          phosphor=\"ClockIcon\"\n          remixicon=\"RiTimeLine\"\n        />\n      )}\n    </PopoverTrigger>\n  );\n}\n\ninterface TimePickerGroupContextValue {\n  getColumns: () => ColumnData[];\n  onColumnRegister: (column: ColumnData) => void;\n  onColumnUnregister: (id: string) => void;\n}\n\nconst TimePickerGroupContext =\n  React.createContext<TimePickerGroupContextValue | null>(null);\n\nfunction useTimePickerGroupContext(consumerName: string) {\n  const context = React.useContext(TimePickerGroupContext);\n  if (!context) {\n    throw new Error(`\\`${consumerName}\\` must be used within \\`${ROOT_NAME}\\``);\n  }\n  return context;\n}\n\ninterface TimePickerContentProps\n  extends DivProps, React.ComponentProps<typeof PopoverContent> {}\n\nfunction TimePickerContent(props: TimePickerContentProps) {\n  const {\n    side = \"bottom\",\n    align = \"start\",\n    sideOffset = 6,\n    className,\n    onOpenAutoFocus: onOpenAutoFocusProp,\n    onInteractOutside: onInteractOutsideProp,\n    ...contentProps\n  } = props;\n\n  const store = useStoreContext(CONTENT_NAME);\n  const { openOnFocus, inputGroupRef } = useTimePickerContext(CONTENT_NAME);\n  const columnsRef = React.useRef<Map<string, Omit<ColumnData, \"id\">>>(\n    new Map(),\n  );\n\n  const onColumnRegister = React.useCallback((column: ColumnData) => {\n    columnsRef.current.set(column.id, column);\n  }, []);\n\n  const onColumnUnregister = React.useCallback((id: string) => {\n    columnsRef.current.delete(id);\n  }, []);\n\n  const getColumns = React.useCallback(() => {\n    const columns = Array.from(columnsRef.current.entries())\n      .map(([id, { ref, getSelectedItemRef, getItems }]) => ({\n        id,\n        ref,\n        getSelectedItemRef,\n        getItems,\n      }))\n      .filter((c) => c.ref.current !== null);\n    return sortNodes(columns);\n  }, []);\n\n  const groupContextValue = React.useMemo<TimePickerGroupContextValue>(\n    () => ({\n      getColumns,\n      onColumnRegister,\n      onColumnUnregister,\n    }),\n    [getColumns, onColumnRegister, onColumnUnregister],\n  );\n\n  const onOpenAutoFocus: NonNullable<PopoverContentProps[\"onOpenAutoFocus\"]> =\n    React.useCallback(\n      (event) => {\n        onOpenAutoFocusProp?.(event);\n        if (event.defaultPrevented) return;\n\n        event.preventDefault();\n\n        const { openedViaFocus } = store.getState();\n\n        if (openedViaFocus) {\n          store.setState(\"openedViaFocus\", false);\n          return;\n        }\n\n        const columns = getColumns();\n        const firstColumn = columns[0];\n\n        if (!firstColumn) return;\n\n        const items = firstColumn.getItems();\n        const selectedItem = items.find((item) => item.selected);\n\n        const candidateRefs = selectedItem\n          ? [selectedItem.ref, ...items.map((item) => item.ref)]\n          : items.map((item) => item.ref);\n\n        focusFirst(candidateRefs, false);\n      },\n      [onOpenAutoFocusProp, getColumns, store],\n    );\n\n  const onInteractOutside: NonNullable<\n    PopoverContentProps[\"onInteractOutside\"]\n  > = React.useCallback(\n    (event) => {\n      onInteractOutsideProp?.(event);\n      if (event.defaultPrevented) return;\n\n      if (openOnFocus && inputGroupRef.current) {\n        const target = event.target;\n        if (!(target instanceof Node)) return;\n        const isInsideInputGroup = inputGroupRef.current.contains(target);\n\n        if (isInsideInputGroup) {\n          event.preventDefault();\n        }\n      }\n    },\n    [onInteractOutsideProp, openOnFocus, inputGroupRef],\n  );\n\n  return (\n    <TimePickerGroupContext.Provider value={groupContextValue}>\n      <PopoverContent\n        data-slot=\"time-picker-content\"\n        side={side}\n        align={align}\n        sideOffset={sideOffset}\n        {...contentProps}\n        className={cn(\n          \"flex w-auto max-w-(--radix-popover-trigger-width) p-0\",\n          className,\n        )}\n        onOpenAutoFocus={onOpenAutoFocus}\n        onInteractOutside={onInteractOutside}\n      />\n    </TimePickerGroupContext.Provider>\n  );\n}\n\ninterface TimePickerColumnContextValue {\n  getItems: () => ItemData[];\n  onItemRegister: (\n    value: number | string,\n    ref: React.RefObject<ColumnItemElement | null>,\n    selected: boolean,\n  ) => void;\n  onItemUnregister: (value: number | string) => void;\n}\n\nconst TimePickerColumnContext =\n  React.createContext<TimePickerColumnContextValue | null>(null);\n\nfunction useTimePickerColumnContext(consumerName: string) {\n  const context = React.useContext(TimePickerColumnContext);\n  if (!context) {\n    throw new Error(`\\`${consumerName}\\` must be used within a column`);\n  }\n  return context;\n}\n\ninterface TimePickerColumnProps extends DivProps {}\n\nfunction TimePickerColumn(props: TimePickerColumnProps) {\n  const { children, className, ref, ...columnProps } = props;\n\n  const columnId = React.useId();\n  const columnRef = React.useRef<ColumnElement | null>(null);\n  const composedRef = useComposedRefs(ref, columnRef);\n\n  const itemsRef = React.useRef<\n    Map<\n      number | string,\n      {\n        ref: React.RefObject<ColumnItemElement | null>;\n        selected: boolean;\n      }\n    >\n  >(new Map());\n\n  const groupContext = useTimePickerGroupContext(COLUMN_NAME);\n\n  const onItemRegister = React.useCallback(\n    (\n      value: number | string,\n      ref: React.RefObject<HTMLButtonElement | null>,\n      selected: boolean,\n    ) => {\n      itemsRef.current.set(value, { ref, selected });\n    },\n    [],\n  );\n\n  const onItemUnregister = React.useCallback((value: number | string) => {\n    itemsRef.current.delete(value);\n  }, []);\n\n  const getItems = React.useCallback(() => {\n    const items = Array.from(itemsRef.current.entries())\n      .map(([value, { ref, selected }]) => ({\n        value,\n        ref,\n        selected,\n      }))\n      .filter((item) => item.ref.current);\n    return sortNodes(items);\n  }, []);\n\n  const getSelectedItemRef = React.useCallback(() => {\n    const items = getItems();\n    return items.find((item) => item.selected)?.ref ?? null;\n  }, [getItems]);\n\n  useIsomorphicLayoutEffect(() => {\n    groupContext.onColumnRegister({\n      id: columnId,\n      ref: columnRef,\n      getSelectedItemRef,\n      getItems,\n    });\n    return () => groupContext.onColumnUnregister(columnId);\n  }, [groupContext, columnId, getSelectedItemRef, getItems]);\n\n  const columnContextValue = React.useMemo<TimePickerColumnContextValue>(\n    () => ({\n      getItems,\n      onItemRegister,\n      onItemUnregister,\n    }),\n    [getItems, onItemRegister, onItemUnregister],\n  );\n\n  return (\n    <TimePickerColumnContext.Provider value={columnContextValue}>\n      <div\n        ref={composedRef}\n        data-slot=\"time-picker-column\"\n        {...columnProps}\n        className={cn(\"flex flex-col gap-1 p-1 not-last:border-r\", className)}\n      >\n        {children}\n      </div>\n    </TimePickerColumnContext.Provider>\n  );\n}\n\ninterface TimePickerColumnItemProps extends ButtonProps {\n  value: number | string;\n  selected?: boolean;\n  format?: SegmentFormat;\n}\n\nfunction TimePickerColumnItem(props: TimePickerColumnItemProps) {\n  const {\n    value,\n    selected = false,\n    format = \"numeric\",\n    className,\n    ref,\n    ...itemProps\n  } = props;\n\n  const itemRef = React.useRef<ColumnItemElement | null>(null);\n  const composedRef = useComposedRefs(ref, itemRef);\n  const columnContext = useTimePickerColumnContext(COLUMN_ITEM_NAME);\n  const groupContext = useTimePickerGroupContext(COLUMN_ITEM_NAME);\n\n  useIsomorphicLayoutEffect(() => {\n    columnContext.onItemRegister(value, itemRef, selected);\n    return () => columnContext.onItemUnregister(value);\n  }, [columnContext, value, selected]);\n\n  useIsomorphicLayoutEffect(() => {\n    if (selected && itemRef.current) {\n      itemRef.current.scrollIntoView({ block: \"nearest\" });\n    }\n  }, [selected]);\n\n  const onClick = React.useCallback(\n    (event: React.MouseEvent<ColumnItemElement>) => {\n      itemProps.onClick?.(event);\n      if (event.defaultPrevented) return;\n\n      itemRef.current?.focus();\n    },\n    [itemProps.onClick],\n  );\n\n  const onKeyDown = React.useCallback(\n    (event: React.KeyboardEvent<ColumnItemElement>) => {\n      itemProps.onKeyDown?.(event);\n      if (event.defaultPrevented) return;\n\n      if (event.key === \"ArrowUp\" || event.key === \"ArrowDown\") {\n        event.preventDefault();\n        const items = columnContext.getItems().sort((a, b) => {\n          if (typeof a.value === \"number\" && typeof b.value === \"number\") {\n            return a.value - b.value;\n          }\n          return 0;\n        });\n        const currentIndex = items.findIndex((item) => item.value === value);\n\n        let nextIndex: number;\n        if (event.key === \"ArrowUp\") {\n          nextIndex = currentIndex > 0 ? currentIndex - 1 : items.length - 1;\n        } else {\n          nextIndex = currentIndex < items.length - 1 ? currentIndex + 1 : 0;\n        }\n\n        const nextItem = items[nextIndex];\n        nextItem?.ref.current?.focus();\n        nextItem?.ref.current?.click();\n      } else if (\n        (event.key === \"Tab\" ||\n          event.key === \"ArrowLeft\" ||\n          event.key === \"ArrowRight\") &&\n        groupContext\n      ) {\n        event.preventDefault();\n\n        queueMicrotask(() => {\n          const columns = groupContext.getColumns();\n\n          if (columns.length === 0) return;\n\n          const currentColumnIndex = columns.findIndex(\n            (c) => c.ref.current?.contains(itemRef.current) ?? false,\n          );\n\n          if (currentColumnIndex === -1) return;\n\n          const goToPrevious =\n            event.key === \"ArrowLeft\" ||\n            (event.key === \"Tab\" && event.shiftKey);\n\n          const nextColumnIndex = goToPrevious\n            ? currentColumnIndex > 0\n              ? currentColumnIndex - 1\n              : columns.length - 1\n            : currentColumnIndex < columns.length - 1\n              ? currentColumnIndex + 1\n              : 0;\n\n          const nextColumn = columns[nextColumnIndex];\n          if (nextColumn?.ref.current) {\n            const items = nextColumn.getItems();\n            const selectedItem = items.find((item) => item.selected);\n\n            const candidateRefs = selectedItem\n              ? [selectedItem.ref, ...items.map((item) => item.ref)]\n              : items.map((item) => item.ref);\n\n            focusFirst(candidateRefs, false);\n          }\n        });\n      }\n    },\n    [itemProps.onKeyDown, columnContext, groupContext, value],\n  );\n\n  const formattedValue =\n    typeof value === \"number\" && format === \"2-digit\"\n      ? value.toString().padStart(2, \"0\")\n      : value.toString();\n\n  return (\n    <button\n      type=\"button\"\n      {...itemProps}\n      ref={composedRef}\n      data-selected={selected ? \"\" : undefined}\n      className={cn(\n        \"w-full rounded px-3 py-1.5 text-left text-sm hover:bg-accent hover:text-accent-foreground focus:border-ring focus:ring-[3px] focus:ring-ring/50 focus:outline-none\",\n        \"data-selected:bg-primary data-selected:text-primary-foreground data-selected:hover:bg-primary data-selected:hover:text-primary-foreground\",\n        className,\n      )}\n      onClick={onClick}\n      onKeyDown={onKeyDown}\n    >\n      {formattedValue}\n    </button>\n  );\n}\n\ninterface TimePickerHourProps extends DivProps {\n  format?: SegmentFormat;\n}\n\nfunction TimePickerHour(props: TimePickerHourProps) {\n  const { asChild, format = \"numeric\", className, ...hourProps } = props;\n\n  const { is12Hour, hourStep, showSeconds } = useTimePickerContext(HOUR_NAME);\n  const store = useStoreContext(HOUR_NAME);\n\n  const value = useStore((state) => state.value);\n  const timeValue = parseTimeString(value);\n\n  const hours = Array.from(\n    {\n      length: is12Hour ? Math.ceil(12 / hourStep) : Math.ceil(24 / hourStep),\n    },\n    (_, i) => {\n      if (is12Hour) {\n        const hour = (i * hourStep) % 12;\n        return hour === 0 ? 12 : hour;\n      }\n      return i * hourStep;\n    },\n  );\n\n  const onHourSelect = React.useCallback(\n    (displayHour: number) => {\n      const now = new Date();\n      const currentTime = timeValue ?? {};\n\n      let hour24 = displayHour;\n      if (is12Hour) {\n        let currentPeriod: Period;\n        if (timeValue?.period !== undefined) {\n          currentPeriod = timeValue.period;\n        } else if (timeValue?.hour !== undefined) {\n          currentPeriod = to12Hour(timeValue.hour).period;\n        } else {\n          currentPeriod = to12Hour(now.getHours()).period;\n        }\n        hour24 = to24Hour(displayHour, currentPeriod);\n      }\n\n      const newTime = { ...currentTime, hour: hour24 };\n      if (timeValue && timeValue.period !== undefined) {\n        newTime.period = timeValue.period;\n      }\n\n      if (newTime.minute === undefined) {\n        newTime.minute = now.getMinutes();\n      }\n\n      if (showSeconds && newTime.second === undefined) {\n        newTime.second = now.getSeconds();\n      }\n\n      const newValue = formatTimeValue(newTime, showSeconds);\n      store.setState(\"value\", newValue);\n    },\n    [timeValue, showSeconds, is12Hour, store],\n  );\n\n  const now = new Date();\n  const referenceHour = timeValue?.hour ?? now.getHours();\n  const displayHour = is12Hour ? to12Hour(referenceHour).hour : referenceHour;\n\n  const HourPrimitive = asChild ? SlotPrimitive.Slot : TimePickerColumn;\n\n  return (\n    <HourPrimitive\n      data-slot=\"time-picker-hour\"\n      {...hourProps}\n      className={cn(\n        \"scrollbar-none flex max-h-[200px] flex-col gap-1 overflow-y-auto p-1\",\n        className,\n      )}\n    >\n      {hours.map((hour) => (\n        <TimePickerColumnItem\n          key={hour}\n          value={hour}\n          selected={displayHour === hour}\n          format={format}\n          onClick={() => onHourSelect(hour)}\n        />\n      ))}\n    </HourPrimitive>\n  );\n}\n\ninterface TimePickerMinuteProps extends DivProps {\n  format?: SegmentFormat;\n}\n\nfunction TimePickerMinute(props: TimePickerMinuteProps) {\n  const { asChild, format = \"2-digit\", className, ...minuteProps } = props;\n\n  const { minuteStep, showSeconds } = useTimePickerContext(MINUTE_NAME);\n  const store = useStoreContext(MINUTE_NAME);\n\n  const value = useStore((state) => state.value);\n  const timeValue = parseTimeString(value);\n\n  const minutes = Array.from(\n    { length: Math.ceil(60 / minuteStep) },\n    (_, i) => i * minuteStep,\n  );\n\n  const onMinuteSelect = React.useCallback(\n    (minute: number) => {\n      const now = new Date();\n      const currentTime = timeValue ?? {};\n      const newTime = { ...currentTime, minute };\n\n      if (newTime.hour === undefined) {\n        newTime.hour = now.getHours();\n      }\n\n      if (showSeconds && newTime.second === undefined) {\n        newTime.second = now.getSeconds();\n      }\n\n      const newValue = formatTimeValue(newTime, showSeconds);\n      store.setState(\"value\", newValue);\n    },\n    [timeValue, showSeconds, store],\n  );\n\n  const MinutePrimitive = asChild ? SlotPrimitive.Slot : TimePickerColumn;\n\n  const now = new Date();\n  const referenceMinute = timeValue?.minute ?? now.getMinutes();\n\n  return (\n    <MinutePrimitive\n      data-slot=\"time-picker-minute\"\n      {...minuteProps}\n      className={cn(\n        \"scrollbar-none flex max-h-[200px] flex-col gap-1 overflow-y-auto p-1\",\n        className,\n      )}\n    >\n      {minutes.map((minute) => (\n        <TimePickerColumnItem\n          key={minute}\n          value={minute}\n          selected={referenceMinute === minute}\n          format={format}\n          onClick={() => onMinuteSelect(minute)}\n        />\n      ))}\n    </MinutePrimitive>\n  );\n}\n\ninterface TimePickerSecondProps extends DivProps {\n  format?: SegmentFormat;\n}\n\nfunction TimePickerSecond(props: TimePickerSecondProps) {\n  const { asChild, format = \"2-digit\", className, ...secondProps } = props;\n\n  const { secondStep } = useTimePickerContext(SECOND_NAME);\n  const store = useStoreContext(SECOND_NAME);\n\n  const value = useStore((state) => state.value);\n  const timeValue = parseTimeString(value);\n\n  const seconds = Array.from(\n    { length: Math.ceil(60 / secondStep) },\n    (_, i) => i * secondStep,\n  );\n\n  const onSecondSelect = React.useCallback(\n    (second: number) => {\n      const now = new Date();\n      const currentTime = timeValue ?? {};\n      const newTime = { ...currentTime, second };\n\n      if (newTime.hour === undefined) {\n        newTime.hour = now.getHours();\n      }\n\n      if (newTime.minute === undefined) {\n        newTime.minute = now.getMinutes();\n      }\n\n      const newValue = formatTimeValue(newTime, true);\n      store.setState(\"value\", newValue);\n    },\n    [timeValue, store],\n  );\n\n  const SecondPrimitive = asChild ? SlotPrimitive.Slot : TimePickerColumn;\n\n  const now = new Date();\n  const referenceSecond = timeValue?.second ?? now.getSeconds();\n\n  return (\n    <SecondPrimitive\n      data-slot=\"time-picker-second\"\n      {...secondProps}\n      className={cn(\n        \"scrollbar-none flex max-h-[200px] flex-col gap-1 overflow-y-auto p-1\",\n        className,\n      )}\n    >\n      {seconds.map((second) => (\n        <TimePickerColumnItem\n          key={second}\n          value={second}\n          selected={referenceSecond === second}\n          format={format}\n          onClick={() => onSecondSelect(second)}\n        />\n      ))}\n    </SecondPrimitive>\n  );\n}\n\nfunction TimePickerPeriod(props: DivProps) {\n  const { asChild, className, ...periodProps } = props;\n\n  const { is12Hour, showSeconds } = useTimePickerContext(PERIOD_NAME);\n  const store = useStoreContext(PERIOD_NAME);\n\n  const value = useStore((state) => state.value);\n  const timeValue = parseTimeString(value);\n\n  const onPeriodToggle = React.useCallback(\n    (period: Period) => {\n      const now = new Date();\n      const currentTime = timeValue ?? {};\n\n      const currentHour =\n        currentTime.hour !== undefined ? currentTime.hour : now.getHours();\n      const currentDisplay = to12Hour(currentHour);\n      const new24Hour = to24Hour(currentDisplay.hour, period);\n\n      const newTime = { ...currentTime, hour: new24Hour };\n\n      if (newTime.minute === undefined) {\n        newTime.minute = now.getMinutes();\n      }\n\n      if (showSeconds && newTime.second === undefined) {\n        newTime.second = now.getSeconds();\n      }\n\n      const newValue = formatTimeValue(newTime, showSeconds);\n      store.setState(\"value\", newValue);\n    },\n    [timeValue, showSeconds, store],\n  );\n\n  if (!is12Hour) return null;\n\n  const now = new Date();\n  const referenceHour = timeValue?.hour ?? now.getHours();\n  const currentPeriod = to12Hour(referenceHour).period;\n\n  const PeriodPrimitive = asChild ? SlotPrimitive.Slot : TimePickerColumn;\n\n  return (\n    <PeriodPrimitive\n      data-slot=\"time-picker-period\"\n      {...periodProps}\n      className={cn(\"flex flex-col gap-1 p-1\", className)}\n    >\n      {PERIODS.map((period) => (\n        <TimePickerColumnItem\n          key={period}\n          value={period}\n          selected={currentPeriod === period}\n          onClick={() => onPeriodToggle(period)}\n        />\n      ))}\n    </PeriodPrimitive>\n  );\n}\n\ninterface TimePickerSeparatorProps extends React.ComponentProps<\"span\"> {\n  asChild?: boolean;\n}\n\nfunction TimePickerSeparator(props: TimePickerSeparatorProps) {\n  const { asChild, children, ...separatorProps } = props;\n\n  const SeparatorPrimitive = asChild ? SlotPrimitive.Slot : \"span\";\n\n  return (\n    <SeparatorPrimitive\n      aria-hidden=\"true\"\n      data-slot=\"time-picker-separator\"\n      {...separatorProps}\n    >\n      {children ?? \":\"}\n    </SeparatorPrimitive>\n  );\n}\n\nfunction TimePickerClear(props: ButtonProps) {\n  const {\n    asChild,\n    className,\n    children,\n    disabled: disabledProp,\n    ...clearProps\n  } = props;\n\n  const { disabled, readOnly } = useTimePickerContext(CLEAR_NAME);\n  const store = useStoreContext(CLEAR_NAME);\n\n  const isDisabled = disabledProp || disabled;\n\n  const onClick = React.useCallback(\n    (event: React.MouseEvent<HTMLButtonElement>) => {\n      clearProps.onClick?.(event);\n      if (event.defaultPrevented) return;\n\n      event.preventDefault();\n      if (disabled || readOnly) return;\n      store.setState(\"value\", \"\");\n    },\n    [clearProps.onClick, disabled, readOnly, store],\n  );\n\n  const ClearPrimitive = asChild ? SlotPrimitive.Slot : \"button\";\n\n  return (\n    <ClearPrimitive\n      type=\"button\"\n      data-slot=\"time-picker-clear\"\n      disabled={isDisabled}\n      {...clearProps}\n      className={cn(\n        \"inline-flex items-center justify-center rounded-sm text-sm font-medium transition-colors hover:bg-accent hover:text-accent-foreground disabled:pointer-events-none disabled:opacity-50\",\n        className,\n      )}\n      onClick={onClick}\n    >\n      {children ?? \"Clear\"}\n    </ClearPrimitive>\n  );\n}\n\nexport {\n  TimePicker,\n  TimePickerClear,\n  TimePickerContent,\n  TimePickerHour,\n  TimePickerInput,\n  TimePickerInputGroup,\n  TimePickerLabel,\n  TimePickerMinute,\n  TimePickerPeriod,\n  type TimePickerProps,\n  TimePickerSecond,\n  TimePickerSeparator,\n  TimePickerTrigger,\n  useStore as useTimePicker,\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": ""
    }
  ]
}