{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "slider",
  "type": "registry:component",
  "title": "Slider",
  "description": "A number set by dragging along a track, with its name and value written inside it.",
  "dependencies": [
    "@base-ui/react",
    "tailwind-variants"
  ],
  "registryDependencies": [],
  "cssVars": {
    "light": {
      "success": "oklch(0.696 0.17 162.48)",
      "warning": "oklch(0.769 0.188 70.08)",
      "info": "oklch(0.623 0.214 259.815)"
    },
    "dark": {
      "success": "oklch(0.696 0.17 162.48)",
      "warning": "oklch(0.769 0.188 70.08)",
      "info": "oklch(0.623 0.214 259.815)"
    }
  },
  "files": [
    {
      "path": "components/panelui/slider.tsx",
      "type": "registry:component",
      "target": "@components/panelui/slider.tsx",
      "content": "'use client';\n\n/**\n * Slider — a number chosen by dragging along a track, with its name and its\n * value written inside the track.\n *\n * ```tsx\n * <Slider label=\"Opacity\" defaultValue={64} format={{ style: 'unit', unit: 'percent' }} />\n *\n * <Slider variant=\"line\" label=\"Zoom\" min={10} max={400} defaultValue={100}>\n *   <Slider.Step by={-10} aria-label=\"Zoom out\"><MinusIcon /></Slider.Step>\n *   <Slider.Step by={10} aria-label=\"Zoom in\"><PlusIcon /></Slider.Step>\n * </Slider>\n * ```\n *\n * ## The track is the label\n *\n * In a panel of settings, a column of sliders each with a label above and a\n * number beside is three things to line up per row. The `fill` slider puts\n * the name at the start of the track and the value at its end, so a row is\n * one bar the height of a button, and a stack of them reads as a table.\n *\n * ## It moves the way the hand does\n *\n * A press away from the handle glides the fill there on a curve with a\n * little overshoot; a drag follows the pointer exactly, with nothing\n * easing behind it. Pulled past either end, the track stretches a few\n * pixels towards the pointer and springs back on release, so the end of\n * the range is felt rather than hit.\n *\n * ## Two weights\n *\n * `fill` is the slider for a settings panel. `line` is a thin track with a\n * round knob, for a toolbar, where a zoom or a scrubber sits between two\n * buttons: `Slider.Step` is that button, and it moves the value by `by`.\n *\n * `origin=\"center\"` fills from the middle out, for a value that can go\n * either way from nothing: exposure, a balance, a pitch shift.\n */\nimport { Slider as BaseSlider } from '@base-ui/react/slider';\nimport {\n  Children,\n  createContext,\n  isValidElement,\n  useCallback,\n  useContext,\n  useEffect,\n  useLayoutEffect,\n  useMemo,\n  useRef,\n  useState,\n  useSyncExternalStore,\n  type ComponentProps,\n  type CSSProperties,\n  type PointerEvent as ReactPointerEvent,\n  type ReactNode,\n} from 'react';\nimport { tv } from 'tailwind-variants';\nimport { mergeProps } from '@base-ui/react/merge-props';\nimport { useRender } from '@base-ui/react/use-render';\nimport type * as ReactTypes from 'react';\nimport type { ElementType as RenderElementType } from 'react';\n\n/** Draws a part as another element, or composes it with a component: `render={<a href=\"/\" />}`. */\ntype RenderProp<Tag extends RenderElementType> = { render?: useRender.ComponentProps<Tag>['render'] };\n\nexport type SliderVariant = 'fill' | 'line';\nexport type SliderSize = 'sm' | 'md' | 'lg';\nexport type SliderColor = 'neutral' | 'primary' | 'info' | 'success' | 'warning' | 'destructive';\n\n/** A glide with a little overshoot, for a press. A drag turns it off. */\nconst GLIDE = 'duration-[420ms] ease-[cubic-bezier(0.3,1.3,0.45,1)]';\n/** How far the track gives past an end, at most, in pixels. */\nconst STRETCH = 8;\n/** How far the pointer has to travel before a press counts as a drag. */\nconst DRAG_SLOP = 3;\n/** Steps are marked when there are this many or fewer. */\nconst MAX_TICKS = 12;\n\nconst sliderVariants = tv({\n  slots: {\n    root: 'group/slider relative flex w-full min-w-0 items-center gap-1.5 data-disabled:opacity-50',\n    control: [\n      'relative min-w-0 flex-1 cursor-pointer touch-none select-none outline-none',\n      'transition-[margin] duration-500 ease-[cubic-bezier(0.3,1.4,0.4,1)] data-stretching:duration-0',\n      'data-disabled:cursor-not-allowed motion-reduce:transition-none',\n    ],\n    track: 'absolute overflow-hidden',\n    fill: [\n      'absolute inset-y-0 start-(--slider-start) w-(--slider-span)',\n      `transition-[inset-inline-start,width] ${GLIDE} group-data-moving/slider:duration-0 motion-reduce:transition-none`,\n    ],\n    tick: 'pointer-events-none absolute -translate-x-1/2 rtl:translate-x-1/2',\n    center:\n      'pointer-events-none absolute inset-y-1.5 start-1/2 w-px -translate-x-1/2 bg-foreground/20 rtl:translate-x-1/2',\n    handle: [\n      'pointer-events-none absolute top-1/2 start-(--slider-at) -translate-y-1/2',\n      `transition-[inset-inline-start,height,width,background-color,scale] ${GLIDE} group-data-moving/slider:duration-0 motion-reduce:transition-none`,\n    ],\n    // Base UI's thumb carries the keyboard and the screen reader. It is\n    // invisible: the handle above is what is seen.\n    thumb: 'pointer-events-none size-px opacity-0',\n    text: 'pointer-events-none absolute inset-0 flex items-center justify-between gap-3 px-3',\n    // Lighter than muted, so the name reads on a coloured fill as well as on the track.\n    label: 'truncate text-foreground/70',\n    value: 'shrink-0 tabular-nums text-foreground',\n    step: [\n      'inline-flex size-7 shrink-0 cursor-pointer items-center justify-center rounded-lg text-muted-foreground outline-none',\n      'transition-[color,background-color,scale] duration-150 hover:bg-foreground/[0.06] hover:text-foreground active:scale-90',\n      'focus-visible:ring-2 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-40',\n      '[&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*=size-])]:size-4',\n    ],\n  },\n  variants: {\n    variant: {\n      fill: {\n        control: 'h-(--slider-h) rounded-[10px] has-[:focus-visible]:ring-2 has-[:focus-visible]:ring-ring',\n        track: 'inset-0 rounded-[inherit] bg-muted',\n        fill: 'bg-(--slider-fill)',\n        // A notch on the top and bottom edges at every step, like the marks\n        // on a rule: the text runs between them and never crosses one.\n        tick: [\n          'inset-y-0 w-px',\n          'bg-[linear-gradient(to_bottom,var(--slider-notch)_0_5px,transparent_5px_calc(100%-5px),var(--slider-notch)_calc(100%-5px))]',\n          '[--slider-notch:color-mix(in_oklab,var(--foreground)_22%,transparent)]',\n        ],\n        handle: [\n          // Centred on the value, so it stands on the same line as the fill's\n          // edge and the notch of the step it is on.\n          'h-[calc(var(--slider-h)*0.44)] w-[3px] -translate-x-1/2 rounded-full bg-(--slider-mark) opacity-60 rtl:translate-x-1/2',\n          'group-hover/slider:h-[calc(var(--slider-h)*0.56)] group-hover/slider:opacity-100 group-data-moving/slider:h-[calc(var(--slider-h)*0.56)] group-data-moving/slider:opacity-100',\n          'group-data-disabled/slider:opacity-40',\n          // Where it would cross the name or the value, it steps out of the way:\n          // the edge of the fill still says where the value is.\n          'data-crowded:opacity-0 group-hover/slider:data-crowded:opacity-0 group-data-moving/slider:data-crowded:opacity-0',\n        ],\n        text: 'text-(length:--slider-text) font-medium',\n      },\n      line: {\n        control: 'h-6 rounded-full',\n        track: 'inset-x-0 top-1/2 h-1 -translate-y-1/2 rounded-full bg-foreground/15',\n        fill: 'rounded-full bg-(--slider-mark)',\n        tick: 'top-1/2 size-[3px] -translate-y-1/2 rounded-full bg-background/70',\n        handle: [\n          'size-4 -translate-x-1/2 rounded-full border-2 border-(--slider-mark) bg-background shadow-[0_1px_3px_rgb(0_0_0/0.2)] rtl:translate-x-1/2',\n          'group-hover/slider:scale-110 group-data-moving/slider:scale-125',\n          'group-has-[:focus-visible]/slider:ring-2 group-has-[:focus-visible]/slider:ring-ring group-has-[:focus-visible]/slider:ring-offset-2 group-has-[:focus-visible]/slider:ring-offset-background',\n        ],\n      },\n    },\n    color: {\n      neutral: {\n        root: '[--slider-fill:color-mix(in_oklab,var(--foreground)_11%,transparent)] [--slider-mark:var(--foreground)]',\n      },\n      primary: {\n        root: '[--slider-fill:color-mix(in_oklab,var(--primary)_24%,transparent)] [--slider-mark:var(--primary)]',\n      },\n      info: { root: '[--slider-fill:color-mix(in_oklab,var(--info)_26%,transparent)] [--slider-mark:var(--info)]' },\n      success: {\n        root: '[--slider-fill:color-mix(in_oklab,var(--success)_26%,transparent)] [--slider-mark:var(--success)]',\n      },\n      warning: {\n        root: '[--slider-fill:color-mix(in_oklab,var(--warning)_28%,transparent)] [--slider-mark:var(--warning)]',\n      },\n      destructive: {\n        root: '[--slider-fill:color-mix(in_oklab,var(--destructive)_24%,transparent)] [--slider-mark:var(--destructive)]',\n      },\n    },\n    size: {\n      sm: { root: '[--slider-h:1.75rem] [--slider-text:12px]' },\n      md: { root: '[--slider-h:2rem] [--slider-text:13px]' },\n      lg: { root: '[--slider-h:2.5rem] [--slider-text:14px]' },\n    },\n  },\n  compoundVariants: [\n    // The line's fill is drawn solid, so a neutral one is the foreground itself.\n    { variant: 'line', color: 'neutral', className: { fill: 'bg-foreground' } },\n  ],\n  defaultVariants: { variant: 'fill', color: 'neutral', size: 'md' },\n});\n\ntype SliderValue = number | number[];\n\ninterface SliderContextValue {\n  nudge(by: number): void;\n  atMin: boolean;\n  atMax: boolean;\n  disabled: boolean;\n}\n\nconst SliderContext = createContext<SliderContextValue | null>(null);\n\nexport interface SliderProps<Value extends SliderValue = number> extends Omit<\n  BaseSlider.Root.Props<Value>,\n  'className' | 'style' | 'format' | 'orientation' | 'children' | 'render' | 'onValueChange' | 'value' | 'defaultValue'\n> {\n  /** The value, when the caller holds it. Two numbers make a range with two handles. */\n  value?: Value;\n  /** The value to start at, when the caller does not hold it. Default `min`. */\n  defaultValue?: Value;\n  /** Called with the new value while it changes, by a drag, a press, a key or a `Slider.Step`. */\n  onValueChange?: (value: Value) => void;\n  /** `fill` writes the name and value inside a bar; `line` is a thin track with a knob. Default `fill`. */\n  variant?: SliderVariant;\n  /**\n   * What it sets. Written inside a `fill` slider, and what a screen reader\n   * calls either kind.\n   */\n  label?: string;\n  /**\n   * How the value is written: number-format options such as\n   * `{ style: 'unit', unit: 'percent' }`, or a function from the number to\n   * a string for anything else, such as a multiplier written `1.40×`. Also\n   * what a screen reader reads.\n   */\n  format?: Intl.NumberFormatOptions | ((value: number) => string);\n  /** Where the fill grows from. `center` fills outwards from the middle of the range. Default `start`. */\n  origin?: 'start' | 'center';\n  /** The colour of the fill and the handle. `neutral` by default. */\n  color?: SliderColor;\n  /** Height of a `fill` slider. Default `md`. */\n  size?: SliderSize;\n  /** Show the value at the end of a `fill` slider. Default true. */\n  showValue?: boolean;\n  /** Mark every step with a notch. Default: on when there are 12 steps or fewer. */\n  ticks?: boolean;\n  /** `Slider.Step` buttons. One with a negative `by` stands before the track, a positive one after it. */\n  children?: ReactNode;\n  className?: string;\n  style?: CSSProperties;\n}\n\nconst useIsomorphicLayoutEffect = typeof window === 'undefined' ? useEffect : useLayoutEffect;\n\nconst noop = () => () => {};\nconst yes = () => true;\nconst no = () => false;\n\nconst clamp = (value: number, min: number, max: number) => Math.min(Math.max(value, min), max);\nconst toPercent = (value: number, min: number, max: number) => ((clamp(value, min, max) - min) / (max - min)) * 100;\n/** Rounds away the float noise that repeated steps leave, such as 0.30000000000000004. */\nconst tidy = (value: number, step: number) => {\n  const decimals = (String(step).split('.')[1] ?? '').length;\n  return Number(value.toFixed(Math.min(decimals + 2, 10)));\n};\n\n/**\n * A slider. `defaultValue`, or `value` with `onValueChange`; `min`, `max`\n * and `step` as for any range.\n */\nfunction SliderRoot<Value extends SliderValue = number>({\n  value: valueProp,\n  defaultValue,\n  onValueChange,\n  variant = 'fill',\n  label,\n  format,\n  origin = 'start',\n  color,\n  size,\n  showValue = true,\n  ticks,\n  min = 0,\n  max = 100,\n  step = 1,\n  disabled = false,\n  locale,\n  children,\n  className,\n  style,\n  ...props\n}: SliderProps<Value>) {\n  const slots = sliderVariants({ variant, color, size });\n  const [inner, setInner] = useState<SliderValue>(() => defaultValue ?? (origin === 'center' ? (min + max) / 2 : min));\n  const value = (valueProp ?? inner) as SliderValue;\n  const values = Array.isArray(value) ? value : [value];\n  const range = values.length > 1;\n\n  const live = useRef({ value, onValueChange, controlled: valueProp !== undefined });\n  live.current = { value, onValueChange, controlled: valueProp !== undefined };\n\n  const change = useCallback((next: SliderValue) => {\n    if (!live.current.controlled) setInner(next);\n    live.current.onValueChange?.(next as Value);\n  }, []);\n\n  // The server cannot know the reader's locale, so the page is first written\n  // in a fixed one and switches to theirs once it is running in the browser.\n  const hydrated = useSyncExternalStore(noop, yes, no);\n  const write = useMemo(() => {\n    if (typeof format === 'function') return format;\n    const formatter = new Intl.NumberFormat(locale ?? (hydrated ? undefined : 'en-US'), format);\n    return (number: number) => formatter.format(number);\n  }, [format, locale, hydrated]);\n\n  // The fill: from the start, from the middle, or between two handles.\n  const percents = values.map((each) => toPercent(each, min, max));\n  const [low, high] = range\n    ? [Math.min(...percents), Math.max(...percents)]\n    : origin === 'center'\n      ? [Math.min(50, percents[0]!), Math.max(50, percents[0]!)]\n      : [0, percents[0]!];\n\n  const count = (max - min) / step;\n  const showTicks = ticks ?? (Number.isInteger(Math.round(count * 1e6) / 1e6) && count >= 2 && count <= MAX_TICKS);\n\n  /*\n   * Press, drag and stretch. Base UI moves the value; this watches the same\n   * pointer to tell a press from a drag, which decides whether the fill\n   * glides, and to measure how far past an end the pointer has gone.\n   */\n  const control = useRef<HTMLDivElement | null>(null);\n  // Held in state as well, because the element can be replaced after\n  // hydration, and the measuring below has to follow it to the new one.\n  const [controlElement, setControlElement] = useState<HTMLDivElement | null>(null);\n  const controlRef = useCallback((element: HTMLDivElement | null) => {\n    control.current = element;\n    setControlElement(element);\n  }, []);\n  const root = useRef<HTMLDivElement>(null);\n  const [stretch, setStretch] = useState(0);\n  const press = (event: ReactPointerEvent<HTMLDivElement>) => {\n    if (disabled || event.button !== 0) return;\n    const box = control.current?.getBoundingClientRect();\n    if (!box) return;\n    const startX = event.clientX;\n    const startY = event.clientY;\n    const rtl = getComputedStyle(control.current!).direction === 'rtl';\n    const move = (next: PointerEvent) => {\n      if (Math.hypot(next.clientX - startX, next.clientY - startY) > DRAG_SLOP) {\n        root.current?.setAttribute('data-moving', '');\n      }\n      const past =\n        next.clientX < box.left ? next.clientX - box.left : next.clientX > box.right ? next.clientX - box.right : 0;\n      // Eases off, so the further the pointer goes the less the track follows.\n      const give = Math.sign(past) * STRETCH * Math.sqrt(Math.min(Math.max(Math.abs(past) - 12, 0) / 200, 1));\n      setStretch(rtl ? -give : give);\n    };\n    const release = () => {\n      root.current?.removeAttribute('data-moving');\n      setStretch(0);\n      window.removeEventListener('pointermove', move);\n      window.removeEventListener('pointerup', release);\n      window.removeEventListener('pointercancel', release);\n    };\n    window.addEventListener('pointermove', move);\n    window.addEventListener('pointerup', release);\n    window.addEventListener('pointercancel', release);\n  };\n  useEffect(() => () => root.current?.removeAttribute('data-moving'), []);\n\n  // Which handles would land on the text, measured where they are going\n  // rather than where the glide has them now.\n  const labelRef = useRef<HTMLSpanElement>(null);\n  const valueRef = useRef<HTMLSpanElement>(null);\n  const [crowded, setCrowded] = useState<boolean[]>([]);\n  const percentKey = percents.join(',');\n  useIsomorphicLayoutEffect(() => {\n    const box = controlElement;\n    if (variant !== 'fill' || !box) return;\n    const measure = () => {\n      const width = box.clientWidth;\n      const rtl = getComputedStyle(box).direction === 'rtl';\n      // Distances from the start edge, in the direction the text runs.\n      const span = (element: HTMLElement | null) => {\n        if (!element || !element.textContent) return null;\n        const start = rtl ? width - element.offsetLeft - element.offsetWidth : element.offsetLeft;\n        return [start - 6, start + element.offsetWidth + 6] as const;\n      };\n      const zones = [span(labelRef.current), span(valueRef.current)].filter((zone) => zone !== null);\n      const next = percentKey.split(',').map((percent) => {\n        const x = (Number(percent) / 100) * width;\n        const at = Math.min(Math.max(x, 8), width - 8) - 1.5;\n        return zones.some(([left, right]) => at + 3 > left && at < right);\n      });\n      setCrowded((last) => (last.length === next.length && last.every((each, i) => each === next[i]) ? last : next));\n    };\n    measure();\n    // The text can change size after the first paint, when a web font or a\n    // stylesheet arrives, without the track changing at all.\n    const observer = new ResizeObserver(measure);\n    for (const element of [box, labelRef.current, valueRef.current]) if (element) observer.observe(element);\n    let live = true;\n    document.fonts?.ready.then(() => live && measure());\n    return () => {\n      live = false;\n      observer.disconnect();\n    };\n  }, [variant, percentKey, controlElement]);\n\n  const single = values[0]!;\n  const context = useMemo<SliderContextValue>(\n    () => ({\n      nudge: (by) => {\n        if (range) return;\n        change(tidy(clamp(single + by, min, max), step));\n      },\n      atMin: !range && single <= min,\n      atMax: !range && single >= max,\n      disabled,\n    }),\n    [range, single, min, max, step, change, disabled]\n  );\n\n  const text = range ? values.map(write).join(' – ') : write(single);\n  const name = label ?? props['aria-label'];\n\n  return (\n    <SliderContext.Provider value={context}>\n      <BaseSlider.Root<Value>\n        ref={root}\n        data-slot=\"slider\"\n        data-variant={variant}\n        value={value as Value}\n        onValueChange={(next) => change(next as SliderValue)}\n        min={min}\n        max={max}\n        step={step}\n        disabled={disabled}\n        locale={locale}\n        className={slots.root({ className })}\n        style={\n          {\n            ...style,\n            '--slider-start': `${low}%`,\n            '--slider-span': `${high - low}%`,\n          } as CSSProperties\n        }\n        {...props}\n      >\n        <BaseSlider.Control\n          ref={controlRef}\n          data-slot=\"slider-control\"\n          data-stretching={stretch !== 0 ? '' : undefined}\n          onPointerDown={press}\n          className={slots.control()}\n          style={{\n            marginInlineStart: stretch < 0 ? stretch : 0,\n            marginInlineEnd: stretch > 0 ? -stretch : 0,\n          }}\n        >\n          <div data-slot=\"slider-track\" className={slots.track()}>\n            <div data-slot=\"slider-fill\" className={slots.fill()} />\n            {showTicks\n              ? Array.from({ length: Math.round(count) - 1 }, (_, at) => (\n                  <span\n                    key={at}\n                    aria-hidden\n                    className={slots.tick()}\n                    style={{ insetInlineStart: `${((at + 1) / Math.round(count)) * 100}%` }}\n                  />\n                ))\n              : null}\n            {origin === 'center' && !range ? <span aria-hidden className={slots.center()} /> : null}\n          </div>\n          {values.map((each, at) => {\n            const percent = toPercent(each, min, max);\n            // At either end the fill's handle stays clear of the rounded corner.\n            const place = variant !== 'fill' ? `${percent}%` : `clamp(8px, ${percent}%, calc(100% - 8px))`;\n            return (\n              <span\n                key={at}\n                aria-hidden\n                data-slot=\"slider-handle\"\n                data-crowded={crowded[at] ? '' : undefined}\n                className={slots.handle()}\n                style={{ ['--slider-at' as string]: place } as CSSProperties}\n              />\n            );\n          })}\n          {variant === 'fill' && (label || showValue) ? (\n            <span aria-hidden data-slot=\"slider-text\" className={slots.text()}>\n              <span ref={labelRef} className={slots.label()}>\n                {label}\n              </span>\n              {showValue ? (\n                <span ref={valueRef} className={slots.value()}>\n                  {text}\n                </span>\n              ) : null}\n            </span>\n          ) : null}\n          {values.map((_, at) => (\n            <BaseSlider.Thumb\n              key={at}\n              index={range ? at : undefined}\n              aria-label={range ? `${name ?? 'Value'}, ${at === 0 ? 'start' : 'end'}` : name}\n              getAriaValueText={(_formatted, number) => write(number)}\n              className={slots.thumb()}\n            />\n          ))}\n        </BaseSlider.Control>\n        <SliderSteps>{children}</SliderSteps>\n      </BaseSlider.Root>\n    </SliderContext.Provider>\n  );\n}\nSliderRoot.displayName = 'Slider';\n\n/** Puts each `Slider.Step` on its side of the track: a step down before, a step up after. */\nfunction SliderSteps({ children }: { children: ReactNode }) {\n  const before: ReactNode[] = [];\n  const after: ReactNode[] = [];\n  Children.forEach(children, (child) => {\n    if (isValidElement<SliderStepProps>(child) && typeof child.props.by === 'number' && child.props.by < 0) {\n      before.push(child);\n    } else {\n      after.push(child);\n    }\n  });\n  return (\n    <>\n      {before.length ? <div className=\"order-first flex shrink-0 items-center\">{before}</div> : null}\n      {after.length ? <div className=\"order-last flex shrink-0 items-center\">{after}</div> : null}\n    </>\n  );\n}\n\nexport interface SliderStepProps extends Omit<ComponentProps<'button'>, 'children'> {\n  /** How much one press moves the value. Negative steps down and sits before the track. */\n  by: number;\n  /** Usually an icon. Give the button an `aria-label` then. */\n  children?: ReactNode;\n}\n\n/**\n * A button beside the track that moves the value by `by`: zoom out and zoom\n * in on either side of a zoom slider. It is disabled at the end it moves\n * towards.\n */\nfunction SliderStep({ by, className, disabled, onClick, render, ...props }: SliderStepProps & RenderProp<'button'>) {\n  const slider = useContext(SliderContext);\n  if (!slider) throw new Error('Slider.Step must be used inside a <Slider>.');\n  const slots = sliderVariants();\n  const stuck = by < 0 ? slider.atMin : slider.atMax;\n  return useRender({\n    defaultTagName: 'button',\n    render,\n    props: mergeProps(\n      {\n        type: 'button',\n        'data-slot': 'slider-step',\n        disabled: disabled || slider.disabled || stuck,\n        className: slots.step({ className }),\n        onClick: (event: ReactTypes.MouseEvent<HTMLButtonElement>) => {\n          onClick?.(event);\n          if (!event.defaultPrevented) slider.nudge(by);\n        },\n      },\n      props\n    ),\n  });\n}\nSliderStep.displayName = 'Slider.Step';\n\nexport const Slider = Object.assign(SliderRoot, {\n  Step: SliderStep,\n});\n\nexport { SliderStep, sliderVariants };\n"
    }
  ]
}
