{"$schema":"https://ui.shadcn.com/schema/registry-item.json","name":"time-picker","type":"registry:ui","title":"Time picker","description":"Choose a time with sensible keyboard behavior.","categories":["inputs"],"dependencies":["lucide-react","motion"],"registryDependencies":["https://ui.teamofsilicons.com/r/silicon-motion-tokens.json"],"files":[{"path":"registry/components/time-picker/time-picker.tsx","type":"registry:component","target":"@components/silicon-ui/time-picker/time-picker.tsx","content":"\"use client\";\n\nimport { AnimatePresence, motion, useReducedMotion } from \"motion/react\";\nimport type { Variants } from \"motion/react\";\nimport { ChevronDown, Clock3 } from \"lucide-react\";\nimport { useEffect, useId, useLayoutEffect, useRef, useState } from \"react\";\nimport { motionTokens } from \"../lib/motion-tokens\";\nimport { useNearestItem } from \"../lib/fluid-hover\";\nimport styles from \"./time-picker.module.css\";\n\nexport interface TimePickerProps {\n  label: string;\n  value?: string;\n  defaultValue?: string;\n  onChange?: (value: string) => void;\n  description?: string;\n  placeholder?: string;\n  minuteStep?: 1 | 5 | 10 | 15 | 30;\n  format?: \"12h\" | \"24h\";\n  disabled?: boolean;\n  className?: string;\n}\n\nconst pad = (value: number) => String(value).padStart(2, \"0\");\nconst toMinutes = (value: string) => { const [h = NaN, m = NaN] = value.split(\":\").map(Number); return Number.isFinite(h) && Number.isFinite(m) ? h * 60 + m : 0; };\nconst enter = motionTokens.ease.enter;\nconst standard = motionTokens.ease.standard;\n/** The shown time rolls like a clock face: a later time rises from below, an earlier one drops from above. */\nconst valueRoll: Variants = {\n  enter: (direction: number) => ({ opacity: 0, y: `${direction * 0.35}em`, filter: `blur(${motionTokens.blur.soft}px)` }),\n  center: { opacity: 1, y: 0, filter: \"blur(0px)\", transition: { duration: motionTokens.duration.standard, ease: enter } },\n  exit: (direction: number) => ({ opacity: 0, y: `${direction * -0.3}em`, filter: `blur(${motionTokens.blur.subtle}px)`, transition: { duration: motionTokens.duration.fast, ease: standard } }),\n};\n/** Reduced motion keeps a short crossfade; the resting state matches valueRoll so server and client markup agree. */\nconst valueFade: Variants = { enter: { opacity: 0 }, center: { opacity: 1, y: 0, filter: \"blur(0px)\", transition: { duration: motionTokens.duration.instant } }, exit: { opacity: 0, transition: { duration: motionTokens.duration.instant } } };\n\nexport function TimePicker({ label, value, defaultValue = \"09:00\", onChange, description, placeholder = \"Select a time\", minuteStep = 15, format = \"12h\", disabled = false, className }: TimePickerProps) {\n  const id = useId();\n  const labelId = `${id}-label`;\n  const valueId = `${id}-value`;\n  const rootRef = useRef<HTMLDivElement>(null);\n  const optionRefs = useRef<Array<HTMLButtonElement | null>>([]);\n  const centerOnOpen = useRef(false);\n  const [internal, setInternal] = useState(defaultValue);\n  const [open, setOpen] = useState(false);\n  const [activeIndex, setActiveIndex] = useState(-1);\n  // Fluid hover: the mouse makes the nearest time active, gaps and padding included, and a click there picks it.\n  const nearest = useNearestItem<HTMLDivElement>({ items: '[role=\"option\"]', onPick: item => setActiveIndex(Number(item.dataset.index)) });\n  const selected = value ?? internal;\n  const reduce = useReducedMotion();\n  const [previous, setPrevious] = useState(selected);\n  const [direction, setDirection] = useState(1);\n  if (previous !== selected) { setPrevious(selected); setDirection(toMinutes(selected) >= toMinutes(previous) ? 1 : -1); }\n  const options = Array.from({ length: Math.ceil(1440 / minuteStep) }, (_, index) => { const minutes = index * minuteStep; const hour = Math.floor(minutes / 60); const minute = minutes % 60; return `${pad(hour)}:${pad(minute)}`; });\n  const display = (raw: string) => { const minutes = toMinutes(raw); const hour = Math.floor(minutes / 60); const minute = minutes % 60; return format === \"24h\" ? `${pad(hour)}:${pad(minute)}` : `${hour % 12 || 12}:${pad(minute)} ${hour < 12 ? \"AM\" : \"PM\"}`; };\n  useEffect(() => { const close = (event: PointerEvent) => { if (!rootRef.current?.contains(event.target as Node)) setOpen(false); }; document.addEventListener(\"pointerdown\", close); return () => document.removeEventListener(\"pointerdown\", close); }, []);\n  // Runs before paint so the menu's first frame is already centered on the selected time.\n  useLayoutEffect(() => {\n    if (!open || activeIndex < 0) return;\n    // Scroll only the menu, never the page: the selected time opens centered, arrow keys then keep the highlight in view.\n    const option = optionRefs.current[activeIndex];\n    const menu = option?.parentElement;\n    if (!option || !menu) return;\n    const top = option.offsetTop;\n    const bottom = top + option.offsetHeight;\n    if (centerOnOpen.current) { centerOnOpen.current = false; menu.scrollTop = top - (menu.clientHeight - option.offsetHeight) / 2; }\n    else if (top < menu.scrollTop) menu.scrollTop = top;\n    else if (bottom > menu.scrollTop + menu.clientHeight) menu.scrollTop = bottom - menu.clientHeight;\n  }, [activeIndex, open]);\n  const choose = (next: string) => { if (value === undefined) setInternal(next); onChange?.(next); setOpen(false); };\n  const openMenu = () => {\n    const selectedIndex = options.indexOf(selected);\n    setActiveIndex(selectedIndex >= 0 ? selectedIndex : 0);\n    centerOnOpen.current = true;\n    setOpen(true);\n  };\n  const onKeyDown = (event: React.KeyboardEvent<HTMLButtonElement>) => {\n    if (event.key === \"Escape\") { event.preventDefault(); setOpen(false); return; }\n    if (event.key === \"Enter\" || event.key === \" \") {\n      event.preventDefault();\n      if (open && activeIndex >= 0) choose(options[activeIndex]!);\n      else openMenu();\n      return;\n    }\n    if (event.key === \"ArrowDown\" || event.key === \"ArrowUp\") {\n      event.preventDefault();\n      if (!open) { openMenu(); return; }\n      setActiveIndex(index => (index + (event.key === \"ArrowDown\" ? 1 : -1) + options.length) % options.length);\n    }\n  };\n  return <div ref={rootRef} className={[styles.field, className].filter(Boolean).join(\" \")}>\n    <span id={labelId} className={styles.label}>{label}</span>\n    <div className={styles.anchor}>\n      <button type=\"button\" className={styles.trigger} disabled={disabled} aria-labelledby={`${labelId} ${valueId}`} aria-haspopup=\"listbox\" aria-expanded={open} aria-controls={`${id}-listbox`} onClick={() => open ? setOpen(false) : openMenu()} onKeyDown={onKeyDown}>\n        <Clock3 size={16} aria-hidden=\"true\" />\n        <span id={valueId} className={styles.srOnly}>{selected ? display(selected) : placeholder}</span>\n        <span className={styles.valueText} aria-hidden=\"true\"><AnimatePresence initial={false} custom={direction}><motion.span key={selected || \"placeholder\"} className={selected ? styles.value : styles.placeholder} custom={direction} variants={reduce ? valueFade : valueRoll} initial=\"enter\" animate=\"center\" exit=\"exit\">{selected ? display(selected) : placeholder}</motion.span></AnimatePresence></span>\n        <ChevronDown className={styles.chevron} size={16} aria-hidden=\"true\" />\n      </button>\n      <AnimatePresence initial={false}>{open && <motion.div id={`${id}-listbox`} className={styles.menu} role=\"listbox\" aria-label={`${label} options`} {...nearest}\n        initial={reduce ? { opacity: 0 } : { opacity: 0, y: -6, scale: .97 }}\n        animate={{ opacity: 1, y: 0, scale: 1, transition: reduce ? { duration: motionTokens.duration.instant } : { ...motionTokens.spring.snappy, opacity: { duration: motionTokens.duration.fast, ease: enter } } }}\n        exit={{ opacity: 0, ...(reduce ? {} : { y: -4, scale: .98 }), transition: { duration: motionTokens.duration.instant, ease: standard } }}>\n        {options.map((option, index) => <button ref={node => { optionRefs.current[index] = node; }} id={`${id}-option-${index}`} type=\"button\" role=\"option\" aria-selected={option === selected} data-active={activeIndex === index || undefined} className={styles.option} key={option} data-index={index} onClick={() => choose(option)}>{display(option)}{option === selected && <span className={styles.dot} aria-hidden=\"true\" />}</button>)}\n      </motion.div>}</AnimatePresence>\n    </div>\n    {description && <span className={styles.description}>{description}</span>}\n  </div>;\n}\n"},{"path":"registry/components/time-picker/time-picker.module.css","type":"registry:component","target":"@components/silicon-ui/time-picker/time-picker.module.css","content":".field { display: grid; gap: var(--space-related); min-width: 0; }\n.label { font-size: var(--text-sm); font-weight: 500; line-height: var(--leading-body); letter-spacing: var(--tracking-body); }\n.anchor { position: relative; min-width: 0; }\n/* The trigger anchors the menu, so press feedback stays in color; it never scales. */\n.trigger { position: relative; display: flex; width: 100%; min-height: var(--control-height-md); align-items: center; gap: var(--space-related); border: 1px solid var(--border); border-radius: var(--radius-control); corner-shape: var(--corner-shape); padding: 0 var(--space-group); background: var(--surface); color: var(--foreground); font: inherit; font-size: var(--field-text-size, var(--text-sm)); text-align: left; cursor: pointer; transition: border-color var(--duration-fast) var(--ease-standard), background-color var(--duration-fast) var(--ease-standard), box-shadow var(--duration-fast) var(--ease-standard); }\n@media (hover: hover) and (pointer: fine) { .trigger:hover:not(:disabled) { border-color: var(--border-strong); background: var(--surface-muted); } }\n.trigger:active:not(:disabled), .trigger[aria-expanded=\"true\"] { border-color: var(--border-strong); background: var(--surface-muted); }\n.trigger:disabled { opacity: .5; cursor: not-allowed; }\n.trigger > svg:first-child { flex: 0 0 auto; color: var(--text-muted); }\n.valueText { display: grid; flex: 1; min-width: 0; grid-template-columns: minmax(0, 1fr); }\n.valueText > span { grid-area: 1 / 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n.value { font-variant-numeric: tabular-nums; }\n.placeholder { color: var(--text-muted); } .description { color: var(--field-hint-colour, var(--text-muted)); }\n.srOnly { position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0; }\n.chevron { flex: 0 0 auto; color: var(--text-muted); transition: transform var(--duration-spring) var(--ease-spring); }\n.trigger[aria-expanded=\"true\"] .chevron { transform: rotate(180deg); }\n.description { font-size: var(--field-hint-size, var(--text-xs)); letter-spacing: var(--tracking-body); }\n.menu { position: absolute; z-index: var(--layer-dropdown); top: calc(100% + var(--space-related)); right: 0; left: 0; max-height: 250px; overflow-y: auto; overscroll-behavior: contain; border: 1px solid var(--border); border-radius: var(--radius-container); corner-shape: var(--corner-shape); padding: var(--space-tight); background: var(--surface-raised); box-shadow: var(--shadow-floating); transform-origin: top center; will-change: transform, opacity; }\n/* Arrow keys move the highlight often, so it changes almost instantly. The dot marks the chosen time, so only one row is ever highlighted. */\n.option { display: flex; width: 100%; min-height: 36px; align-items: center; justify-content: space-between; border: 0; border-radius: calc(var(--radius-container) - var(--space-tight)); corner-shape: var(--corner-shape); padding: 0 var(--space-group); background: transparent; color: var(--foreground); font: inherit; font-size: var(--text-sm); font-variant-numeric: tabular-nums; text-align: left; cursor: pointer; transition: background-color 80ms var(--ease-standard); }\n.option[data-active=\"true\"] { background: var(--surface-muted); }\n.dot { width: 6px; height: 6px; border-radius: var(--radius-full); corner-shape: round; background: var(--foreground); }\n@media (prefers-reduced-motion: reduce) { .trigger, .chevron, .option { transition: none; } }\n"},{"path":"lib/fluid-hover.tsx","type":"registry:lib","target":"@components/silicon-ui/lib/fluid-hover.tsx","content":"\"use client\";\n\n/**\n * Fluid hover: in a set of items (a menu, a list, tabs, a grid), the item nearest the pointer is lit, even while the\n * pointer is in a gap, the padding or past the last item, and one highlight springs between items. A click in a gap goes\n * to the lit item, so what is lit is what a click hits. A new pointer entry fades the highlight in where it lands instead\n * of sliding it over from the last item. The keyboard moves it without travel, and reduced motion keeps the fade but\n * drops the travel.\n *\n *   const list = useRef<HTMLUListElement>(null);\n *   const hover = useFluidHover(list, { items: \"[data-item]\" });\n *   <ul ref={list} className={styles.list} {...hover.handlers}>      // .list { position: relative }\n *     <FluidHoverHighlight hover={hover} className={styles.highlight} />\n *     …items…\n *   </ul>\n *\n * Adapted from Fluid Functionalism's use-fluid-hover and fluid-hover-highlight by Micka Touillaud (MIT),\n * https://github.com/mickadesign/fluid-functionalism. Items are found by a selector instead of registering themselves,\n * so a component adopts it without changing its items.\n */\nimport { useEffect, useEffectEvent, useRef, useState } from \"react\";\nimport type { FocusEvent, MouseEvent, PointerEvent, RefObject } from \"react\";\nimport { AnimatePresence, motion, useReducedMotion } from \"motion/react\";\nimport { motionTokens } from \"./motion-tokens\";\n\n/** An item's box in the container's own layout space (unaffected by transforms or scrolling). */\nexport interface FluidRect { top: number; left: number; width: number; height: number }\n\nexport interface FluidHoverState {\n  /** Where the highlight sits, or null when nothing is lit. */\n  rect: FluidRect | null;\n  /** The lit item. */\n  item: HTMLElement | null;\n  /** False when the highlight should jump (keyboard, a resize) rather than travel. */\n  glide: boolean;\n  /** Steps on each new pointer entry, so the highlight fades in where it lands. */\n  session: number;\n}\n\nexport interface FluidHoverOptions {\n  /** Selector for the items inside the container. */\n  items: string;\n  /** \"y\" for lists (default), \"x\" for a row of tabs, \"xy\" for grids. */\n  axis?: \"x\" | \"y\" | \"xy\";\n  /** Items the highlight skips (and a gap click never activates). Defaults to disabled or aria-disabled items. */\n  isItemDisabled?: (item: HTMLElement) => boolean;\n  /** \"skip\" (default) lights the nearest enabled item even over a disabled one; \"clear\" lights nothing over a disabled item (a calendar's unavailable days). */\n  disabled?: \"skip\" | \"clear\";\n  /** Route a click in a gap to the lit item. On by default; turn it off where empty space should stay inert. */\n  gapClick?: boolean;\n  /** Light the item that receives keyboard focus. On by default. */\n  followFocus?: boolean;\n}\n\nconst defaultDisabled = (item: HTMLElement) =>\n  item.matches(\":disabled, [aria-disabled='true'], [data-disabled]\");\n\n/** Controls that sit between items keep their own clicks. */\nconst CONTROL = \"input, textarea, select, button, a, summary, [contenteditable], [role='textbox'], [role='searchbox'], [role='button']\";\n\n/** The item's box relative to the container, summing offsets through positioned ancestors in between. */\nexport function rectOf(item: HTMLElement, container: HTMLElement): FluidRect {\n  let top = item.offsetTop, left = item.offsetLeft;\n  let parent = item.offsetParent as HTMLElement | null;\n  while (parent && parent !== container && container.contains(parent)) {\n    top += parent.offsetTop + parent.clientTop;\n    left += parent.offsetLeft + parent.clientLeft;\n    parent = parent.offsetParent as HTMLElement | null;\n  }\n  return { top, left, width: item.offsetWidth, height: item.offsetHeight };\n}\n\n/**\n * The item the pointer is inside, or else the one whose edge is nearest along the axis (both axes for a grid). Edges, not\n * centres, so a gap next to a wide item still lights the item the pointer is closer to.\n */\nexport function pickNearest(items: HTMLElement[], x: number, y: number, axis: \"x\" | \"y\" | \"xy\"): HTMLElement | null {\n  let nearest: HTMLElement | null = null, best = Infinity;\n  for (const item of items) {\n    const r = item.getBoundingClientRect();\n    if (!r.width && !r.height) continue;\n    if (x >= r.left && x <= r.right && y >= r.top && y <= r.bottom) return item;\n    const dx = Math.max(r.left - x, 0, x - r.right), dy = Math.max(r.top - y, 0, y - r.bottom);\n    const distance = axis === \"x\" ? dx : axis === \"y\" ? dy : Math.hypot(dx, dy);\n    if (distance < best) { best = distance; nearest = item; }\n  }\n  return nearest;\n}\n\n/** Hands a gap click to the item as a real mouse click (detail, modifiers and coordinates kept), so the item reads it as a pointer press, not a keyboard one. */\nfunction forwardClick(item: HTMLElement, event: MouseEvent<HTMLElement>) {\n  const { detail, clientX, clientY, screenX, screenY, ctrlKey, shiftKey, altKey, metaKey, button, buttons } = event;\n  item.dispatchEvent(new globalThis.MouseEvent(\"click\", { bubbles: true, cancelable: true, composed: true, view: window, detail, clientX, clientY, screenX, screenY, ctrlKey, shiftKey, altKey, metaKey, button, buttons }));\n}\n\n/** Whether an event happened inside the element whose handler hears it, not in a portalled child (a submenu, a tooltip). */\nconst isOwn = (event: { currentTarget: EventTarget; target: EventTarget }) =>\n  !(event.currentTarget instanceof Node) || !(event.target instanceof Node) || event.currentTarget.contains(event.target);\n\nexport function useFluidHover<T extends HTMLElement>(container: RefObject<T | null>, options: FluidHoverOptions) {\n  const { items: selector, axis = \"y\", isItemDisabled = defaultDisabled, disabled = \"skip\", gapClick = true, followFocus = true } = options;\n  const [state, setState] = useState<FluidHoverState>({ rect: null, item: null, glide: false, session: 0 });\n  const current = useRef(state);\n  const pointer = useRef(false);\n  const entering = useRef(false);\n  const frame = useRef(0);\n\n  function commit(next: FluidHoverState) {\n    const prev = current.current;\n    if (prev.item === next.item && prev.session === next.session && sameRect(prev.rect, next.rect)) return;\n    current.current = next;\n    setState(next);\n  }\n\n  function light(item: HTMLElement | null, glide: boolean) {\n    const root = container.current;\n    if (!root || !item) return commit({ ...current.current, rect: null, item: null, glide });\n    let session = current.current.session;\n    if (entering.current) { entering.current = false; session += 1; }\n    commit({ rect: rectOf(item, root), item, glide: glide && session === current.current.session, session });\n  }\n\n  const enabledItems = () => {\n    const root = container.current;\n    return root ? [...root.querySelectorAll<HTMLElement>(selector)].filter(item => !isItemDisabled(item)) : [];\n  };\n\n  // The lit item can move under a still highlight (a resize, content above it changing): follow it without travel.\n  const follow = useEffectEvent(() => { const { item } = current.current; if (item?.isConnected) light(item, false); });\n  useEffect(() => {\n    const root = container.current;\n    if (!root || typeof ResizeObserver === \"undefined\") return;\n    const observer = new ResizeObserver(() => follow());\n    observer.observe(root);\n    return () => observer.disconnect();\n  }, [container]);\n  useEffect(() => () => cancelAnimationFrame(frame.current), []);\n\n  const handlers = {\n    onMouseEnter: () => { entering.current = true; },\n    onMouseMove: (event: MouseEvent<T>) => {\n      if (!isOwn(event)) return;\n      pointer.current = true;\n      const { clientX, clientY } = event;\n      cancelAnimationFrame(frame.current);\n      frame.current = requestAnimationFrame(() => {\n        const over = disabled === \"clear\" ? document.elementFromPoint(clientX, clientY)?.closest<HTMLElement>(selector) : null;\n        light(over && isItemDisabled(over) ? null : pickNearest(enabledItems(), clientX, clientY, axis), true);\n      });\n    },\n    onMouseLeave: () => { cancelAnimationFrame(frame.current); entering.current = false; light(null, false); },\n    onKeyDownCapture: () => { pointer.current = false; },\n    onFocus: (event: FocusEvent<T>) => {\n      if (!followFocus || pointer.current || !isOwn(event)) return;\n      const item = event.target instanceof HTMLElement ? event.target.closest<HTMLElement>(selector) : null;\n      if (item && container.current?.contains(item) && !isItemDisabled(item)) light(item, false);\n    },\n    onBlur: (event: FocusEvent<T>) => {\n      if (!pointer.current && !event.currentTarget.contains(event.relatedTarget as Node | null)) light(null, false);\n    },\n    onClick: (event: MouseEvent<T>) => {\n      if (!gapClick || !isOwn(event)) return;\n      const target = event.target as Element;\n      if (!target.isConnected || target.closest(selector) || target.closest(CONTROL)) return;\n      const { item } = current.current;\n      if (item?.isConnected && !isItemDisabled(item)) forwardClick(item, event);\n    },\n  };\n\n  /** Clears the highlight at once, such as when a menu opens again. */\n  const reset = () => { cancelAnimationFrame(frame.current); entering.current = true; light(null, false); };\n\n  return { ...state, handlers, reset, light };\n}\n\nexport interface NearestItemOptions extends Omit<FluidHoverOptions, \"followFocus\"> {\n  /** Called with the nearest enabled item as the mouse moves over the container. */\n  onPick: (item: HTMLElement) => void;\n}\n\n/**\n * Fluid hover's picking without its highlight, for a list that already keeps its own active item and highlight (a listbox\n * with aria-activedescendant, a roving list): the mouse picks the nearest enabled item, gaps and padding included, and a\n * click in a gap goes to that item. Spread the result on the container.\n */\n// A hook only so callbacks that touch refs can be passed during render; it keeps no state.\nexport function useNearestItem<T extends HTMLElement>({ items: selector, axis = \"y\", isItemDisabled = defaultDisabled, gapClick = true, onPick }: NearestItemOptions) {\n  const nearest = (event: MouseEvent<T>) => {\n    if (!isOwn(event)) return null;\n    const items = [...event.currentTarget.querySelectorAll<HTMLElement>(selector)].filter(item => !isItemDisabled(item));\n    return pickNearest(items, event.clientX, event.clientY, axis);\n  };\n  return {\n    onPointerMove: (event: PointerEvent<T>) => { if (event.pointerType !== \"mouse\") return; const item = nearest(event); if (item) onPick(item); },\n    onClick: (event: MouseEvent<T>) => {\n      if (!gapClick) return;\n      const target = event.target as Element;\n      if (!target.isConnected || target.closest(selector) || target.closest(CONTROL)) return;\n      const item = nearest(event);\n      if (item) forwardClick(item, event);\n    },\n  };\n}\n\nconst sameRect = (a: FluidRect | null, b: FluidRect | null) =>\n  a === b || (!!a && !!b && a.top === b.top && a.left === b.left && a.width === b.width && a.height === b.height);\n\nexport interface FluidHoverHighlightProps {\n  /** The state from useFluidHover (or any { rect, glide, session }). */\n  hover: Pick<FluidHoverState, \"rect\" | \"glide\" | \"session\">;\n  /** Radius, fill, z-index. The container must be position: relative; the highlight is placed at its top-left corner. */\n  className?: string;\n  /** Extra attributes for styling, such as data-tone. */\n  tone?: string;\n}\n\n/** The one highlight a fluid hover set renders: it springs between items on transforms, and fades in and out. */\nexport function FluidHoverHighlight({ hover, className, tone }: FluidHoverHighlightProps) {\n  const reduced = useReducedMotion() ?? false;\n  const { rect, glide, session } = hover;\n  const place = rect ? { x: rect.left, y: rect.top, width: rect.width, height: rect.height } : undefined;\n  return (\n    <AnimatePresence initial={false}>\n      {place ? (\n        <motion.span key={session} className={className} data-tone={tone} aria-hidden=\"true\"\n          style={{ position: \"absolute\", top: 0, left: 0, pointerEvents: \"none\" }}\n          initial={{ opacity: 0, ...place }}\n          animate={{ opacity: 1, ...place }}\n          exit={{ opacity: 0, transition: motionTokens.spring.fast.exit }}\n          transition={{ default: glide && !reduced ? motionTokens.spring.fast : { duration: 0 }, opacity: { duration: motionTokens.duration.fast } }} />\n      ) : null}\n    </AnimatePresence>\n  );\n}\n"}],"docs":"Docs and live preview: https://ui.teamofsilicons.com/time-picker","meta":{"tier":"free","kind":"component","docs":"https://ui.teamofsilicons.com/time-picker","markdown":"https://ui.teamofsilicons.com/time-picker.md","tags":["time","field"],"status":"stable"}}