@tremolo-ui/react 0.8.0 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +416 -252
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +256 -110
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.ts +256 -110
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +368 -203
- package/dist/index.js.map +1 -1
- package/package.json +9 -9
- package/src/components/AnimationCanvas/index.tsx +4 -18
- package/src/components/FileInput/index.tsx +4 -1
- package/src/components/Knob/Thumb.tsx +19 -19
- package/src/components/Knob/index.tsx +85 -41
- package/src/components/NumberInput/InputField.tsx +9 -7
- package/src/components/NumberInput/Stepper.tsx +30 -6
- package/src/components/NumberInput/StepperButton.tsx +12 -4
- package/src/components/NumberInput/context.tsx +24 -2
- package/src/components/NumberInput/index.tsx +142 -27
- package/src/components/Piano/index.tsx +24 -7
- package/src/components/PointsEditor/Container.tsx +1 -1
- package/src/components/PointsEditor/Point.tsx +61 -30
- package/src/components/PointsEditor/context.tsx +3 -4
- package/src/components/PointsEditor/index.tsx +14 -15
- package/src/components/Slider/Marks.tsx +3 -2
- package/src/components/Slider/MarksOption.tsx +3 -2
- package/src/components/Slider/Thumb.tsx +20 -11
- package/src/components/Slider/Track.tsx +15 -13
- package/src/components/Slider/context.tsx +3 -3
- package/src/components/Slider/index.tsx +85 -48
- package/src/components/XYPad/Thumb.tsx +18 -8
- package/src/components/XYPad/context.tsx +1 -1
- package/src/components/XYPad/index.tsx +74 -42
- package/src/components/_util/VisuallyHiddenRangeInput.tsx +1 -1
- package/src/hooks/_internal/useChangeGesture.ts +93 -0
- package/src/hooks/_internal/useCheckSteps.ts +1 -1
- package/src/hooks/useAnimationFrame.ts +37 -15
- package/src/hooks/useEventListener.ts +93 -22
- package/src/hooks/useWheel.ts +4 -4
- package/src/index.ts +9 -5
- package/src/hooks/useInterval.ts +0 -27
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":["Root","Root","Thumb","defaultExternalStyles","WHEEL_OPTIONS","Root","Root","Root","Root","Thumb","defaultExternalStyles","WHEEL_OPTIONS","Root"],"sources":["../src/components/AnimationCanvas/index.tsx","../src/hooks/_internal/useCallbackRef.ts","../src/hooks/useDropZone.ts","../src/components/DropZone/index.tsx","../src/components/FileInput/context.tsx","../src/components/FileInput/Trigger.tsx","../src/components/FileInput/index.tsx","../src/hooks/_internal/useCheckSteps.ts","../src/hooks/useDragValue.ts","../src/hooks/useWheel.ts","../src/components/_util/Placement.tsx","../src/components/Knob/context.tsx","../src/components/Knob/ActiveLine.tsx","../src/components/Knob/InactiveLine.tsx","../src/components/Knob/SVGRoot.tsx","../src/components/Knob/Thumb.tsx","../src/components/Knob/index.tsx","../src/components/NumberInput/context.tsx","../src/hooks/useLongPress.ts","../src/components/NumberInput/StepperButton.tsx","../src/components/NumberInput/DecrementStepper.tsx","../src/components/NumberInput/IncrementStepper.tsx","../src/components/NumberInput/InputField.tsx","../src/components/NumberInput/Stepper.tsx","../src/components/NumberInput/index.tsx","../src/components/Piano/index.tsx","../src/components/PointsEditor/Background.tsx","../src/components/PointsEditor/context.tsx","../src/components/PointsEditor/Container.tsx","../src/components/_util/VisuallyHiddenRangeInput.tsx","../src/components/PointsEditor/Point.tsx","../src/components/PointsEditor/SelectionBox.tsx","../src/components/PointsEditor/index.tsx","../src/components/Slider/context.tsx","../src/components/Slider/MarksOption.tsx","../src/components/Slider/Marks.tsx","../src/components/Slider/Thumb.tsx","../src/components/Slider/Track.tsx","../src/components/Slider/index.tsx","../src/components/XYPad/context.tsx","../src/components/XYPad/Area.tsx","../src/components/XYPad/Thumb.tsx","../src/components/XYPad/index.tsx","../src/hooks/useAnimationFrame.ts","../src/hooks/useDrag.ts","../src/hooks/useEventListener.ts","../src/hooks/useInterval.ts","../src/hooks/useMIDIAccess.ts","../src/hooks/useMIDIInput.ts","../src/hooks/useMIDIMessage.ts"],"sourcesContent":["import {\n ComponentPropsWithoutRef,\n forwardRef,\n useEffect,\n useRef,\n useState,\n} from 'react'\n\nimport {\n createAnimationCanvas,\n type AnimationCanvasInstance,\n type AnimationFrame,\n} from '@tremolo-ui/dom'\n\nimport { useComposedRefs } from '../../compose-refs'\n\nexport type InitFunction = (\n context: CanvasRenderingContext2D,\n option: {\n /** current canvas width */\n width: number\n /** current canvas height */\n height: number\n },\n) => void\n\nexport type DrawFunction = (\n context: CanvasRenderingContext2D,\n option: AnimationFrame,\n) => void\n\nexport interface AnimationCanvasCommonProps {\n /**\n * Draw one frame. It is given the 2D context and the frame: the size in CSS\n * pixels, `count`, `deltaTime`, `elapsedTime` and `fps`.\n *\n * A new function replaces the old one in place, so writing it inline does\n * not restart anything.\n */\n draw: DrawFunction\n /**\n * Called once before the first frame, with the context and the size in CSS\n * pixels. Set up what every frame shares here.\n */\n init?: InitFunction\n /**\n * Redraw on every animation frame. Turn it off to draw only when there is a\n * reason to: when the canvas mounts, when it is resized, and when the\n * component re-renders.\n *\n * @default true\n */\n animate?: boolean\n /**\n * Read once, when the 2D context is created, so changing it later has no\n * effect. Passing a fresh object on every render is therefore harmless.\n *\n * @see https://developer.mozilla.org/docs/Web/API/HTMLCanvasElement/getContext#contextattributes\n */\n options?: CanvasRenderingContext2DSettings\n /**\n * Carry the drawing across a resize, so that the canvas does not blank for a\n * frame while the new size is drawn. A fixed canvas is resized too, when its\n * `width` or `height` changes.\n *\n * @default true\n */\n reduceFlickering?: boolean\n}\n\n/** A canvas of the size given in CSS pixels. This is the default. */\nexport interface AnimationCanvasFixedProps {\n /** Leave it out, or pass `false`, for a canvas of the size below. */\n resizable?: false\n /**\n * Width of the canvas in CSS pixels.\n * @default 100\n */\n width?: number\n /**\n * Height of the canvas in CSS pixels.\n * @default 100\n */\n height?: number\n}\n\n/**\n * A canvas that follows the size of its parent element, so the parent needs a\n * size of its own.\n */\nexport interface AnimationCanvasResizableProps {\n /**\n * Follow the size of the parent element instead of `width` and `height`.\n * Switching it rebuilds the canvas.\n */\n resizable: true\n /** Not accepted: the size comes from the parent element. */\n width?: never\n /** Not accepted: the size comes from the parent element. */\n height?: never\n}\n\nexport type AnimationCanvasProps = AnimationCanvasCommonProps &\n (AnimationCanvasFixedProps | AnimationCanvasResizableProps)\n\ntype Props = AnimationCanvasProps &\n Omit<\n ComponentPropsWithoutRef<'canvas'>,\n keyof AnimationCanvasCommonProps | 'resizable' | 'width' | 'height'\n >\n\n/**\n * A simple animatable canvas with requestAnimationFrame()\n */\nexport const AnimationCanvas = /* @__PURE__ */ forwardRef<\n HTMLCanvasElement,\n Props\n>(function AnimationCanvas(\n {\n // common\n draw,\n init,\n animate = true,\n options,\n reduceFlickering = true,\n // fixed\n width = 100,\n height = 100,\n // resizable\n resizable = false,\n // canvas props\n className,\n onContextMenu = (event) => event.preventDefault(),\n ...props\n },\n forwardedRef,\n) {\n // See useDrag for why the node is held in state rather than a ref: an inline\n // ref would be re-attached on every render and tear the instance down.\n const [node, setNode] = useState<HTMLCanvasElement | null>(null)\n // The caller's ref gets the same element.\n const composedRef = useComposedRefs<HTMLCanvasElement>(forwardedRef, setNode)\n\n // Read when the instance is created. The effect below keeps it current, and\n // runs right after, so a stale handler is replaced within the same commit.\n const latest = useRef({\n draw,\n init,\n animate,\n width,\n height,\n reduceFlickering,\n options,\n })\n const instanceRef = useRef<AnimationCanvasInstance | null>(null)\n\n // Runs before the creation effect below. This keeps a rebuilt instance on\n // the current render's size and handlers while updating an existing one in\n // place on ordinary renders.\n useEffect(() => {\n latest.current = {\n draw,\n init,\n animate,\n width,\n height,\n reduceFlickering,\n options,\n }\n instanceRef.current?.update({\n animate,\n size: { width, height },\n reduceFlickering,\n })\n })\n\n useEffect(() => {\n if (!node) return\n\n const current = latest.current\n const instance = createAnimationCanvas(node, {\n draw: (context, frame) => latest.current.draw(context, frame),\n init: (context, size) => latest.current.init?.(context, size),\n animate: current.animate,\n size: { width: current.width, height: current.height },\n reduceFlickering: current.reduceFlickering,\n resizable,\n contextAttributes: current.options,\n })\n instanceRef.current = instance\n\n return () => {\n instanceRef.current = null\n instance.destroy()\n }\n // Only `resizable` decides how the instance is wired, so it is the one\n // setting that rebuilds it. `options` is read from the ref above rather\n // than depended on: it is almost always written inline, and depending on\n // it would tear the canvas down on every render.\n }, [node, resizable])\n\n return (\n <canvas\n className={className}\n ref={composedRef}\n onContextMenu={onContextMenu}\n {...props}\n />\n )\n})\n","import { DependencyList, useCallback, useInsertionEffect, useRef } from 'react'\n\n/**\n * Internal\n * @private\n */\nexport function useCallbackRef<Args extends unknown[], Return>(\n callback: ((...args: Args) => Return) | undefined,\n deps: DependencyList = [],\n) {\n const callbackRef = useRef<typeof callback>(() => {\n throw new Error('Cannot call an event handler while rendering.')\n })\n\n useInsertionEffect(() => {\n callbackRef.current = callback\n })\n\n // `deps` comes from the caller, so it cannot be the array literal the\n // compiler rules expect.\n // oxlint-disable-next-line react-hooks/exhaustive-deps, react/use-memo\n return useCallback((...args: Args) => callbackRef.current?.(...args), deps)\n}\n","import { useEffect, useRef, useState } from 'react'\n\nimport {\n createDropZone,\n type DropZoneInstance,\n type DropZoneOptions,\n} from '@tremolo-ui/dom'\n\nimport { useCallbackRef } from './_internal/useCallbackRef'\n\n// `onStateChange` is left out: what it reports comes back from the hook as\n// `over` and `invalid`, which is what a component renders from.\nexport interface UseDropZoneOptions extends Omit<\n DropZoneOptions,\n 'onStateChange'\n> {}\n\n/**\n * Take files dropped onto an element.\n *\n * Any element can become a drop target this way — a waveform display, a track\n * lane, a canvas — without wrapping it in anything.\n *\n * @returns a ref callback for the element, and whether a drag is over it\n */\nexport function useDropZone<T extends Element>({\n accept,\n multiple,\n disabled,\n onDrop,\n onReject,\n}: UseDropZoneOptions = {}): {\n refCallback: (node: T | null) => void\n over: boolean\n invalid: boolean\n} {\n const dropHandler = useCallbackRef(onDrop)\n const rejectHandler = useCallbackRef(onReject)\n\n const [state, setState] = useState({ over: false, invalid: false })\n\n // See useDrag for why the node is held in state.\n const [node, setNode] = useState<T | null>(null)\n\n const instanceRef = useRef<DropZoneInstance | null>(null)\n\n // Read when the instance is created. The effect below keeps it current, and\n // runs right after, so a stale setting is replaced within the same commit.\n const latest = useRef({ accept, multiple, disabled })\n\n useEffect(() => {\n if (!node) return\n\n const instance = createDropZone(node, {\n ...latest.current,\n onDrop: (files, event) => dropHandler(files, event),\n onReject: (files, event) => rejectHandler(files, event),\n onStateChange: (next) => setState(next),\n })\n instanceRef.current = instance\n\n return () => {\n instanceRef.current = null\n instance.destroy()\n // The listeners are gone, so no `dragleave` is coming to clear this.\n setState({ over: false, invalid: false })\n }\n }, [node, dropHandler, rejectHandler])\n\n // Runs after every render: the settings come from props and are cheap to\n // push, and updating in place leaves a drag in progress untouched.\n useEffect(() => {\n latest.current = { accept, multiple, disabled }\n instanceRef.current?.update({ accept, multiple, disabled })\n })\n\n return { refCallback: setNode, over: state.over, invalid: state.invalid }\n}\n","import { ComponentPropsWithoutRef, forwardRef, ReactNode } from 'react'\n\nimport { useComposedRefs } from '../../compose-refs'\nimport { useDropZone } from '../../hooks/useDropZone'\n\nexport interface DropZoneProps {\n /**\n * Which files to take, written the way the `accept` attribute of a file\n * input is: a comma separated list of extensions (`.wav`), MIME types\n * (`audio/wav`) and type groups (`audio/*`).\n *\n * What does not match goes to `onReject` rather than `onDrop`. While the\n * drag is still in the air the element is marked `[data-invalid]`, as far as\n * the browser lets it be told apart — a rule written as an extension cannot\n * be decided until the drop.\n */\n accept?: string\n /**\n * Take more than one file from a single drop. With it off, only the first\n * accepted file is reported, as a file input without `multiple` does.\n *\n * @default false\n */\n multiple?: boolean\n /**\n * Refuse the drop. The drag is still swallowed rather than let through: an\n * unhandled drop makes the browser leave the page and open the file.\n *\n * @default false\n */\n disabled?: boolean\n\n /**\n * Called with the dropped files that match `accept`.\n *\n * @param event the drop, for anything the files leave out — where on the\n * element it landed, most of all.\n */\n onDrop?: (files: File[], event: DragEvent) => void\n /**\n * Called with the dropped files that do not match `accept`, so that the\n * reason can be shown. It comes after `onDrop` for the same drop, so a list\n * of rejected files can be cleared in `onDrop` and filled here.\n */\n onReject?: (files: File[], event: DragEvent) => void\n\n /** What the zone shows. */\n children: ReactNode\n}\n\ntype Props = DropZoneProps &\n Omit<ComponentPropsWithoutRef<'div'>, keyof DropZoneProps>\n\n/**\n * An area that takes files dropped onto it.\n *\n * To make an element you already have into a drop target, without wrapping it\n * in anything, use the `useDropZone` hook this is built on.\n */\nexport const Root = /* @__PURE__ */ forwardRef<HTMLDivElement, Props>(\n (\n {\n accept,\n multiple = false,\n disabled = false,\n onDrop,\n onReject,\n children,\n ...props\n },\n ref,\n ) => {\n const { refCallback, over, invalid } = useDropZone<HTMLDivElement>({\n accept,\n multiple,\n disabled,\n onDrop,\n onReject,\n })\n const composedRef = useComposedRefs<HTMLDivElement>(ref, refCallback)\n\n return (\n <div\n ref={composedRef}\n data-dragover={over ? '' : undefined}\n data-invalid={invalid ? '' : undefined}\n data-disabled={disabled ? '' : undefined}\n {...props}\n >\n {children}\n </div>\n )\n },\n)\n\n/**\n * An area that takes files dropped onto it.\n */\nexport const DropZone = {\n Root,\n}\n","import { createContext, useContext } from 'react'\n\nexport type FileInputContextValue = {\n /** The id of the hidden `<input type=\"file\">`, for `Trigger`'s `htmlFor`. */\n inputId: string\n disabled: boolean\n}\n\nconst FileInputContext =\n /* @__PURE__ */ createContext<FileInputContextValue | null>(null)\n\nexport const FileInputProvider = FileInputContext.Provider\n\nexport function useFileInputContext(): FileInputContextValue\nexport function useFileInputContext<T>(\n selector: (state: FileInputContextValue) => T,\n): T\nexport function useFileInputContext<T>(\n selector?: (state: FileInputContextValue) => T,\n) {\n const context = useContext(FileInputContext)\n if (!context) throw new Error('Missing FileInputContext.Provider in the tree')\n return selector ? selector(context) : context\n}\n","import { ComponentPropsWithoutRef, forwardRef, ReactNode } from 'react'\n\nimport { useFileInputContext } from './context'\n\nexport interface FileInputTriggerProps {\n /** What the trigger reads. It is the accessible name of the file input. */\n children: ReactNode\n}\n\ntype Props = FileInputTriggerProps &\n Omit<\n ComponentPropsWithoutRef<'label'>,\n keyof FileInputTriggerProps | 'htmlFor'\n >\n\n/**\n * Opens the file picker, and names the input for a screen reader.\n *\n * A `<label>` rather than a `<button>`: the browser forwards the click to the\n * input on its own, the input stays the one thing in the tab order, and its\n * accessible name comes from this text without an `aria-label` to keep in\n * step. A disabled input ignores the click, so nothing has to be intercepted.\n */\nexport const Trigger = /* @__PURE__ */ forwardRef<HTMLLabelElement, Props>(\n ({ children, ...props }, ref) => {\n const { inputId, disabled } = useFileInputContext()\n\n return (\n <label\n ref={ref}\n htmlFor={inputId}\n data-disabled={disabled ? '' : undefined}\n {...props}\n >\n {children}\n </label>\n )\n },\n)\n","import {\n ChangeEvent,\n ComponentPropsWithoutRef,\n forwardRef,\n ReactNode,\n useId,\n useMemo,\n} from 'react'\n\nimport { partitionByAccept, visuallyHiddenStyle } from '@tremolo-ui/dom'\n\nimport { FileInputProvider } from './context'\nimport { Trigger } from './Trigger'\n\nexport interface FileInputProps {\n /**\n * Which files to take, written the way the HTML attribute is: a comma\n * separated list of extensions (`.wav`), MIME types (`audio/wav`) and type\n * groups (`audio/*`).\n *\n * **The browser treats it as a hint to the picker, not a rule.** A person\n * can switch the picker to \"All Files\", so what arrives is checked again\n * here, and anything that does not match goes to `onReject` instead.\n */\n accept?: string\n /**\n * Let more than one file be picked at a time.\n * @default false\n */\n multiple?: boolean\n /**\n * Refuse to open the picker, and mark every part with `[data-disabled]`.\n * @default false\n */\n disabled?: boolean\n\n /**\n * Called with the files that were picked and match `accept`.\n *\n * It is not called at all when the picker is dismissed, or when every file\n * picked was rejected — an empty array never arrives, so there is no need to\n * tell \"nothing was chosen\" from \"the choice was cleared\".\n */\n onChange?: (files: File[]) => void\n /**\n * Called with the files that were picked and do not match `accept`, so that\n * the reason can be shown. Given alongside `onChange` when a selection held\n * both, and after it, so a list of rejected files can be cleared in\n * `onChange` and filled here.\n */\n onReject?: (files: File[]) => void\n\n /** Normally a `FileInput.Trigger`, and whatever shows what was picked. */\n children: ReactNode\n}\n\ntype Props = FileInputProps &\n Omit<ComponentPropsWithoutRef<'div'>, keyof FileInputProps>\n\n/**\n * Hands over the files a person picked.\n *\n * The `<input type=\"file\">` is here, kept out of sight but in the tab order,\n * because it is the control: it takes the focus, opens the picker on Enter,\n * and carries the accessible name. What is on screen is whatever the children\n * draw.\n */\nexport const Root = /* @__PURE__ */ forwardRef<HTMLDivElement, Props>(\n (\n {\n accept,\n multiple = false,\n disabled = false,\n onChange,\n onReject,\n children,\n // Pulled out of `props` so that they land on the input rather than on\n // the wrapper: it is the control, and a description is only announced\n // from the element that takes the focus. No declaration in\n // `FileInputProps` is needed for this — they are already on the div\n // attributes it is merged with, with the same types.\n 'aria-label': ariaLabel,\n 'aria-labelledby': ariaLabelledby,\n 'aria-describedby': ariaDescribedby,\n ...props\n },\n ref,\n ) => {\n const inputId = useId()\n\n const context = useMemo(() => ({ inputId, disabled }), [inputId, disabled])\n\n function handleChange(event: ChangeEvent<HTMLInputElement>) {\n const { accepted, rejected } = partitionByAccept(\n Array.from(event.target.files ?? []),\n accept,\n )\n\n // Picking the same file twice fires no second change event while the\n // value is still on the input, so it is cleared as soon as it is read.\n // Nothing is lost: the files are already in hand.\n event.target.value = ''\n\n if (accepted.length > 0) onChange?.(accepted)\n if (rejected.length > 0) onReject?.(rejected)\n }\n\n return (\n <FileInputProvider value={context}>\n <div ref={ref} data-disabled={disabled ? '' : undefined} {...props}>\n <input\n id={inputId}\n type=\"file\"\n accept={accept}\n multiple={multiple}\n disabled={disabled}\n style={visuallyHiddenStyle}\n aria-label={ariaLabel}\n aria-labelledby={ariaLabelledby}\n aria-describedby={ariaDescribedby}\n onChange={handleChange}\n />\n {children}\n </div>\n </FileInputProvider>\n )\n },\n)\n\n/**\n * Picks files, and hands them over as `File`s.\n */\nexport const FileInput = {\n Root,\n Trigger,\n}\n\nexport { useFileInputContext, type FileInputContextValue } from './context'\nexport { type FileInputTriggerProps } from './Trigger'\n","import { useEffect } from 'react'\n\nimport { checkSteps, type CheckStepsOptions } from '@tremolo-ui/dom'\n\n/**\n * Warn, in development only, when a key press or a wheel notch cannot produce\n * a change the user can see. The probing is `checkSteps` in the core.\n *\n * @internal\n */\nexport function useCheckSteps(options: CheckStepsOptions) {\n const { component, axis, range, keyboard, wheel } = options\n const { min, max, step, scale } = range ?? {}\n\n useEffect(() => {\n try {\n // Inline and first, so a bundler folds the comparison and drops the\n // whole block — and with it the only call to `checkSteps`, which then\n // falls out of the bundle too. See `useCheckPlacement` for why it\n // cannot go through a helper, and why it is wrapped.\n if (process.env.NODE_ENV === 'production') return\n for (const warning of checkSteps(options)) console.warn(warning)\n } catch {\n // No bundler substituted NODE_ENV, so a production build cannot be told\n // from a development one. Say nothing rather than break.\n }\n // `range` and `format` are rebuilt on most renders, so the effect is keyed\n // on what actually decides the outcome. `format` is left out on purpose:\n // an inline arrow function would make this run every render.\n // oxlint-disable-next-line react-hooks/exhaustive-deps\n }, [component, axis, min, max, step, scale, keyboard, wheel])\n}\n","import { RefObject, useEffect, useRef, useState } from 'react'\n\nimport {\n createDragValue,\n elementMapping,\n relativeMapping,\n type AxisOptions,\n type DragState,\n type DragValueInstance,\n type XY,\n type XYInput,\n} from '@tremolo-ui/dom'\n\nimport { useCallbackRef } from './_internal/useCallbackRef'\n\nexport interface UseDragValueOptions {\n /** Scaling of each axis; a single value applies to both. */\n axis: XYInput<AxisOptions>\n\n /**\n * Normalize the pointer against the bounding rect of this element, so that\n * the value *is* the position pointed at.\n *\n * Give either this or `getValue`.\n */\n baseElementRef?: RefObject<Element | null>\n\n /**\n * Move the value away from where it stood when the drag started, by the\n * distance dragged. The pointer position itself carries no meaning.\n *\n * Give either this or `baseElementRef`.\n */\n getValue?: () => XY<number>\n /**\n * Pixels of movement that span the whole range, with `getValue`.\n * @default 100\n */\n pixelRange?: XYInput<number>\n /**\n * How much the movement counts, read on every move. `0.1` makes the same\n * movement cover a tenth of the range, which is what a fine-adjustment\n * modifier wants.\n *\n * With `baseElementRef` the value is normally the position pointed at, so\n * anything but `1` turns the mapping relative and leaves the pointer and the\n * value apart for the rest of the drag.\n *\n * @see relativeMapping\n * @see elementMapping\n */\n sensitivity?: (state: DragState) => number\n\n /**\n * Move the value on pointerdown, before the pointer has moved at all. With\n * `baseElementRef` that makes a press jump the value to where it landed.\n *\n * @see DragValueOptions.updateOnPointerDown\n */\n updateOnPointerDown?: boolean\n /**\n * Pixels the pointer has to travel before the drag counts as one. The\n * default is 0, so the value follows the very first move.\n *\n * @default 0\n */\n threshold?: number\n /** CSS cursor to show while dragging. Applied to the element itself. */\n cursor?: string\n /**\n * Hide the pointer and read its movement directly, instead of following it\n * around the screen. Only with `getValue`: there is no pointer position to\n * normalize against a `baseElementRef` while the pointer is locked.\n *\n * @see DragOptions.pointerLock\n * @default false\n */\n pointerLock?: boolean\n /**\n * Decide whether a pointerdown starts a drag at all, before the pointer is\n * captured.\n *\n * @see DragOptions.shouldStart\n */\n shouldStart?: (event: PointerEvent) => boolean\n\n /**\n * Called with the new value whenever the drag moves it. The hook holds no\n * value of its own, so this is where it is stored.\n *\n * @param state the whole drag, for anything the value leaves out — the\n * pointer event and its modifier keys, most of all.\n */\n onChange?: (value: XY<number>, state: DragState) => void\n /** Called once the drag starts, with the value it starts from. */\n onDragStart?: (value: XY<number>, state: DragState) => void\n /** Called when the pointer is released, with the value it ends on. */\n onDragEnd?: (value: XY<number>, state: DragState) => void\n}\n\n/**\n * Drive a value with a pointer drag.\n *\n * @returns a ref callback for the element that starts the drag, and whether a drag is in progress\n */\nexport function useDragValue<T extends Element>(\n options: UseDragValueOptions,\n): { refCallback: (node: T | null) => void; dragging: boolean } {\n const {\n axis,\n baseElementRef,\n pixelRange,\n updateOnPointerDown,\n threshold,\n cursor,\n pointerLock,\n } = options\n\n if (!baseElementRef && !options.getValue) {\n throw new Error(\n 'useDragValue: give either baseElementRef or getValue, so that the drag has something to move',\n )\n }\n\n const [dragging, setDragging] = useState(false)\n\n const valueGetter = useCallbackRef(options.getValue)\n const changeHandler = useCallbackRef(options.onChange)\n const dragStartHandler = useCallbackRef(options.onDragStart)\n const dragEndHandler = useCallbackRef(options.onDragEnd)\n\n // Read when the instance is created. The effect below keeps it current, and\n // runs right after, so a stale setting is replaced within the same commit.\n const latest = useRef(options)\n\n const instanceRef = useRef<DragValueInstance | null>(null)\n\n // See useDrag for why the node is held in state.\n const [node, setNode] = useState<T | null>(null)\n\n const [pixelRangeX, pixelRangeY] = Array.isArray(pixelRange)\n ? pixelRange\n : [pixelRange, pixelRange]\n\n useEffect(() => {\n if (!node) return\n\n const instance = createDragValue(node, {\n axis: latest.current.axis,\n mapping: baseElementRef\n ? elementMapping(() => baseElementRef.current, {\n // Read through the ref so that a changed setting reaches a drag\n // already in progress.\n sensitivity: (state) => latest.current.sensitivity?.(state) ?? 1,\n })\n : relativeMapping({\n pixelRange: [pixelRangeX ?? 100, pixelRangeY ?? 100],\n // Read through the ref so that a changed setting reaches a drag\n // already in progress.\n sensitivity: (state) => latest.current.sensitivity?.(state) ?? 1,\n }),\n getValue: () => valueGetter() ?? [0, 0],\n updateOnPointerDown: latest.current.updateOnPointerDown,\n threshold: latest.current.threshold,\n cursor: latest.current.cursor,\n pointerLock: latest.current.pointerLock,\n shouldStart: (event) => latest.current.shouldStart?.(event) ?? true,\n onChange: (value, state) => changeHandler(value, state),\n onDragStart: (value, state) => {\n setDragging(true)\n dragStartHandler(value, state)\n },\n onDragEnd: (value, state) => {\n setDragging(false)\n dragEndHandler(value, state)\n },\n })\n instanceRef.current = instance\n\n return () => {\n instanceRef.current = null\n instance.destroy()\n }\n }, [\n node,\n baseElementRef,\n pixelRangeX,\n pixelRangeY,\n valueGetter,\n changeHandler,\n dragStartHandler,\n dragEndHandler,\n ])\n\n // Runs after every render: the settings come from props and are cheap to\n // push, and updating in place leaves a drag in progress untouched.\n useEffect(() => {\n latest.current = options\n instanceRef.current?.update({\n axis,\n updateOnPointerDown,\n threshold,\n cursor,\n pointerLock,\n })\n })\n\n return { refCallback: setNode, dragging }\n}\n","import { RefObject, useEffect, useState } from 'react'\n\nimport { createWheel, type WheelOptions } from '@tremolo-ui/dom'\n\nimport { useCallbackRef } from './_internal/useCallbackRef'\n\n// `onWheel` is left out: `createWheel` takes it so that `update()` can replace\n// the callback, but this hook takes the handler as its first argument and the\n// option would be silently ignored.\nexport interface UseWheelOptions extends Omit<WheelOptions, 'onWheel'> {\n /**\n * Listen on this element rather than on the one the returned ref callback is\n * attached to, and ignore that callback.\n *\n * For a control made of several movable parts: a wheel event only reaches\n * what the cursor is over, so a listener per part responds only while the\n * cursor is over that same part. Listening on the element they share lets\n * every part see the event, and each one decide whether it is the one to act.\n *\n * The element is read when the listener is attached, so a ref filled in by a\n * parent is fine as long as the parent is above in the tree.\n */\n target?: RefObject<Element | null>\n}\n\n/**\n * Listen to wheel events on an element.\n *\n * The listener is not passive, so the handler may call `preventDefault()`.\n *\n * @returns a ref callback to attach to the element, unused when `target` is given\n */\nexport function useWheel<T extends Element>(\n onWheel: (event: WheelEvent) => void,\n { requireFocus = false, target }: UseWheelOptions = {},\n): (node: T | null) => void {\n const wheelHandler = useCallbackRef(onWheel)\n\n // See useDrag for why the node is held in state.\n const [node, setNode] = useState<T | null>(null)\n\n useEffect(() => {\n const element = target ? target.current : node\n if (!element) return\n\n const instance = createWheel(element, (event) => wheelHandler(event), {\n requireFocus,\n })\n\n return () => instance.destroy()\n }, [node, target, wheelHandler, requireFocus])\n\n return setNode\n}\n","import { createContext, ReactNode, useContext, useEffect } from 'react'\n\n/**\n * The subcomponent that established the current placement, if any.\n *\n * @internal\n */\nconst PlacementContext = /* @__PURE__ */ createContext<string | null>(null)\n\n/**\n * Marks its children as being inside `name`, for {@link useCheckPlacement}.\n *\n * @internal\n */\nexport function Placement({\n name,\n children,\n}: {\n name: string\n children: ReactNode\n}) {\n return (\n <PlacementContext.Provider value={name}>\n {children}\n </PlacementContext.Provider>\n )\n}\n\n/**\n * Warn, in development only, when `child` is not rendered inside `parent`.\n *\n * A compound component renders its children exactly as they are composed, so a\n * subcomponent put at the wrong level still renders — it only loses whatever\n * its layout depended on, and does so silently. `Slider.Thumb` outside\n * `Slider.Track` keeps its `position: absolute` but resolves it against the\n * page rather than the track, and neither the build nor the tests notice.\n *\n * @internal\n */\nexport function useCheckPlacement(child: string, parent: string) {\n const found = useContext(PlacementContext)\n\n useEffect(() => {\n try {\n // `process.env.NODE_ENV` is written out here, inline and first, so that a\n // bundler substituting it folds the comparison and drops this whole\n // block — the message strings with it. Reading the flag through a helper\n // or a module constant leaves the branch in the bundle instead: neither\n // folds, which was measured with esbuild rather than assumed.\n //\n // The read is wrapped because `process` may not exist at all. The\n // package is built with `platform: 'neutral'`, so a page loading the ESM\n // straight from a CDN has no bundler to substitute it, and a bare\n // reference would throw a `ReferenceError` mid-effect.\n if (process.env.NODE_ENV !== 'production' && found !== parent) {\n console.warn(\n `[tremolo-ui] ${child} has to be rendered inside ${parent}` +\n (found === null ? '.' : `, but it is inside ${found}.`) +\n ' It renders either way, so nothing fails — its position just' +\n ' comes out wrong.',\n )\n }\n } catch {\n // No bundler substituted NODE_ENV, so there is no way to tell a\n // production build from a development one. Say nothing rather than break.\n }\n }, [found, child, parent])\n}\n","import { createContext, useContext } from 'react'\n\nimport { type KnobAngles } from '@tremolo-ui/dom'\nimport { type Scale } from '@tremolo-ui/functions'\n\nexport type KnobConfig = {\n value: number\n min: number\n max: number\n step: number\n scale: Scale\n startValue: number\n /** angle range [degree] */\n angleRange: number\n}\n\nexport type KnobContextValue = KnobConfig & KnobAngles\n\nconst KnobContext = /* @__PURE__ */ createContext<KnobContextValue | null>(null)\n\nexport const KnobProvider = KnobContext.Provider\n\n/**\n * Everything here is derived during render, so there is no state to keep in\n * sync: `value` comes from the props of `Root` and the rest follows from it.\n */\nexport function useKnobContext(): KnobContextValue\nexport function useKnobContext<T>(selector: (state: KnobContextValue) => T): T\nexport function useKnobContext<T>(selector?: (state: KnobContextValue) => T) {\n const context = useContext(KnobContext)\n if (!context) throw new Error('Missing KnobContext.Provider in the tree')\n return selector ? selector(context) : context\n}\n","import { SVGProps } from 'react'\n\nimport { knobArcPath, knobArcRadius } from '@tremolo-ui/dom'\n\nimport { useCheckPlacement } from '../_util/Placement'\n\nimport { useKnobContext } from './context'\n\nexport function ActiveLine({\n stroke = 'currentColor',\n strokeWidth = 6,\n className,\n ...props\n}: Omit<SVGProps<SVGPathElement>, 'd'>) {\n useCheckPlacement('Knob.ActiveLine', 'Knob.SVGRoot')\n\n const r2 = useKnobContext((s) => s.r2)\n const r3 = useKnobContext((s) => s.r3)\n\n const radius = knobArcRadius(strokeWidth)\n\n return (\n <path\n className={className}\n d={knobArcPath(r2, r3, radius)}\n fill=\"none\"\n stroke={stroke}\n strokeWidth={strokeWidth}\n {...props}\n />\n )\n}\n","import { SVGProps } from 'react'\n\nimport { knobArcPath, knobArcRadius } from '@tremolo-ui/dom'\n\nimport { useCheckPlacement } from '../_util/Placement'\n\nimport { useKnobContext } from './context'\n\nexport function InactiveLine({\n stroke = 'currentColor',\n strokeWidth = 6,\n className,\n ...props\n}: Omit<SVGProps<SVGPathElement>, 'd'>) {\n useCheckPlacement('Knob.InactiveLine', 'Knob.SVGRoot')\n\n const min = useKnobContext((s) => s.min)\n const max = useKnobContext((s) => s.max)\n const startValue = useKnobContext((s) => s.startValue)\n\n const r1 = useKnobContext((s) => s.r1)\n const r2 = useKnobContext((s) => s.r2)\n const r3 = useKnobContext((s) => s.r3)\n const r4 = useKnobContext((s) => s.r4)\n\n const radius = knobArcRadius(strokeWidth)\n\n return (\n <>\n {startValue > min && (\n <path\n className={className}\n d={knobArcPath(r1, r2, radius)}\n fill=\"none\"\n stroke={stroke}\n strokeWidth={strokeWidth}\n {...props}\n />\n )}\n {startValue < max && (\n <path\n className={className}\n d={knobArcPath(r3, r4, radius)}\n fill=\"none\"\n stroke={stroke}\n strokeWidth={strokeWidth}\n {...props}\n />\n )}\n </>\n )\n}\n","import { ReactNode, SVGProps } from 'react'\n\nimport { KNOB_VIEWBOX_SIZE } from '@tremolo-ui/dom'\n\nimport { Placement } from '../_util/Placement'\n\nexport interface KnobSVGRootProps {\n /**\n * `<Knob.InactiveLine />`, `<Knob.ActiveLine />` and `<Knob.Thumb />` go\n * here, in the order you want them painted.\n */\n children: ReactNode\n}\n\nexport function SVGRoot({\n children,\n style,\n ...props\n}: KnobSVGRootProps & Omit<SVGProps<SVGSVGElement>, keyof KnobSVGRootProps>) {\n return (\n <svg\n viewBox={`0 0 ${KNOB_VIEWBOX_SIZE} ${KNOB_VIEWBOX_SIZE}`}\n style={{\n display: 'block',\n ...style,\n }}\n {...props}\n >\n <Placement name=\"Knob.SVGRoot\">{children}</Placement>\n </svg>\n )\n}\n","import { SVGProps } from 'react'\n\nimport { KNOB_VIEWBOX_SIZE } from '@tremolo-ui/dom'\nimport { clamp } from '@tremolo-ui/functions'\n\nimport { useCheckPlacement } from '../_util/Placement'\n\nimport { useKnobContext } from './context'\n\nexport interface KnobThumbProps {\n /**\n * Fill colour of the circle.\n * @default 'currentColor'\n */\n thumb?: string\n /**\n * Colour of the line that points at the value.\n * @default 'currentColor'\n */\n thumbLine?: string\n /**\n * Diameter of the circle, as a percentage of the knob.\n * @default 84\n */\n thumbSize?: number\n /**\n * Thickness of the line, as a percentage of the knob.\n * @default 6\n */\n thumbLineWeight?: number\n /**\n * How far down the line reaches, as a percentage of the knob from its top.\n * The line starts at the edge of the circle.\n * @default 35\n */\n thumbLineLength?: number\n\n /**\n * Classes for what the thumb draws inside itself: `thumbLine` is the line\n * that points at the value.\n */\n classes?: {\n thumbLine?: string\n }\n}\n\nexport function Thumb({\n className,\n\n thumb = 'currentColor',\n thumbLine = 'currentColor',\n thumbSize = 84,\n thumbLineWeight = 6,\n thumbLineLength = 35,\n classes,\n\n ...props\n}: KnobThumbProps & Omit<SVGProps<SVGSVGElement>, 'd' | keyof KnobThumbProps>) {\n useCheckPlacement('Knob.Thumb', 'Knob.SVGRoot')\n\n const angleRange = useKnobContext((s) => s.angleRange)\n\n const p = useKnobContext((s) => s.p)\n const r1 = useKnobContext((s) => s.r1)\n\n return (\n <svg className={className} {...props}>\n <circle cx=\"50%\" cy=\"50%\" r={`${thumbSize / 2}%`} fill={thumb} />\n <line\n className={classes?.thumbLine}\n x1=\"50%\"\n y1={`${(KNOB_VIEWBOX_SIZE - clamp(thumbSize, 0, 100)) / 2}%`}\n x2=\"50%\"\n y2={`${thumbLineLength}%`}\n stroke={thumbLine}\n strokeWidth={`${thumbLineWeight}%`}\n // NOTE\n // https://bugs.webkit.org/show_bug.cgi?id=201854\n // https://developer.mozilla.org/en-US/docs/Web/SVG/Reference/Attribute/transform-origin#browser_compatibility\n style={{\n transform: `rotate(${r1 + p * angleRange}deg)`,\n transformOrigin: '50% 50%',\n }}\n />\n </svg>\n )\n}\n","import {\n ComponentPropsWithoutRef,\n CSSProperties,\n forwardRef,\n ReactNode,\n useCallback,\n useImperativeHandle,\n useMemo,\n useRef,\n} from 'react'\n\nimport {\n applyDelta,\n arrowKeyDirection,\n type AxisOptions,\n cssLength,\n DEFAULT_DRAG_SENSITIVITY,\n DEFAULT_KEYBOARD_OPTIONS,\n DEFAULT_WHEEL_OPTIONS,\n type InputEventOption,\n knobAngles,\n type ModifierValue,\n selectModifier,\n wheelDirection,\n type XY,\n} from '@tremolo-ui/dom'\nimport { linearScale, type Scale, type ValueRange } from '@tremolo-ui/functions'\n\nimport { useComposedRefs } from '../../compose-refs'\nimport { useCheckSteps } from '../../hooks/_internal/useCheckSteps'\nimport { useDragValue } from '../../hooks/useDragValue'\nimport { useWheel } from '../../hooks/useWheel'\n\nimport { ActiveLine } from './ActiveLine'\nimport { KnobProvider } from './context'\nimport { InactiveLine } from './InactiveLine'\nimport { SVGRoot } from './SVGRoot'\nimport { Thumb } from './Thumb'\n\nimport type { CSSVariables } from '../../css-variables'\n\nconst defaultExternalStyles: KnobProps['externalStyles'] = {\n cursor: 'grabbing',\n}\n\nexport interface KnobProps {\n /** The current value. The knob shows only this, so update it from `onChange`. */\n value: number\n /** The value with the knob turned all the way down. */\n min: number\n /** The value with the knob turned all the way up. */\n max: number\n\n /**\n * Granularity of the value. A drag, the wheel and the arrow keys snap it to\n * multiples of `step`.\n *\n * @default 1\n */\n step?: number\n /**\n * How the value is distributed across the travel.\n *\n * Pick one of the scales from `@tremolo-ui/functions`: `linearScale`,\n * `exponentialScale`, `curveScale(n)`, `symmetricSkewScale(n)`, or\n * `skewScale(n)` for a value that has to match a JUCE parameter.\n *\n * @default linearScale\n */\n scale?: Scale\n /**\n * The value a double click restores, while `enableDoubleClickDefault` is on.\n * @default min\n */\n defaultValue?: number\n\n /**\n * Where the active arc starts. Put it at the centre of a bipolar control,\n * such as a pan knob, so that the arc grows from there either way.\n * @default min\n */\n startValue?: number\n\n /**\n * Width and height of the knob. Sets `--knob-size`; the size the theme\n * gives it stands when this is omitted.\n */\n size?: number | string\n\n /**\n * The cursor to show while dragging. It is set on the dragged element, so it\n * stays while the pointer is outside the knob.\n *\n * @default { cursor: 'grabbing' }\n */\n externalStyles?: {\n cursor?: CSSProperties['cursor']\n }\n /**\n * How much one notch of the wheel moves the value. It only acts while the\n * focus is inside, so that scrolling the page past the knob leaves it alone.\n *\n * `['raw', n]` moves the value by `n`, and `['normalized', n]` by `n` of the\n * range between `min` and `max`. The result is snapped to `step`, except for\n * an amount set on a modifier key (`{ default: …, shift: … }`). `null` turns\n * the wheel off.\n *\n * @default ['raw', 1]\n */\n wheel?: ModifierValue<InputEventOption> | null\n /**\n * How much a drag moves the value, per modifier key.\n *\n * `1` is the normal travel of 100px for the whole range; `0.1` makes the\n * same movement cover a tenth of it. Shift is bound to `0.1` by default, to\n * match what it does on the arrow keys.\n *\n * Pressing or releasing the key mid-drag does not disturb the value: the\n * travel so far is kept and the new sensitivity applies from there. It takes\n * effect on the next movement, since a key on its own produces no pointer\n * event.\n *\n * @default { default: 1, shift: 0.1 }\n */\n dragSensitivity?: ModifierValue<number>\n\n /**\n * Hide the cursor while dragging and read the pointer movement directly,\n * rather than letting it wander off across the screen.\n *\n * A knob does not care where the pointer is, only how far it moved, and\n * letting it wander costs twice: the cursor ends up far from the knob it is\n * holding, and **the drag stops at the edge of the screen**, where the\n * operating system pins the pointer and the coordinates stop changing. A\n * `dragSensitivity` below 1 reaches that edge quickly.\n *\n * Off by default because it is not free: the browser shows its own notice,\n * Esc takes the lock back, and the request needs a user gesture and can be\n * refused. A refused request is not an error — the drag simply carries on as\n * an ordinary one.\n *\n * @default false\n */\n pointerLock?: boolean\n\n /**\n * How much one arrow key press moves the value.\n *\n * `['raw', n]` moves the value by `n`, and `['normalized', n]` by `n` of the\n * range between `min` and `max`. The result is snapped to `step`, except for\n * an amount set on a modifier key, which is what lets shift move off the\n * grid. `null` turns the arrow keys off.\n *\n * The default moves by 1, and by 0.1 with shift. With a `step` above 1, raise\n * the amount to match: 1 would round straight back to where it started, and\n * a development build warns about it.\n *\n * @default { default: ['raw', 1], shift: ['raw', 0.1] }\n */\n keyboard?: ModifierValue<InputEventOption> | null\n /**\n * Restore `defaultValue` on a double click.\n * @default true\n */\n enableDoubleClickDefault?: boolean\n\n /**\n * Make the knob unchangeable and remove it from the tab order.\n * The parts carry `data-disabled` while it is set.\n */\n disabled?: boolean\n /**\n * Make the knob unchangeable while leaving it focusable.\n * The parts carry `data-readonly` while it is set.\n */\n readonly?: boolean\n\n /**\n * How far the knob turns from `min` to `max`, in degrees, centred on the\n * top.\n * @default 270\n */\n angleRange?: number\n\n /**\n * Called with the new value when a drag, the wheel, an arrow key or a double\n * click moves it.\n */\n onChange?: (value: number) => void\n\n /**\n * The knob renders exactly what you compose here; there is no default\n * markup to fall back to.\n *\n * @example\n * <Knob.Root value={value} min={0} max={100} onChange={setValue}>\n * <Knob.SVGRoot>\n * <Knob.InactiveLine />\n * <Knob.ActiveLine />\n * <Knob.Thumb />\n * </Knob.SVGRoot>\n * </Knob.Root>\n */\n children: ReactNode\n\n style?: CSSProperties & CSSVariables<'knob-size'>\n}\n\nexport interface KnobMethods {\n focus: () => void\n blur: () => void\n}\n\ntype Props = KnobProps & Omit<ComponentPropsWithoutRef<'div'>, keyof KnobProps>\n\n/**\n * The wheel only acts while the focus is inside, so that scrolling a page past\n * the control does not change its value.\n */\nconst WHEEL_OPTIONS = { requireFocus: true }\n\nexport const Root = /* @__PURE__ */ forwardRef<KnobMethods, Props>(\n (\n {\n value,\n min,\n max,\n step = 1,\n scale = linearScale,\n defaultValue = min,\n startValue = min,\n size,\n externalStyles: _externalStyles,\n wheel = DEFAULT_WHEEL_OPTIONS,\n keyboard = DEFAULT_KEYBOARD_OPTIONS,\n dragSensitivity = DEFAULT_DRAG_SENSITIVITY,\n pointerLock = false,\n enableDoubleClickDefault = true,\n disabled = false,\n readonly = false,\n angleRange = 270,\n onChange,\n onKeyDown,\n onPointerDown,\n onDoubleClick,\n className,\n style,\n children,\n ...props\n }: Props,\n forwardedRef,\n ) => {\n const elmRef = useRef<HTMLElement | SVGElement>(null)\n\n const externalStyles = { ...defaultExternalStyles, ..._externalStyles }\n const inactive = disabled || readonly\n\n // --- internal functions ---\n const range: ValueRange = useMemo(\n () => ({ min, max, step, scale }),\n [min, max, step, scale],\n )\n\n useCheckSteps({ component: 'Knob', range, keyboard, wheel })\n\n const handleKeyDown = useCallback(\n (event: React.KeyboardEvent<HTMLOrSVGElement>) => {\n if (!keyboard || !onChange || inactive) return\n const direction = arrowKeyDirection(event.key)\n if (direction === null) return\n event.preventDefault()\n onChange(applyDelta(value, direction, keyboard, range, event))\n },\n [keyboard, onChange, inactive, value, range],\n )\n\n // --- hooks ---\n // The knob has no travel of its own: the value moves away from where it\n // stood when the drag started, 100px of movement spanning the whole range.\n // Only the vertical axis carries a value, reversed so that dragging up\n // raises it.\n const axis = useMemo(\n (): XY<AxisOptions> => [range, { ...range, reverse: true }],\n [range],\n )\n\n const { refCallback: dragRefCallback, dragging } = useDragValue<\n HTMLElement | SVGElement\n >({\n axis,\n getValue: () => [value, value],\n sensitivity: (state) =>\n selectModifier(dragSensitivity, state.event).value,\n threshold: 1,\n cursor: inactive ? undefined : externalStyles.cursor,\n pointerLock: inactive ? false : pointerLock,\n shouldStart: () => !inactive,\n onChange: (v) => {\n if (inactive) return\n onChange?.(v[1])\n },\n })\n\n const wheelRefCallback = useWheel<HTMLElement>((event) => {\n if (!wheel || inactive) return\n // A notch the knob does not read — a sideways scroll — is left to the\n // page rather than swallowed.\n const direction = wheelDirection(event)\n if (direction === null) return\n event.preventDefault()\n if (!onChange) return\n onChange(applyDelta(value, direction, wheel, range, event))\n }, WHEEL_OPTIONS)\n\n // Composed once, so React attaches the refs a single time instead of\n // detaching and re-attaching on every render.\n const rootRefCallback = useComposedRefs<HTMLElement | SVGElement>(\n elmRef,\n dragRefCallback,\n wheelRefCallback,\n )\n\n const context = useMemo(() => {\n const config = { value, min, max, step, scale, startValue, angleRange }\n return { ...config, ...knobAngles(config) }\n }, [value, min, max, step, scale, startValue, angleRange])\n\n useImperativeHandle(forwardedRef, () => {\n return {\n focus() {\n if (!disabled) elmRef.current?.focus()\n },\n blur() {\n elmRef.current?.blur()\n },\n }\n }, [disabled])\n\n return (\n <KnobProvider value={context}>\n <div\n ref={rootRefCallback}\n className={className}\n tabIndex={disabled ? -1 : 0}\n role=\"slider\"\n aria-valuenow={value}\n aria-valuemin={min}\n aria-valuemax={max}\n aria-disabled={disabled}\n aria-readonly={readonly}\n data-disabled={disabled ? '' : undefined}\n data-readonly={readonly ? '' : undefined}\n data-dragging={dragging ? '' : undefined}\n style={\n {\n '--knob-size': cssLength(size),\n ...style,\n } as CSSProperties\n }\n onPointerDown={onPointerDown}\n onDoubleClick={(event) => {\n if (!inactive && enableDoubleClickDefault && onChange) {\n onChange(defaultValue)\n }\n onDoubleClick?.(event)\n }}\n onKeyDown={(event) => {\n handleKeyDown(event)\n onKeyDown?.(event)\n }}\n {...props}\n >\n {children}\n </div>\n </KnobProvider>\n )\n },\n)\n\n/**\n * Interactive rotary knob component implemented in SVG.\n */\nexport const Knob = {\n Root,\n SVGRoot,\n InactiveLine,\n ActiveLine,\n Thumb,\n}\n\nexport { useKnobContext, type KnobContextValue } from './context'\nexport { type KnobSVGRootProps } from './SVGRoot'\nexport { type KnobThumbProps } from './Thumb'\n","import { createContext, RefObject, useContext } from 'react'\n\nimport {\n type InputEventOption,\n type ModifierState,\n type ModifierValue,\n} from '@tremolo-ui/dom'\nimport { type Scale, type ValueRange } from '@tremolo-ui/functions'\n\nexport type NumberInputContextValue = {\n value: number\n\n /**\n * The range the caller asked for. Left undefined when unbounded, which is\n * what `aria-valuemin` / `aria-valuemax` and the steppers need to tell apart\n * from a range that happens to sit at the edge of the safe integers.\n */\n min?: number\n max?: number\n step: number\n /** How the value is distributed across the travel. */\n scale: Scale\n\n disabled: boolean\n readonly: boolean\n clampValue: boolean\n\n /**\n * The effective scaling for `applyDelta`, with unbounded ends filled in.\n * Ignores `min` / `max` when `clampValue` is off.\n */\n range: ValueRange\n /** The effective range for raw input, whose open ends cover finite numbers. */\n rawRange: ValueRange\n\n keyboard: ModifierValue<InputEventOption> | null\n /** Pixels of vertical drag on `Stepper` that move the value by one `step`. */\n drag: number | null\n /** How much a `Stepper` drag counts, per modifier key. */\n dragSensitivity: ModifierValue<number>\n /** Whether a `Stepper` drag hides the pointer and reads its movement. */\n pointerLock: boolean\n\n /** What `InputField` selects when it takes focus. */\n selectOnFocus: 'all' | 'number' | 'none'\n /** Whether `InputField` shows the plain value while it has focus. */\n unformatOnFocus: boolean\n /** Whether `InputField` puts the caret back after an arrow key steps the value. */\n keepCaretOnStep: boolean\n /** Whether Enter leaves `InputField` as well as committing. */\n blurOnEnter: boolean\n\n /** What the input shows: the draft while editing, the formatted value otherwise. */\n text: string\n editing: boolean\n /** Whether the value sits outside `min` / `max`. Not judged while editing. */\n outOfRange: boolean\n /** Whether a stepper would have no effect, for `aria-disabled`. */\n atMin: boolean\n atMax: boolean\n\n format: (value: number) => string\n parse: (text: string) => number\n\n /** Typing. Replaces the draft and reports the parsed value, unclamped. */\n setDraft: (text: string) => void\n /** Blur or Enter. Optionally clamps the draft, reports it, and drops it. */\n commitDraft: () => void\n /** Drops the draft and replaces the value: steppers, wheel, keyboard, drag. */\n changeValue: (next: number) => void\n /** Moves the value by one `option` in `direction`, normally +1 or -1. */\n nudge: (\n direction: number,\n option: ModifierValue<InputEventOption>,\n modifiers?: ModifierState,\n ) => void\n\n /** `InputField` registers itself here; `Root` focuses it through its methods. */\n inputRef: RefObject<HTMLInputElement | null>\n}\n\nconst NumberInputContext =\n /* @__PURE__ */ createContext<NumberInputContextValue | null>(null)\n\nexport const NumberInputProvider = NumberInputContext.Provider\n\n/**\n * The only state behind this is the editing draft; `value` comes from the props\n * of `Root` and everything else is derived during render.\n */\nexport function useNumberInputContext(): NumberInputContextValue\nexport function useNumberInputContext<T>(\n selector: (state: NumberInputContextValue) => T,\n): T\nexport function useNumberInputContext<T>(\n selector?: (state: NumberInputContextValue) => T,\n) {\n const context = useContext(NumberInputContext)\n if (!context)\n throw new Error('Missing NumberInputContext.Provider in the tree')\n return selector ? selector(context) : context\n}\n\n/** Set by `Stepper` once a drag has actually moved, so the steppers stand down. */\nexport type StepperContextValue = {\n /** Whether the drag in progress has moved the value. */\n moved: () => boolean\n}\n\nconst StepperContext =\n /* @__PURE__ */ createContext<StepperContextValue | null>(null)\n\nexport const StepperProvider = StepperContext.Provider\n\nexport function useStepperContext(): StepperContextValue | null {\n return useContext(StepperContext)\n}\n","import { useCallback, useEffect, useRef } from 'react'\n\nimport { createLongPress, type LongPressInstance } from '@tremolo-ui/dom'\n\nimport { useCallbackRef } from './_internal/useCallbackRef'\n\n/**\n * Repeat `callback` while a pointer is held down: once on the press, then\n * every `interval` after `initialDelay`. The repeat itself is\n * `createLongPress` in `@tremolo-ui/dom`.\n *\n * Returns the function that starts a press, for an `onPointerDown`.\n */\nexport function useLongPress(\n callback: () => void,\n initialDelay = 500,\n interval = 40,\n) {\n // Updated before layout effects run, so that a press started from one\n // right after a re-render already calls the new callback.\n const onPress = useCallbackRef(callback)\n const instanceRef = useRef<LongPressInstance | null>(null)\n\n useEffect(() => {\n instanceRef.current?.update({ delay: initialDelay, interval })\n })\n\n useEffect(() => {\n const instance = createLongPress({\n onPress: () => onPress(),\n delay: initialDelay,\n interval,\n })\n instanceRef.current = instance\n return () => {\n instanceRef.current = null\n instance.destroy()\n }\n // Created once: the options are pushed with update() above, so that a new\n // delay does not end a press in progress.\n // oxlint-disable-next-line react-hooks/exhaustive-deps\n }, [])\n\n return useCallback(\n (event?: Pick<PointerEvent, 'button' | 'pointerId'>) =>\n instanceRef.current?.start(event),\n [],\n )\n}\n","import { ComponentPropsWithoutRef, ReactNode, CSSProperties } from 'react'\n\nimport { useLongPress } from '../../hooks/useLongPress'\n\nimport { useNumberInputContext, useStepperContext } from './context'\n\nimport type { CSSVariables } from '../../css-variables'\n\n/**\n * `--stepper-icon-size` is read by the default arrow inside, and set on the\n * button so that it inherits there.\n */\nexport type StepperButtonProps = Omit<\n ComponentPropsWithoutRef<'div'>,\n 'style'\n> & {\n style?: CSSProperties & CSSVariables<'stepper-icon-size'>\n}\n\ntype Props = StepperButtonProps\n\n/**\n * Shared body of `IncrementStepper` and `DecrementStepper`: the two differ only\n * in which way they move the value and which arrow they draw.\n */\nexport function StepperButton({\n direction,\n variant,\n icon,\n className,\n style,\n children,\n onPointerDown,\n ...props\n}: Props & {\n direction: 1 | -1\n variant: 'increment' | 'decrement'\n icon: ReactNode\n}) {\n const { step, disabled, readonly, atMin, atMax, nudge } =\n useNumberInputContext()\n const stepper = useStepperContext()\n\n // Not memoized: `useLongPress` only ever calls it through a ref, so a fresh\n // identity per render costs nothing.\n const press = useLongPress(() => {\n // Once the pointer has actually travelled, the drag on `Stepper` owns the\n // value; repeating on top of it would move it twice.\n if (stepper?.moved()) return\n nudge(direction, ['raw', step])\n })\n\n return (\n <div\n className={className}\n role=\"button\"\n tabIndex={-1}\n // A bare arrow has no accessible name of its own. Overridable, since a\n // caller may need it in their own language.\n aria-label={direction > 0 ? 'Increment' : 'Decrement'}\n aria-disabled={disabled || (direction > 0 ? atMax : atMin)}\n // `role=\"button\"` does not take aria-readonly, so the state reaches the\n // styles through the data attribute alone.\n data-disabled={\n disabled || (direction > 0 ? atMax : atMin) ? '' : undefined\n }\n data-readonly={readonly ? '' : undefined}\n style={style}\n onPointerDown={(event) => {\n if (!disabled && !readonly) press(event)\n onPointerDown?.(event)\n }}\n {...props}\n >\n {children ?? icon}\n </div>\n )\n}\n\nexport function StepperArrow({ up }: { up: boolean }) {\n return (\n <svg\n // The arrow the component draws when no children are given, so it sizes\n // itself rather than asking a stylesheet to.\n style={{\n width: 'var(--stepper-icon-size)',\n height: 'var(--stepper-icon-size)',\n }}\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n >\n <polyline points={up ? '18 15 12 9 6 15' : '6 9 12 15 18 9'} />\n </svg>\n )\n}\n","import {\n StepperArrow,\n StepperButton,\n type StepperButtonProps,\n} from './StepperButton'\n\n/** Lowers the value by one `step`, repeating while held. */\nexport function DecrementStepper(props: StepperButtonProps) {\n return (\n <StepperButton\n direction={-1}\n variant=\"decrement\"\n icon={<StepperArrow up={false} />}\n {...props}\n />\n )\n}\n","import {\n StepperArrow,\n StepperButton,\n type StepperButtonProps,\n} from './StepperButton'\n\n/** Raises the value by one `step`, repeating while held. */\nexport function IncrementStepper(props: StepperButtonProps) {\n return (\n <StepperButton\n direction={1}\n variant=\"increment\"\n icon={<StepperArrow up />}\n {...props}\n />\n )\n}\n","import {\n ComponentPropsWithoutRef,\n forwardRef,\n useEffect,\n useLayoutEffect,\n useRef,\n useState,\n} from 'react'\n\nimport {\n caretAtDecimalOffset,\n caretDecimalOffset,\n numberSpan,\n} from '@tremolo-ui/dom'\n\nimport { useComposedRefs } from '../../compose-refs'\n\nimport { useNumberInputContext } from './context'\n\n// The value belongs to `NumberInput.Root`, and the field is always text.\ntype Props = Omit<\n ComponentPropsWithoutRef<'input'>,\n 'type' | 'value' | 'defaultValue'\n>\n\n/**\n * The text field of a `NumberInput`, and the only place the value can be typed.\n *\n * While the user types, their own text stands rather than `format(value)`, so\n * that a half-finished entry is not rewritten under the caret.\n *\n * How it behaves on focus, Enter and the arrow keys is set on `Root`\n * (`selectOnFocus`, `unformatOnFocus`, `keepCaretOnStep`, `blurOnEnter`).\n */\nexport const InputField = /* @__PURE__ */ forwardRef<HTMLInputElement, Props>(\n function InputField(\n { className, style, onChange, onFocus, onBlur, onKeyDown, ...props },\n forwardedRef,\n ) {\n const {\n value,\n min,\n max,\n step,\n disabled,\n readonly,\n keyboard,\n selectOnFocus,\n unformatOnFocus,\n keepCaretOnStep,\n blurOnEnter,\n text,\n editing,\n outOfRange,\n setDraft,\n commitDraft,\n nudge,\n inputRef,\n } = useNumberInputContext()\n\n const [focused, setFocused] = useState(false)\n\n // Only until the first keystroke: from then on the draft is the user's own\n // text and stands on its own, formatted or not.\n const shown = unformatOnFocus && focused && !editing ? String(value) : text\n\n // Selecting has to wait for that swap. Called from the focus handler it\n // would run against the formatted text still in the input and cover the\n // wrong characters.\n useEffect(() => {\n if (!focused || selectOnFocus === 'none') return\n const input = inputRef.current\n if (!input) return\n if (selectOnFocus === 'all') {\n input.setSelectionRange(0, input.value.length)\n } else {\n const span = numberSpan(input.value)\n if (span) input.setSelectionRange(span.start, span.end)\n }\n // Deliberately not re-run as the text changes: that would drag the\n // selection back over what the user is typing.\n }, [focused, selectOnFocus, inputRef])\n\n /**\n * Where to put the caret once the stepped value has been rendered, and the\n * text it was measured against.\n */\n const caretAfterStep = useRef<{ offset: number; from: string } | null>(null)\n\n // A layout effect rather than a plain one: the caret is moved before the\n // browser paints, so it is never seen at the end of the text first.\n useLayoutEffect(() => {\n const pending = caretAfterStep.current\n if (!pending) return\n caretAfterStep.current = null\n\n const input = inputRef.current\n // The step may have been clamped away, leaving the text as it was. The\n // caret has not moved either, since the key press was prevented.\n if (!input || input.value === pending.from) return\n\n const caret = caretAtDecimalOffset(input.value, pending.offset)\n input.setSelectionRange(caret, caret)\n })\n\n const composedRef = useComposedRefs<HTMLInputElement>(\n forwardedRef,\n inputRef,\n )\n\n return (\n <input\n ref={composedRef}\n className={className}\n // Not type=\"number\": that brings native spinners and a value the browser\n // parses itself, neither of which survives a unit suffix.\n type=\"text\"\n inputMode=\"decimal\"\n role=\"spinbutton\"\n value={shown}\n disabled={disabled}\n readOnly={readonly}\n aria-disabled={disabled}\n aria-readonly={readonly}\n data-disabled={disabled ? '' : undefined}\n data-readonly={readonly ? '' : undefined}\n aria-valuenow={value}\n aria-valuemin={min}\n aria-valuemax={max}\n // The formatted text, even while the field shows the plain number: it is\n // the one that says what the value means.\n aria-valuetext={text}\n step={step}\n data-out-of-range={outOfRange ? '' : undefined}\n style={style}\n onChange={(event) => {\n // The text is the user's own now, so a caret measured against the\n // stepped one no longer means anything.\n caretAfterStep.current = null\n setDraft(event.currentTarget.value)\n onChange?.(event)\n }}\n onFocus={(event) => {\n setFocused(true)\n onFocus?.(event)\n }}\n onBlur={(event) => {\n setFocused(false)\n // Nothing was typed, so there is no draft and this returns at once —\n // taking focus and leaving again never commits anything.\n commitDraft()\n onBlur?.(event)\n }}\n onKeyDown={(event) => {\n const key = event.key\n if (key === 'Enter') {\n commitDraft()\n if (blurOnEnter) event.currentTarget.blur()\n } else if (\n keyboard &&\n !disabled &&\n !readonly &&\n (key === 'ArrowUp' || key === 'ArrowDown') &&\n // Arrow keys pick a candidate while an IME is converting. Stepping\n // the value there would fight the conversion, and moving the caret\n // would break it outright.\n !event.nativeEvent.isComposing\n ) {\n event.preventDefault()\n const input = event.currentTarget\n if (keepCaretOnStep && input.selectionStart !== null) {\n caretAfterStep.current = {\n offset: caretDecimalOffset(input.value, input.selectionStart),\n from: input.value,\n }\n }\n nudge(key === 'ArrowUp' ? 1 : -1, keyboard, event)\n }\n onKeyDown?.(event)\n }}\n {...props}\n />\n )\n },\n)\n","import {\n ComponentPropsWithoutRef,\n forwardRef,\n useEffect,\n useMemo,\n useRef,\n useState,\n} from 'react'\n\nimport { createStepperDrag, type StepperDragInstance } from '@tremolo-ui/dom'\n\nimport { useComposedRefs } from '../../compose-refs'\n\nimport { StepperProvider, useNumberInputContext } from './context'\n\ntype Props = ComponentPropsWithoutRef<'div'>\n\n/**\n * The area the steppers sit in, and a drag handle in its own right: dragging it\n * up and down moves the value one `step` every `drag` pixels. The counting is\n * `createStepperDrag` in the core.\n *\n * The drag lives here rather than on `InputField` because `createDrag` turns\n * off text selection on whatever element it is attached to.\n */\nexport const Stepper = /* @__PURE__ */ forwardRef<HTMLDivElement, Props>(\n function Stepper({ className, style, children, ...props }, forwardedRef) {\n const {\n value,\n disabled,\n readonly,\n drag,\n dragSensitivity,\n pointerLock,\n rawRange,\n changeValue,\n } = useNumberInputContext()\n // Only attached while it can do something: `createDrag` puts\n // `touch-action: none` on the element, and a stepper that cannot be\n // dragged has no reason to stop the page scrolling under a finger.\n const enabled = drag !== null && !disabled && !readonly\n\n // See useDrag for why the node is held in state rather than a ref.\n const [node, setNode] = useState<HTMLDivElement | null>(null)\n const instanceRef = useRef<StepperDragInstance | null>(null)\n const latest = useRef({ value, changeValue })\n const options = {\n range: rawRange,\n pixels: drag ?? 1,\n sensitivity: dragSensitivity,\n pointerLock,\n }\n const optionsRef = useRef(options)\n\n // Runs after every render.\n useEffect(() => {\n latest.current = { value, changeValue }\n optionsRef.current = options\n instanceRef.current?.update(options)\n })\n\n useEffect(() => {\n if (!node || !enabled) return\n const instance = createStepperDrag(node, {\n ...optionsRef.current,\n getValue: () => latest.current.value,\n onChange: (next) => latest.current.changeValue(next),\n })\n instanceRef.current = instance\n return () => {\n instanceRef.current = null\n instance.destroy()\n }\n }, [node, enabled])\n\n const composedRef = useComposedRefs<HTMLDivElement>(forwardedRef, setNode)\n\n const context = useMemo(\n () => ({ moved: () => instanceRef.current?.moved() ?? false }),\n [],\n )\n\n return (\n <StepperProvider value={context}>\n <div ref={composedRef} className={className} style={style} {...props}>\n {children}\n </div>\n </StepperProvider>\n )\n },\n)\n","import {\n ComponentPropsWithoutRef,\n forwardRef,\n ReactNode,\n useCallback,\n useImperativeHandle,\n useMemo,\n useRef,\n useState,\n} from 'react'\n\nimport {\n DEFAULT_DRAG_SENSITIVITY,\n DEFAULT_KEYBOARD_OPTIONS,\n DEFAULT_WHEEL_OPTIONS,\n commitNumberInputText,\n InputEventOption,\n ModifierState,\n type ModifierValue,\n numberInputBounds,\n numberInputRanges,\n nudgeNumberInput,\n parseNumberText,\n wheelDirection,\n} from '@tremolo-ui/dom'\nimport { linearScale, type Scale } from '@tremolo-ui/functions'\n\nimport { useCheckSteps } from '../../hooks/_internal/useCheckSteps'\nimport { useWheel } from '../../hooks/useWheel'\n\nimport { NumberInputProvider } from './context'\nimport { DecrementStepper } from './DecrementStepper'\nimport { IncrementStepper } from './IncrementStepper'\nimport { InputField } from './InputField'\nimport { Stepper } from './Stepper'\n\nexport interface NumberInputProps {\n /**\n * The value. What the input shows is `format(value)`, except while the user\n * is typing, when their own text stands until it is committed.\n */\n value: number\n\n /**\n * Lowest value. Leave it out for no lower bound.\n *\n * It is enforced when a value is committed or stepped, while `clampValue` is\n * on. A value below it carries `data-out-of-range` on `InputField`.\n */\n min?: number\n /**\n * Highest value. Leave it out for no upper bound.\n *\n * It is enforced when a value is committed or stepped, while `clampValue` is\n * on. A value above it carries `data-out-of-range` on `InputField`.\n */\n max?: number\n /**\n * Granularity of the value. The steppers, a drag on `Stepper`, the wheel and\n * the arrow keys snap it to multiples of `step`; a typed value is left as it\n * is.\n *\n * @default 1\n */\n step?: number\n /**\n * How the value is distributed across the travel of a drag or a\n * `'normalized'` wheel / keyboard nudge.\n *\n * @default linearScale\n */\n scale?: Scale\n\n /**\n * Render the value as text. Plain digits by default.\n *\n * `unitFormat` from `@tremolo-ui/functions` builds this and `parse` together\n * for a unit, as a pair to spread into the root.\n *\n * @example\n * <NumberInput.Root {...unitFormat('Hz', { digits: 2 })} value={v} />\n */\n format?: (value: number) => string\n /**\n * Read a value back out of the text. Has to undo `format`.\n *\n * Text with no number in it reads as `NaN`, which leaves the value alone.\n * The default, `parseNumberText` from `@tremolo-ui/dom`, reads the number\n * with the unit around it left out, and is `NaN` for one it cannot read\n * whole: `1,000`, `1:30`, or a number with a unit in front, such as `L 30`.\n */\n parse?: (text: string) => number\n\n /**\n * Keep the value within `min` and `max` when it is committed or stepped.\n * Typing is never clamped, so that a value can be entered digit by digit.\n * @default true\n */\n clampValue?: boolean\n\n /**\n * How much one notch of the wheel moves the value. It only acts while the\n * focus is inside, so that scrolling the page past the input leaves it\n * alone.\n *\n * `['raw', n]` moves the value by `n`, and `['normalized', n]` by `n` of the\n * range between `min` and `max`. The result is snapped to `step`, except for\n * an amount set on a modifier key (`{ default: …, shift: … }`). `null` turns\n * the wheel off.\n *\n * @default ['raw', 1]\n */\n wheel?: ModifierValue<InputEventOption> | null\n /**\n * How much one arrow key press moves the value.\n *\n * `['raw', n]` moves the value by `n`, and `['normalized', n]` by `n` of the\n * range between `min` and `max`. The result is snapped to `step`, except for\n * an amount set on a modifier key, which is what lets shift move off the\n * grid. `null` turns the arrow keys off.\n *\n * The default moves by 1, and by 0.1 with shift. With a `step` above 1, raise\n * the amount to match: 1 would round straight back to where it started, and\n * a development build warns about it.\n *\n * @default { default: ['raw', 1], shift: ['raw', 0.1] }\n */\n keyboard?: ModifierValue<InputEventOption> | null\n /**\n * Pixels of vertical drag on `Stepper` that move the value by one `step`.\n * `null` turns the drag off.\n * @default 1\n */\n drag?: number | null\n\n /**\n * How much a `Stepper` drag counts, per modifier key.\n *\n * `1` is `drag` pixels per `step`; `0.1` makes the same movement cover a\n * tenth of that. Shift is bound to `0.1` by default, to match what it does\n * on the arrow keys.\n *\n * A modifier entry is not snapped to `step`, which is what lets a finer\n * amount move at all.\n *\n * @default { default: 1, shift: 0.1 }\n */\n dragSensitivity?: ModifierValue<number>\n\n /**\n * Hide the cursor while dragging a `Stepper` and read the pointer movement\n * directly, rather than letting it wander off across the screen.\n *\n * The drag is already relative, so the pointer position carries nothing —\n * but it still runs into the edge of the screen, where the operating system\n * pins it and the coordinates stop changing.\n *\n * Off by default: the browser shows its own notice, Esc takes the lock back,\n * and the request can be refused. A refused request is not an error, and the\n * drag carries on as an ordinary one.\n *\n * @default false\n */\n pointerLock?: boolean\n\n /**\n * Select the text when `InputField` takes focus: `'all'` selects all of it,\n * `'number'` only the number, leaving the unit the format put around it,\n * and `'none'` leaves the caret where the click put it.\n * @default 'none'\n */\n selectOnFocus?: 'all' | 'number' | 'none'\n /**\n * Show the plain value while `InputField` has focus, dropping whatever\n * `format` put around it: an input reading `1.23kHz` shows `1230` to be\n * typed over.\n *\n * The number shown is the value itself, not the number inside the formatted\n * text. Those differ whenever the format scales — `1.23` out of `1.23kHz`\n * would read back as 1.23 and lose a factor of a thousand — and it is also\n * why a rounded display no longer becomes the value: `1.6` shown as `2Hz`\n * offers `1.6` for editing, not `2`.\n *\n * @default false\n */\n unformatOnFocus?: boolean\n /**\n * Put the caret in `InputField` back where it was after an arrow key steps\n * the value.\n *\n * A controlled input whose `value` is replaced drops the caret at the end,\n * so without this the second press of a repeated step always acts on the\n * last digit. With it, the digit under the caret stays under the caret and\n * a column can be held while stepping.\n *\n * The position is measured from the decimal point rather than from either\n * end, so it survives the number growing or shrinking: the caret between\n * `9` and `.9` is still between `10` and `.0`.\n *\n * It only restores the caret. Which digit it sits on does not change the\n * size of the step — that is `keyboard`'s to say.\n *\n * @default false\n */\n keepCaretOnStep?: boolean\n /**\n * Commit and leave `InputField` when Enter is pressed. Enter commits either\n * way.\n * @default true\n */\n blurOnEnter?: boolean\n\n /**\n * Make the input unchangeable and remove it from the tab order.\n * The parts carry `data-disabled` while it is set.\n */\n disabled?: boolean\n /**\n * Make the value unchangeable.\n * The parts carry `data-readonly` while it is set.\n */\n readonly?: boolean\n\n /**\n * Called with the new value. While the user types, it is called for every\n * entry that reads as a number, unclamped; committing the entry calls it\n * again if clamping changes the value.\n */\n onChange?: (value: number) => void\n\n /**\n * The input renders exactly what you compose here; there is no default\n * markup to fall back to.\n *\n * @example\n * <NumberInput.Root value={value} min={0} max={100} onChange={setValue}>\n * <NumberInput.InputField />\n * <NumberInput.Stepper>\n * <NumberInput.IncrementStepper />\n * <NumberInput.DecrementStepper />\n * </NumberInput.Stepper>\n * </NumberInput.Root>\n */\n children: ReactNode\n}\n\nexport interface NumberInputMethods {\n focus: () => void\n blur: () => void\n}\n\nconst defaultFormat = (value: number) => String(value)\n\ntype Props = NumberInputProps &\n Omit<ComponentPropsWithoutRef<'div'>, keyof NumberInputProps>\n\nexport const Root = /* @__PURE__ */ forwardRef<NumberInputMethods, Props>(\n (\n {\n value,\n min,\n max,\n step = 1,\n scale = linearScale,\n format: formatProp,\n parse: parseProp,\n clampValue = true,\n wheel = DEFAULT_WHEEL_OPTIONS,\n keyboard = DEFAULT_KEYBOARD_OPTIONS,\n drag = 1,\n dragSensitivity = DEFAULT_DRAG_SENSITIVITY,\n pointerLock = false,\n selectOnFocus = 'none',\n unformatOnFocus = false,\n keepCaretOnStep = false,\n blurOnEnter = true,\n disabled = false,\n readonly = false,\n className,\n style,\n onChange,\n children,\n ...props\n }: Props,\n forwardedRef,\n ) => {\n // --- state and ref ---\n const inputRef = useRef<HTMLInputElement>(null)\n /**\n * The text being typed. The only state here: it is not a copy of `value`,\n * but the half-finished entry that has no value to be derived from yet.\n */\n const [draft, setDraft] = useState<string | null>(null)\n const inactive = disabled || readonly\n\n // --- interpret props ---\n const format = formatProp ?? defaultFormat\n const parse = parseProp ?? parseNumberText\n\n const { normalized: range, raw: rawRange } = useMemo(\n () => numberInputRanges({ min, max, step, scale, clampValue }),\n [clampValue, min, max, step, scale],\n )\n const ranges = useMemo(\n () => ({ normalized: range, raw: rawRange }),\n [range, rawRange],\n )\n\n // The pipeline range stands in the safe-integer range when an end is\n // open, and there is no travel to sample across that. Probing is skipped\n // rather than run against a range nobody set.\n const probeRange = useMemo(\n () =>\n min !== undefined && max !== undefined && min < max\n ? { min, max, step, scale }\n : null,\n [min, max, step, scale],\n )\n\n useCheckSteps({\n component: 'NumberInput',\n range: probeRange,\n keyboard,\n wheel,\n format,\n })\n\n const text = draft ?? format(value)\n const editing = draft !== null\n const bounds = numberInputBounds(value, { min, max, clampValue })\n const { atMin, atMax } = bounds\n const outOfRange = !editing && bounds.outOfRange\n\n // --- internal functions ---\n const handleDraft = useCallback(\n (next: string) => {\n if (inactive) return\n setDraft(next)\n // Deliberately unclamped: clamping here would make \"1500\" impossible\n // to type into an input whose max is 100.\n const parsed = parse(next)\n if (Number.isFinite(parsed)) onChange?.(parsed)\n },\n [inactive, parse, onChange],\n )\n\n const changeValue = useCallback(\n (next: number) => {\n if (inactive) return\n setDraft(null)\n if (next !== value) onChange?.(next)\n },\n [inactive, value, onChange],\n )\n\n const commitDraft = useCallback(() => {\n if (draft === null || inactive) return\n const committed = commitNumberInputText(draft, parse, {\n min,\n max,\n clampValue,\n })\n // Text with no number in it is not a value. Dropping the draft puts the\n // input back to what it was showing.\n if (committed === null) setDraft(null)\n else changeValue(committed)\n }, [draft, inactive, parse, clampValue, min, max, changeValue])\n\n const nudge = useCallback(\n (\n direction: number,\n option: ModifierValue<InputEventOption>,\n modifiers?: ModifierState,\n ) => {\n changeValue(\n nudgeNumberInput(value, direction, option, ranges, modifiers),\n )\n },\n [changeValue, value, ranges],\n )\n\n // --- hooks ---\n const wheelRefCallback = useWheel<HTMLDivElement>(\n (event) => {\n const direction = wheelDirection(event)\n if (!wheel || inactive || direction === null) return\n event.preventDefault()\n nudge(direction, wheel, event)\n },\n { requireFocus: true },\n )\n\n const context = useMemo(\n () => ({\n value,\n min,\n max,\n step,\n scale,\n disabled,\n readonly,\n clampValue,\n range,\n rawRange,\n keyboard,\n text,\n editing,\n outOfRange,\n dragSensitivity,\n pointerLock,\n selectOnFocus,\n unformatOnFocus,\n keepCaretOnStep,\n blurOnEnter,\n atMin,\n atMax,\n drag,\n format,\n parse,\n setDraft: handleDraft,\n commitDraft,\n changeValue,\n nudge,\n inputRef,\n }),\n [\n value,\n min,\n max,\n step,\n scale,\n disabled,\n readonly,\n clampValue,\n range,\n rawRange,\n keyboard,\n text,\n editing,\n outOfRange,\n atMin,\n atMax,\n drag,\n dragSensitivity,\n pointerLock,\n selectOnFocus,\n unformatOnFocus,\n keepCaretOnStep,\n blurOnEnter,\n format,\n parse,\n handleDraft,\n commitDraft,\n changeValue,\n nudge,\n ],\n )\n\n useImperativeHandle(forwardedRef, () => {\n return {\n focus() {\n if (!disabled) inputRef.current?.focus()\n },\n blur() {\n inputRef.current?.blur()\n },\n }\n }, [disabled])\n\n return (\n <NumberInputProvider value={context}>\n <div\n ref={wheelRefCallback}\n className={className}\n data-disabled={disabled ? '' : undefined}\n data-readonly={readonly ? '' : undefined}\n style={style}\n {...props}\n >\n {children}\n </div>\n </NumberInputProvider>\n )\n },\n)\n\n/**\n * Input with some useful functions for entering numerical values.\n */\nexport const NumberInput = {\n Root,\n InputField,\n Stepper,\n IncrementStepper,\n DecrementStepper,\n}\n\nexport { useNumberInputContext, type NumberInputContextValue } from './context'\n","import {\n ComponentPropsWithoutRef,\n CSSProperties,\n forwardRef,\n ReactNode,\n useEffect,\n useImperativeHandle,\n useMemo,\n useRef,\n useState,\n} from 'react'\n\nimport {\n blackKeyWidth,\n createPianoInput,\n fitWhiteKeyWidth,\n getNoteRangeArray,\n type KeyboardShortcuts,\n type KeyboardShortcutsScope,\n notePosition,\n pianoWidth,\n type NoteRange,\n type PianoInputInstance,\n type PianoLayout,\n} from '@tremolo-ui/dom'\nimport { isWhiteKey, noteKey } from '@tremolo-ui/functions'\n\n/**\n * What {@link PianoProps.keyProps} may return for one key.\n *\n * `data-*` attributes are spelled out because TypeScript only allows them on\n * JSX syntax, not on an object type, and selecting on one is the usual way to\n * mark a key out.\n */\nexport type KeyAttributes = ComponentPropsWithoutRef<'div'> &\n Record<`data-${string}`, string | number | boolean | undefined>\n\n/** What a key is, when {@link PianoProps.label} or `keyProps` is asked about it. */\nexport interface KeyState {\n /** Position in the note range, counting from `noteRange.first`. */\n index: number\n keyType: 'white' | 'black'\n /** Whether the note is currently sounding. */\n active: boolean\n /** Whether the note is above {@link PianoProps.midiMax} and cannot sound. */\n disabled: boolean\n}\n\nexport interface PianoProps {\n /**\n * Classes for what a key draws inside itself: `keyLabelWrapper`, and\n * `keyLabel` around what `label` returns. The key itself takes a class\n * through `keyProps`, which is also where the note and its state are.\n */\n classes?: {\n keyLabelWrapper?: string\n keyLabel?: string\n }\n\n /**\n * The notes to draw keys for, as MIDI note numbers from `first` to `last`,\n * both included.\n */\n noteRange: NoteRange\n\n /**\n * Let a pointer slide from one key to the next while it is down. With it\n * off, the key that was pressed sounds until the pointer is released.\n *\n * @default true\n */\n glissando?: boolean\n\n /**\n * Highest note that can sound. Keys above it carry `data-disabled`.\n *\n * @default 127\n */\n midiMax?: number\n\n /**\n * Play notes from the computer keyboard: `keys[i]` plays\n * `noteRange.first + i`. `SHORTCUTS` has ready-made layouts, which assume\n * `noteRange.first` is a C. Where they listen is `keyboardShortcutsScope`.\n */\n keyboardShortcuts?: KeyboardShortcuts\n\n /**\n * Where keyboard shortcuts listen. `root` handles keys only while the Piano\n * root or one of its descendants has focus; `window` handles them anywhere\n * on the page except in editable elements.\n *\n * @default 'root'\n */\n keyboardShortcutsScope?: KeyboardShortcutsScope\n\n /**\n * Follow the width of the parent element, deriving the width of a white key\n * from it. {@link PianoProps.whiteKeyWidth} is ignored.\n *\n * @default false\n */\n resizable?: boolean\n\n /**\n * Width of a white key in pixels, not counting `keyGap`. Ignored while\n * `resizable` is on.\n * @default 40\n */\n whiteKeyWidth?: number\n /**\n * Space between two white keys, in pixels.\n * @default 1\n */\n keyGap?: number\n /**\n * Width of a black key, as a fraction of {@link PianoProps.whiteKeyWidth}.\n * @default 0.65\n */\n blackKeyWidthRatio?: number\n /**\n * Height of a black key, as a fraction of the height of the keyboard.\n * @default 0.6\n */\n blackKeyHeightRatio?: number\n\n /**\n * What to draw inside a key. `''`, `null` and `undefined` leave it bare, so\n * a layout with gaps — `SHORTCUTS.HOME_ROW_NATURAL`, say — needs no\n * special casing.\n */\n label?: (note: number, state: KeyState) => ReactNode\n\n /**\n * Extra props for one key, by note: a class, a style, a `data-*` attribute\n * to select on.\n *\n * The geometry of the key (`left`, `width`, `height`) is applied after the\n * returned `style` and cannot be overridden, so a key cannot be drawn\n * somewhere other than where it responds.\n *\n * @example highlight the notes of a scale\n * ```tsx\n * keyProps={(note) => ({ 'data-in-scale': inScale(note, root, 'major') ? '' : undefined })}\n * ```\n */\n keyProps?: (note: number, state: KeyState) => KeyAttributes\n\n /**\n * Called when a note starts sounding, whether a pointer, a keyboard shortcut\n * or `playNote` asked for it. A note held by several of them at once is\n * reported once. `velocity` is what `playNote` was given, and `undefined`\n * otherwise.\n */\n onPlayNote?: (note: number, velocity?: number) => void\n /** Called once everything holding a note has let go of it. */\n onStopNote?: (note: number) => void\n}\n\nexport interface PianoMethods {\n playNote: (note: number, velocity?: number) => void\n stopNote: (note: number) => void\n}\n\ntype Props = PianoProps &\n Omit<ComponentPropsWithoutRef<'div'>, keyof PianoProps>\n\nexport const Root = /* @__PURE__ */ forwardRef<PianoMethods, Props>(\n function Root(\n {\n noteRange,\n glissando = true,\n midiMax = 127,\n keyboardShortcuts,\n keyboardShortcutsScope = 'root',\n resizable = false,\n whiteKeyWidth = 40,\n keyGap = 1,\n blackKeyWidthRatio = 0.65,\n blackKeyHeightRatio = 0.6,\n style,\n className,\n label,\n keyProps,\n classes,\n onPlayNote,\n onStopNote,\n ...props\n },\n forwardedRef,\n ) {\n // See useDrag for why the node is held in state rather than a ref: an inline\n // ref would be re-attached on every render and tear the instance down.\n const [node, setNode] = useState<HTMLDivElement | null>(null)\n const [activeNotes, setActiveNotes] = useState<number[]>([])\n /** Set while `resizable` is on, measured from the parent. */\n const [resizedKeyWidth, setResizedKeyWidth] = useState(whiteKeyWidth)\n\n const notes = useMemo(() => getNoteRangeArray(noteRange), [noteRange])\n\n const layout: PianoLayout = useMemo(\n () => ({\n noteRange,\n whiteKeyWidth: resizable ? resizedKeyWidth : whiteKeyWidth,\n keyGap,\n blackKeyWidthRatio,\n blackKeyHeightRatio,\n }),\n [\n noteRange,\n resizable,\n resizedKeyWidth,\n whiteKeyWidth,\n keyGap,\n blackKeyWidthRatio,\n blackKeyHeightRatio,\n ],\n )\n\n // Read when the instance is created. The effect below keeps it current, and\n // runs right after, so a stale handler is replaced within the same commit.\n const latest = useRef({\n layout,\n glissando,\n midiMax,\n keyboardShortcuts,\n keyboardShortcutsScope,\n onPlayNote,\n onStopNote,\n })\n const instanceRef = useRef<PianoInputInstance | null>(null)\n useEffect(() => {\n if (!node) return\n\n const instance = createPianoInput(node, {\n layout: latest.current.layout,\n glissando: latest.current.glissando,\n midiMax: latest.current.midiMax,\n keyboardShortcuts: latest.current.keyboardShortcuts,\n keyboardShortcutsScope: latest.current.keyboardShortcutsScope,\n onPlayNote: (note, velocity) =>\n latest.current.onPlayNote?.(note, velocity),\n onStopNote: (note) => latest.current.onStopNote?.(note),\n onActiveNotesChange: setActiveNotes,\n })\n instanceRef.current = instance\n\n return () => {\n instanceRef.current = null\n instance.destroy()\n }\n // Only the element decides how the instance is wired. Everything else is\n // pushed with update() below, so that changing the layout mid-drag — the\n // parent being resized under `resizable`, say — does not abort the drag.\n }, [node])\n\n // Runs after every render.\n useEffect(() => {\n latest.current = {\n layout,\n glissando,\n midiMax,\n keyboardShortcuts,\n keyboardShortcutsScope,\n onPlayNote,\n onStopNote,\n }\n instanceRef.current?.update({\n layout,\n glissando,\n midiMax,\n keyboardShortcuts,\n keyboardShortcutsScope,\n })\n })\n\n // By value, so that a range written inline does not reconnect the\n // observer on every render.\n const { first, last } = noteRange\n useEffect(() => {\n if (!resizable || !node) return\n const parent = node.parentElement\n if (!parent) throw new Error(\"doesn't have a parent element.\")\n\n const resizeObserver = new ResizeObserver(() => {\n setResizedKeyWidth(\n fitWhiteKeyWidth(node.clientWidth, {\n noteRange: { first, last },\n keyGap,\n blackKeyWidthRatio,\n }),\n )\n })\n resizeObserver.observe(parent)\n return () => resizeObserver.disconnect()\n }, [resizable, node, first, last, keyGap, blackKeyWidthRatio])\n\n useImperativeHandle(\n forwardedRef,\n () => ({\n playNote: (note, velocity) =>\n instanceRef.current?.noteOn(note, { source: 'api', velocity }),\n stopNote: (note) =>\n instanceRef.current?.noteOff(note, { source: 'api' }),\n }),\n [],\n )\n\n return (\n <div\n ref={setNode}\n className={className}\n data-resizable={resizable ? '' : undefined}\n role=\"group\"\n // The group can own keyboard shortcuts and must receive focus.\n // oxlint-disable-next-line jsx-a11y/no-noninteractive-tabindex\n tabIndex={0}\n style={\n {\n // Computed from the layout rather than chosen, so it stays inline.\n width: resizable ? '100%' : pianoWidth(layout),\n // The keys inside are placed against this box.\n position: 'relative',\n ...style,\n } as CSSProperties\n }\n {...props}\n >\n {notes.map((note, index) => {\n const keyType = isWhiteKey(note) ? 'white' : 'black'\n const state: KeyState = {\n index,\n keyType,\n active: activeNotes.includes(note),\n disabled: note > midiMax,\n }\n\n const {\n className: keyClassName,\n style: keyStyle,\n ...rest\n } = keyProps?.(note, state) ?? {}\n\n const content = label?.(note, state)\n\n return (\n <div\n key={note}\n className={keyClassName}\n data-note={note}\n data-note-key={noteKey(note)}\n data-active={state.active ? '' : undefined}\n data-disabled={state.disabled ? '' : undefined}\n {...rest}\n style={{\n // The key is placed and sized from the layout below, which\n // needs it out of flow; the black keys sit over the white\n // ones whichever order they are drawn in.\n position: 'absolute',\n zIndex: keyType === 'white' ? 1 : 2,\n ...keyStyle,\n left: notePosition(note, layout),\n width:\n keyType === 'white'\n ? layout.whiteKeyWidth\n : blackKeyWidth(layout),\n height:\n keyType === 'white'\n ? '100%'\n : `${blackKeyHeightRatio * 100}%`,\n }}\n >\n {content !== '' && content !== null && content !== undefined && (\n <div className={classes?.keyLabelWrapper}>\n <div className={classes?.keyLabel}>{content}</div>\n </div>\n )}\n </div>\n )\n })}\n </div>\n )\n },\n)\n\n/**\n * Customizable piano component.\n */\nexport const Piano = { Root }\n","import { ComponentPropsWithoutRef } from 'react'\n\nexport function Background({\n className,\n children,\n style,\n ...props\n}: ComponentPropsWithoutRef<'div'>) {\n return (\n <div\n className={className}\n style={{\n // Behind the points, filling the editor: what it is for, rather than\n // a look. The order of the layers holds whichever way they are\n // written, so the z-index is part of the same mechanics.\n position: 'absolute',\n inset: 0,\n zIndex: 0,\n ...style,\n }}\n {...props}\n >\n {children}\n </div>\n )\n}\n","import { createContext, CSSProperties, RefObject, useContext } from 'react'\n\nimport {\n type InputEventOption,\n type ModifierValue,\n type PointsEditorInstance,\n type SelectionBoxRect,\n} from '@tremolo-ui/dom'\n\nexport type PointsEditorContextValue = {\n disabled: boolean\n readonly: boolean\n /** Inherited by every `Point`; `null` turns the wheel off. */\n wheel: ModifierValue<InputEventOption> | null\n /** Inherited by every `Point`; `null` turns the keyboard off. */\n keyboard: ModifierValue<InputEventOption> | null\n /** Inherited by every `Point`. See `PointsEditorProps.dragSensitivity`. */\n dragSensitivity: ModifierValue<number>\n externalStyles: {\n cursor?: CSSProperties['cursor']\n }\n /**\n * `Container` registers its element here; `Point` normalizes the pointer\n * against it, so a point is placed by its position within the container.\n */\n containerRef: RefObject<HTMLDivElement | null>\n /** Whether points can be selected at all. See `PointsEditorProps.selectable`. */\n selectable: boolean\n /** Ids of the points currently selected. Always empty while `selectable` is off. */\n selection: readonly string[]\n /**\n * The selection and the moves, from `createPointsEditor` in\n * `@tremolo-ui/dom`: every `Point` registers with it, and a press, a drag,\n * a key or the wheel goes through it so that a selection moves as one.\n */\n editor: PointsEditorInstance\n /** The selection box, while one is being dragged. */\n selectionBox: SelectionBoxRect | null\n}\n\nconst PointsEditorContext =\n /* @__PURE__ */ createContext<PointsEditorContextValue | null>(null)\n\nexport const PointsEditorProvider = PointsEditorContext.Provider\n\n/**\n * The settings `Root` was given, for the subcomponents to read. A point's\n * value still belongs to the `Point` that draws it; what the root keeps is\n * which points are selected, and a registry of who they are.\n */\nexport function usePointsEditorContext(): PointsEditorContextValue\nexport function usePointsEditorContext<T>(\n selector: (state: PointsEditorContextValue) => T,\n): T\nexport function usePointsEditorContext<T>(\n selector?: (state: PointsEditorContextValue) => T,\n) {\n const context = useContext(PointsEditorContext)\n if (!context)\n throw new Error('Missing PointsEditorContext.Provider in the tree')\n return selector ? selector(context) : context\n}\n","import { ComponentPropsWithoutRef, forwardRef } from 'react'\n\nimport { POINT_AXIS, wheelMove } from '@tremolo-ui/dom'\n\nimport { useComposedRefs } from '../../compose-refs'\nimport { useDragValue } from '../../hooks/useDragValue'\nimport { useWheel } from '../../hooks/useWheel'\nimport { Placement } from '../_util/Placement'\n\nimport { usePointsEditorContext } from './context'\n\ntype Props = ComponentPropsWithoutRef<'div'>\n\nexport const Container = /* @__PURE__ */ forwardRef<HTMLDivElement, Props>(\n function Container({ children, className, style, ...props }, forwardedRef) {\n const { containerRef, disabled, selectable, editor } =\n usePointsEditorContext()\n const { refCallback: dragRefCallback } = useDragValue<HTMLDivElement>({\n axis: POINT_AXIS,\n baseElementRef: containerRef,\n // A press that landed on a point belongs to that point. Declining here\n // rather than in onDragStart matters: by then the container would already\n // have taken the pointer capture away from the point.\n shouldStart: (event) =>\n !disabled && !editor.isPointElement(event.target as Element | null),\n onDragStart: ([x, y], state) => {\n editor.beginSelectionBox({ x, y }, state.event)\n },\n onChange: ([x, y]) => editor.moveSelectionBox({ x, y }),\n onDragEnd: () => editor.endSelectionBox(),\n })\n\n // One listener for the whole editor rather than one per point: a wheel\n // event only reaches what the cursor is over, and a point is a 16px\n // target, so the focused point takes it from anywhere over the editor —\n // the way it does for Slider and XYPad.\n useWheel(\n (event) => {\n // Scrolling up moves the point towards y = 0; shift switches to x.\n const move = wheelMove(event)\n if (!move) return\n const axis = move.axis === 0 ? 'x' : 'y'\n if (editor.nudgeFocusedPoint(axis, move.direction, event)) {\n event.preventDefault()\n }\n },\n { target: containerRef },\n )\n\n // The container is what the pointer position is normalized against, so the\n // context ref is composed with any ref the caller passed.\n // Only attached when there is a selection to draw: `createDrag` puts\n // `touch-action: none` on whatever it holds, and an editor that cannot\n // select has no reason to stop the page scrolling under a finger.\n const composedRef = useComposedRefs<HTMLDivElement>(\n forwardedRef,\n containerRef,\n selectable && !disabled ? dragRefCallback : undefined,\n )\n\n return (\n <div\n ref={composedRef}\n className={className}\n style={{\n // The points inside are placed against this box, over the\n // background and under the selection box.\n position: 'absolute',\n inset: 0,\n zIndex: 10,\n ...style,\n }}\n {...props}\n >\n <Placement name=\"PointsEditor.Container\">{children}</Placement>\n </div>\n )\n },\n)\n","import { ComponentPropsWithoutRef, forwardRef } from 'react'\n\nimport { visuallyHiddenStyle } from '@tremolo-ui/dom'\n\ntype Props = Omit<ComponentPropsWithoutRef<'input'>, 'type' | 'style'>\n\nexport const VisuallyHiddenRangeInput = /* @__PURE__ */ forwardRef<\n HTMLInputElement,\n Props\n>((props, ref) => (\n <input {...props} ref={ref} type=\"range\" style={visuallyHiddenStyle} />\n))\n","import {\n ComponentPropsWithoutRef,\n CSSProperties,\n useEffect,\n useId,\n useRef,\n useState,\n} from 'react'\n\nimport {\n arrowKeyMove,\n clampPoint,\n type InputEventOption,\n type ModifierValue,\n POINT_AXIS,\n type PointPosition,\n type PointsEditorPoint,\n selectModifier,\n} from '@tremolo-ui/dom'\n\nimport { useComposedRefs } from '../../compose-refs'\nimport { useDragValue } from '../../hooks/useDragValue'\nimport { useCheckPlacement } from '../_util/Placement'\nimport { VisuallyHiddenRangeInput } from '../_util/VisuallyHiddenRangeInput'\n\nimport { usePointsEditorContext } from './context'\n\nimport type { CSSVariables } from '../../css-variables'\n\nexport interface PointsEditorPointProps<T extends PointPosition> {\n /**\n * Where the point is, as `{ x, y }` from 0 to 1 on each axis, with `y`\n * growing downwards.\n */\n value: T\n /**\n * How the selection refers to this point. One is generated when it is left\n * out, which lasts as long as the point is mounted — give your own if the\n * selection has to survive a remount, or be recognised in your own state.\n */\n id?: string\n /** The lowest position the point can take, per axis. An axis left out is 0. */\n min?: Partial<PointPosition>\n /** The highest position the point can take, per axis. An axis left out is 1. */\n max?: Partial<PointPosition>\n\n /** Sets `--color`, for the theme to colour the point with. */\n color?: string\n\n /** Overrides the `disabled` of `PointsEditor.Root`. */\n disabled?: boolean\n /** Overrides the `readonly` of `PointsEditor.Root`. */\n readonly?: boolean\n\n /** Overrides the `wheel` of `PointsEditor.Root`. */\n wheel?: ModifierValue<InputEventOption> | null\n /** Overrides the `keyboard` of `PointsEditor.Root`. */\n keyboard?: ModifierValue<InputEventOption> | null\n\n /**\n * The accessible name of each axis. There are two range inputs inside the\n * point, so this takes one name per axis; a single string names them both.\n */\n 'aria-label'?: string | Partial<Record<'x' | 'y', string>>\n /** What the value of each axis means, when the number does not say it. */\n 'aria-valuetext'?: string | Partial<Record<'x' | 'y', string>>\n\n /**\n * Called with the new position when the point is dragged or moved by the\n * arrow keys or the wheel, including when it moves along with a selection.\n */\n onChange?: (value: PointPosition) => void\n /** Called when a drag on this point starts, with where the point is. */\n onDragStart?: (value: PointPosition) => void\n /** Called when that drag ends, with where the point is. */\n onDragEnd?: (value: PointPosition) => void\n\n style?: CSSProperties & CSSVariables<'color' | 'translate'>\n}\n\n/**\n * One setting for both axes, or one per axis. The point holds a range input\n * for each, so anything that names or describes it comes in a pair.\n */\nfunction perAxis(\n value: string | Partial<Record<'x' | 'y', string>> | undefined,\n axis: 'x' | 'y',\n) {\n return typeof value === 'string' ? value : value?.[axis]\n}\n\nexport function Point<T extends PointPosition>({\n value,\n children,\n id: idProp,\n min,\n max,\n color,\n\n disabled: _disabled,\n readonly: _readonly,\n wheel: _wheel,\n keyboard: _keyboard,\n 'aria-label': ariaLabel,\n 'aria-valuetext': ariaValuetext,\n\n onChange,\n onDragStart,\n onDragEnd,\n\n className,\n style,\n onPointerDown,\n onKeyDown,\n onFocus,\n ...props\n}: PointsEditorPointProps<T> &\n Omit<ComponentPropsWithoutRef<'div'>, keyof PointsEditorPointProps<T>>) {\n const {\n containerRef,\n externalStyles,\n disabled: rootDisabled,\n readonly: rootReadonly,\n wheel: rootWheel,\n keyboard: rootKeyboard,\n dragSensitivity,\n selection,\n editor,\n } = usePointsEditorContext()\n\n const generatedId = useId()\n const id = idProp ?? generatedId\n const selected = selection.includes(id)\n\n const disabled = _disabled ?? rootDisabled\n const readonly = _readonly ?? rootReadonly\n const inactive = disabled || readonly\n // `null` means \"no event\" and has to survive the fallback, so `??` is not\n // enough: only an omitted prop inherits from the root.\n const wheel = _wheel === undefined ? rootWheel : _wheel\n const keyboard = _keyboard === undefined ? rootKeyboard : _keyboard\n\n useCheckPlacement('PointsEditor.Point', 'PointsEditor.Container')\n\n // Compared against the focus below, so the point needs its own element.\n const [element, setElement] = useState<HTMLDivElement | null>(null)\n const xInputRef = useRef<HTMLInputElement>(null)\n const yInputRef = useRef<HTMLInputElement>(null)\n\n // What the editor needs to move this point along with the rest of a\n // selection. Rewritten after every render rather than kept in the registry\n // itself: the value changes on every frame of a drag.\n const registration = useRef<PointsEditorPoint>({\n value,\n min,\n max,\n readonly: inactive,\n onChange,\n element,\n wheel,\n })\n useEffect(() => {\n registration.current = {\n value,\n min,\n max,\n readonly: inactive,\n onChange,\n element,\n wheel,\n }\n })\n\n useEffect(\n () => editor.registerPoint(id, () => registration.current),\n [id, editor],\n )\n\n /** Where the pointer was when the drag started, to measure the move from. */\n const pointerOrigin = useRef<PointPosition | null>(null)\n\n // The value is the position itself: no scaling, and no rounding to a step.\n const { refCallback: dragRefCallback, dragging } =\n useDragValue<HTMLDivElement>({\n axis: POINT_AXIS,\n baseElementRef: containerRef,\n sensitivity: (state) =>\n selectModifier(dragSensitivity, state.event).value,\n cursor: inactive ? undefined : externalStyles.cursor,\n shouldStart: () => !disabled,\n // The value is a move rather than a position: the point keeps the offset\n // it was grabbed at, and everything else selected moves with it by the\n // same amount.\n onChange: ([x, y]) => {\n const origin = pointerOrigin.current\n if (!origin) return\n editor.movePointDrag({ x: x - origin.x, y: y - origin.y })\n },\n onDragStart: ([x, y], state) => {\n // The press decides the selection before the snapshot is taken, so it\n // happens here rather than in an onPointerDown: a native listener on\n // the element runs before React's, and the two would disagree.\n editor.beginPointDrag(id, state.event)\n pointerOrigin.current = { x, y }\n xInputRef.current?.focus()\n\n if (inactive) return\n onDragStart?.(clampPoint(value, min, max))\n },\n onDragEnd: () => {\n pointerOrigin.current = null\n\n if (inactive) return\n onDragEnd?.(clampPoint(value, min, max))\n },\n })\n\n const refCallback = useComposedRefs<HTMLDivElement>(\n dragRefCallback,\n setElement,\n )\n\n const handleKeyDown = (event: React.KeyboardEvent<HTMLDivElement>) => {\n // The key picks the axis, whichever of the two inputs holds the focus:\n // the focus lands on the x input, so reading the axis off the input would\n // leave the y axis with no keys at all. y grows downwards, so ArrowUp\n // moves the point towards 0.\n const move = arrowKeyMove(event.key)\n if (!move) return\n event.preventDefault()\n if (!onChange || inactive || !keyboard) return\n editor.nudgePoint(\n id,\n move.axis === 0 ? 'x' : 'y',\n move.direction,\n keyboard,\n event,\n )\n }\n\n const current = clampPoint(value, min, max)\n\n return (\n // The visual point has two values, so its semantics live on the two range\n // inputs nested inside it rather than on this drag handle.\n // oxlint-disable-next-line jsx-a11y/no-static-element-interactions\n <div\n ref={refCallback}\n className={className}\n data-disabled={disabled ? '' : undefined}\n data-readonly={readonly ? '' : undefined}\n // A press lands on the point, which cannot hold focus, and the browser\n // answers that by clearing the focus to the body — undoing the focus the\n // drag just gave the input. Taking the focus here keeps it inside, and\n // it is passed on to the input below.\n tabIndex={-1}\n data-dragging={dragging ? '' : undefined}\n data-selected={selected ? '' : undefined}\n style={\n {\n '--color': color,\n // The mechanics of the position below: a point is placed by its\n // position in the container, measured from its own centre.\n position: 'absolute',\n translate: 'var(--translate, -50% -50%)',\n ...style,\n // Where the point is: the value, not a style.\n left: `${value.x * 100}%`,\n top: `${value.y * 100}%`,\n } as CSSProperties\n }\n onPointerDown={onPointerDown}\n onFocus={(event) => {\n // The point itself is not the control: its semantics live on the\n // inputs, so whatever reaches it is handed to the first of them.\n if (!disabled && event.target === event.currentTarget) {\n xInputRef.current?.focus()\n }\n onFocus?.(event)\n }}\n onKeyDown={(event) => {\n handleKeyDown(event)\n onKeyDown?.(event)\n }}\n {...props}\n >\n {(['x', 'y'] as const).map((axis) => (\n <VisuallyHiddenRangeInput\n key={axis}\n ref={axis === 'x' ? xInputRef : yInputRef}\n data-axis={axis}\n value={current[axis]}\n min={min?.[axis] ?? 0}\n max={max?.[axis] ?? 1}\n step=\"any\"\n disabled={disabled}\n aria-readonly={readonly}\n aria-orientation={axis === 'x' ? 'horizontal' : 'vertical'}\n aria-label={perAxis(ariaLabel, axis) ?? axis}\n aria-valuetext={perAxis(ariaValuetext, axis)}\n onChange={(event) => {\n if (readonly) {\n event.currentTarget.value = String(current[axis])\n return\n }\n const delta = event.currentTarget.valueAsNumber - value[axis]\n editor.nudgeSelection(id, {\n x: axis === 'x' ? delta : 0,\n y: axis === 'y' ? delta : 0,\n })\n }}\n />\n ))}\n {children}\n </div>\n )\n}\n","import { ComponentPropsWithoutRef, CSSProperties } from 'react'\n\nimport { useCheckPlacement } from '../_util/Placement'\n\nimport { usePointsEditorContext } from './context'\n\n/**\n * The box a drag on empty space draws, and what it covers is selected.\n *\n * Nothing is rendered while no drag is running, and leaving it out leaves the\n * editor without one — the selection still works, it just cannot be seen.\n */\nexport function SelectionBox({\n children,\n className,\n style,\n ...props\n}: ComponentPropsWithoutRef<'div'>) {\n useCheckPlacement('PointsEditor.SelectionBox', 'PointsEditor.Container')\n\n const selectionBox = usePointsEditorContext((s) => s.selectionBox)\n if (!selectionBox) return null\n\n return (\n <div\n className={className}\n {...props}\n style={\n {\n // Drawn over the points while a drag runs, and taking none of the\n // pointer: the drag underneath is what it is reporting on.\n position: 'absolute',\n zIndex: 20,\n pointerEvents: 'none',\n ...style,\n // Where the box is and how big it is: the drag, not a style, so it\n // is written after whatever the caller passed.\n left: `${selectionBox.x * 100}%`,\n top: `${selectionBox.y * 100}%`,\n width: `${selectionBox.width * 100}%`,\n height: `${selectionBox.height * 100}%`,\n } as CSSProperties\n }\n >\n {children}\n </div>\n )\n}\n","import {\n ComponentPropsWithoutRef,\n CSSProperties,\n forwardRef,\n ReactNode,\n useEffect,\n useMemo,\n useRef,\n useState,\n} from 'react'\n\nimport {\n createPointsEditor,\n DEFAULT_DRAG_SENSITIVITY,\n type InputEventOption,\n type ModifierValue,\n POINTS_EDITOR_DEFAULT_KEYBOARD,\n POINTS_EDITOR_DEFAULT_WHEEL,\n type SelectionBoxRect,\n} from '@tremolo-ui/dom'\n\nimport { Background } from './Background'\nimport { Container } from './Container'\nimport { PointsEditorProvider } from './context'\nimport { Point } from './Point'\nimport { SelectionBox } from './SelectionBox'\n\n/** One array for every editor with selection turned off, so memos hold still. */\nconst EMPTY: readonly string[] = []\n\nexport interface PointsEditorProps {\n /**\n * Make the points unchangeable and remove them from the tab order.\n * The parts carry `data-disabled` while it is set.\n */\n disabled?: boolean\n /**\n * Make the points unmovable.\n * The parts carry `data-readonly` while it is set.\n */\n readonly?: boolean\n\n /**\n * The cursor to show while dragging a point. It is set on the dragged\n * point, so it stays while the pointer is outside it.\n *\n * @default { cursor: 'grabbing' }\n */\n externalStyles?: {\n cursor?: CSSProperties['cursor']\n }\n\n /**\n * How much one notch of the wheel moves the focused `Point`. Scrolling\n * sideways, or with shift held, moves x; otherwise it moves y.\n *\n * A position runs from 0 to 1, so `['normalized', n]` and `['raw', n]` both\n * move it by `n` of the editor. `null` turns the wheel off, and a `Point`\n * can override it with a `wheel` of its own.\n *\n * @default ['normalized', 0.01]\n */\n wheel?: ModifierValue<InputEventOption> | null\n /**\n * How much one arrow key press moves a `Point`. Left and right move x, up\n * and down move y.\n *\n * A position runs from 0 to 1, so `['normalized', n]` and `['raw', n]` both\n * move it by `n` of the editor; give a map to set an amount per modifier\n * key. `null` turns the arrow keys off, and a `Point` can override it with a\n * `keyboard` of its own.\n *\n * @default { default: ['normalized', 0.01], shift: ['normalized', 0.001] }\n */\n keyboard?: ModifierValue<InputEventOption> | null\n\n /**\n * How much a drag moves a `Point`, per modifier key.\n *\n * `1` is the pointer position itself, which is what dragging a point\n * normally is. **Anything else turns the drag relative**: `0.1` makes the\n * same movement cover a tenth of the editor, so the point stops following\n * the pointer and starts moving a tenth as fast. Shift is bound to `0.1` by\n * default, to match what it does on the arrow keys.\n *\n * Pressing or releasing the key mid-drag does not disturb the point: the\n * travel so far is kept and the new sensitivity applies from there. **The\n * pointer and the point stay apart for the rest of the drag** — snapping\n * them back together on release would move the point nobody asked to move.\n *\n * @default { default: 1, shift: 0.1 }\n */\n dragSensitivity?: ModifierValue<number>\n\n /**\n * Let points be selected, and a selection be moved as one.\n *\n * Off by default, because it changes what a press and a drag mean: a press\n * on empty space starts a selection box rather than doing nothing, and a drag\n * on a point moves everything else that is selected. An editor whose points\n * each mean something different — the four handles of an ADSR envelope, say\n * — has nothing to gain from moving them together.\n *\n * **A selection calls `onChange` on several points in the same tick**, so\n * each of them has to update from the previous state rather than from a\n * value captured in the render:\n *\n * ```jsx\n * onChange={(v) => setPoints((prev) => ({ ...prev, [id]: v }))}\n * ```\n *\n * Written the other way round — `setPoints({ ...points, [id]: v })` — every\n * call but the last is thrown away, and only one point appears to move.\n *\n * @default false\n */\n selectable?: boolean\n\n /**\n * Ids of the selected points, to hold the selection yourself. Leave it out\n * and the editor keeps its own.\n *\n * A `Point` takes its id from its `id` prop, or generates one that lasts as\n * long as it is mounted.\n */\n selection?: string[]\n /** The selection to start with, when the editor keeps its own. */\n defaultSelection?: string[]\n /** Called whenever the selection changes, controlled or not. */\n onSelectionChange?: (selection: string[]) => void\n\n /**\n * The editor renders exactly what you compose here; there is no default\n * markup to fall back to.\n *\n * @example\n * <PointsEditor.Root>\n * <PointsEditor.Background>\n * <svg viewBox=\"0 0 200 100\">...</svg>\n * </PointsEditor.Background>\n * <PointsEditor.Container>\n * {points.map((point, i) => (\n * <PointsEditor.Point key={i} value={point} onChange={...} />\n * ))}\n * </PointsEditor.Container>\n * </PointsEditor.Root>\n */\n children: ReactNode\n}\n\ntype Props = PointsEditorProps &\n Omit<ComponentPropsWithoutRef<'div'>, keyof PointsEditorProps>\n\nexport const Root = /* @__PURE__ */ forwardRef<HTMLDivElement, Props>(\n (\n {\n disabled = false,\n readonly = false,\n wheel = POINTS_EDITOR_DEFAULT_WHEEL,\n keyboard = POINTS_EDITOR_DEFAULT_KEYBOARD,\n dragSensitivity = DEFAULT_DRAG_SENSITIVITY,\n selectable = false,\n selection: selectionProp,\n defaultSelection,\n onSelectionChange,\n externalStyles,\n style,\n className,\n children,\n ...props\n },\n forwardedRef,\n ) => {\n const containerRef = useRef<HTMLDivElement>(null)\n // Picked apart so that the memo below depends on values rather than on the\n // object literal a caller writes inline, which is new on every render.\n const { cursor = 'grabbing' } = externalStyles ?? {}\n\n // --- selection ---\n // The selection lives here, where React state can hold it; which points a\n // press or a box selects, and how a selection moves, is the core's.\n const controlled = selectionProp !== undefined\n const [ownSelection, setOwnSelection] = useState<string[]>(\n defaultSelection ?? [],\n )\n // Nothing is selected while selection is off, so a drag picks up only the\n // point it started on and `data-selected` never turns on.\n const selection = selectable ? (selectionProp ?? ownSelection) : EMPTY\n\n const [selectionBox, setSelectionBox] = useState<SelectionBoxRect | null>(\n null,\n )\n\n const [editor] = useState(() =>\n createPointsEditor({ onSelectionBoxChange: setSelectionBox }),\n )\n\n // Drags read the selection from a native event handler, which runs after\n // the commit, so pushing it from an effect is current by the time it\n // matters. The handler goes the same way: it closes over this render.\n useEffect(() => {\n editor.update({\n selectable,\n selection,\n onSelectionChange: (next) => {\n if (!controlled) setOwnSelection(next)\n onSelectionChange?.(next)\n },\n })\n })\n\n useEffect(() => () => editor.destroy(), [editor])\n\n const context = useMemo(\n () => ({\n disabled,\n readonly,\n wheel,\n keyboard,\n dragSensitivity,\n externalStyles: { cursor },\n containerRef,\n selectable,\n selection,\n editor,\n selectionBox,\n }),\n [\n disabled,\n readonly,\n wheel,\n keyboard,\n dragSensitivity,\n cursor,\n selectable,\n selection,\n editor,\n selectionBox,\n ],\n )\n\n return (\n <PointsEditorProvider value={context}>\n <div\n ref={forwardedRef}\n className={className}\n data-disabled={disabled ? '' : undefined}\n data-readonly={readonly ? '' : undefined}\n style={\n {\n // The layers inside are placed against this box.\n position: 'relative',\n ...style,\n } as CSSProperties\n }\n {...props}\n >\n {children}\n </div>\n </PointsEditorProvider>\n )\n },\n)\n\n/**\n * Multiple Point Controller\n */\nexport const PointsEditor = {\n Root,\n Background,\n Container,\n Point,\n SelectionBox,\n}\n\nexport {\n usePointsEditorContext,\n type PointsEditorContextValue,\n} from './context'\nexport { type PointsEditorPointProps } from './Point'\n","import { createContext, RefObject, useContext } from 'react'\n\nimport type { Scale } from '@tremolo-ui/functions'\n\nimport type { SliderThumbMethods } from './Thumb'\n\nexport type SliderContextValue = {\n value: number\n min: number\n max: number\n step: number\n /** How the value is distributed across the travel. */\n scale: Scale\n vertical: boolean\n reverse: boolean\n disabled: boolean\n readonly: boolean\n onChange?: (value: number) => void\n /**\n * Position of the thumb, 0-100, already accounting for the display direction\n * implied by `vertical` and `reverse`.\n */\n percent: number\n\n /** `Track` registers its element here; `Root` normalizes the pointer against it. */\n trackRef: RefObject<HTMLDivElement | null>\n /** `Thumb` registers itself here; `Root` focuses it when a drag starts. */\n thumbRef: RefObject<SliderThumbMethods | null>\n}\n\nconst SliderContext = /* @__PURE__ */ createContext<SliderContextValue | null>(\n null,\n)\n\nexport const SliderProvider = SliderContext.Provider\n\n/**\n * Everything here is derived during render, so there is no state to keep in\n * sync: `value` comes from the props of `Root` and the rest follows from it.\n */\nexport function useSliderContext(): SliderContextValue\nexport function useSliderContext<T>(\n selector: (state: SliderContextValue) => T,\n): T\nexport function useSliderContext<T>(\n selector?: (state: SliderContextValue) => T,\n) {\n const context = useContext(SliderContext)\n if (!context) throw new Error('Missing SliderContext.Provider in the tree')\n return selector ? selector(context) : context\n}\n","import { ComponentPropsWithoutRef, CSSProperties } from 'react'\n\nimport { cssLength, valuePercent } from '@tremolo-ui/dom'\n\nimport { useCheckPlacement } from '../_util/Placement'\n\nimport { useSliderContext } from './context'\n\nimport type { CSSVariables } from '../../css-variables'\n\nexport interface SliderMarksOptionProps {\n /** Where the mark sits, on the same scale as the thumb. */\n value: number\n\n /**\n * Draw the mark itself. Turn it off to show the label alone.\n * @default true\n */\n mark?: boolean\n /**\n * Text shown next to the mark. Leave it out to show the value; `null` leaves\n * the label out, and an empty string draws an empty one.\n */\n label?: number | string | null\n /** Thickness of the mark. Sets `--thickness`. */\n thickness?: number | string\n /** Length of the mark. Sets `--length`. */\n length?: number | string\n /** Space between the mark and the label. Sets `--gap`. */\n gap?: number | string\n /** Classes for the mark and the label inside the option. */\n classes?: {\n mark?: string\n label?: string\n }\n /** Styles for the mark and the label inside the option. */\n styles?: {\n mark?: CSSProperties\n label?: CSSProperties\n }\n\n style?: CSSProperties &\n CSSVariables<'thickness' | 'length' | 'gap' | 'translate'>\n}\n\nexport function MarksOption({\n value,\n mark = true,\n label,\n thickness,\n length,\n gap,\n classes,\n styles,\n className,\n style,\n ...props\n}: SliderMarksOptionProps &\n Omit<ComponentPropsWithoutRef<'div'>, keyof SliderMarksOptionProps>) {\n useCheckPlacement('Slider.MarksOption', 'Slider.Marks')\n\n const min = useSliderContext((s) => s.min)\n const max = useSliderContext((s) => s.max)\n const scale = useSliderContext((s) => s.scale)\n const vertical = useSliderContext((s) => s.vertical)\n const reverse = useSliderContext((s) => s.reverse)\n\n // The marks have to sit on the same curve the thumb runs along.\n const percent = valuePercent(value, { min, max, scale }, vertical !== reverse)\n\n return (\n <div\n className={className}\n style={\n {\n // The mark and the label read these, so they are set once here.\n '--thickness': cssLength(thickness),\n '--length': cssLength(length),\n '--gap': cssLength(gap),\n // The mechanics of the position below, as on the thumb.\n position: 'absolute',\n translate: vertical\n ? 'var(--translate, 0 -50%)'\n : 'var(--translate, -50% 0)',\n zIndex: 10,\n ...style,\n // Where the mark belongs on the track: the value, not a style.\n left: !vertical ? `${percent}%` : undefined,\n top: vertical ? `${percent}%` : undefined,\n } as CSSProperties\n }\n data-orientation={vertical ? 'vertical' : 'horizontal'}\n {...props}\n >\n {mark && (\n <div\n className={classes?.mark}\n style={styles?.mark}\n data-orientation={vertical ? 'vertical' : 'horizontal'}\n ></div>\n )}\n {label !== null && (\n <div className={classes?.label} style={styles?.label}>\n {label ?? value}\n </div>\n )}\n </div>\n )\n}\n","import { ComponentPropsWithoutRef, CSSProperties } from 'react'\n\nimport { cssLength, type MarksOptions, sliderMarks } from '@tremolo-ui/dom'\n\nimport { Placement } from '../_util/Placement'\n\nimport { useSliderContext } from './context'\nimport { MarksOption } from './MarksOption'\n\nimport type { CSSVariables } from '../../css-variables'\n\nexport interface SliderMarksProps {\n /**\n * Space between the marks and the track. Sets `--gap`; the theme's own\n * spacing stands when this is omitted.\n */\n gap?: number | string\n /**\n * Build the marks instead of writing `Slider.MarksOption` out: a number puts\n * one every that many, `'step'` one every `step`, and `{ per, mark, label }`\n * also turns the mark or the label off for all of them. `children` is\n * ignored while it is set.\n *\n * `'step'` makes one per step, which is a great many for a fine `step`.\n */\n options?: MarksOptions\n\n style?: CSSProperties & CSSVariables<'gap'>\n}\n\nexport function Marks({\n gap,\n options,\n children,\n className,\n style,\n ...props\n}: SliderMarksProps &\n Omit<ComponentPropsWithoutRef<'div'>, keyof SliderMarksProps>) {\n const min = useSliderContext((s) => s.min)\n const max = useSliderContext((s) => s.max)\n const step = useSliderContext((s) => s.step)\n const vertical = useSliderContext((s) => s.vertical)\n const reverse = useSliderContext((s) => s.reverse)\n\n const optionsList = options ? sliderMarks(options, min, max, step) : []\n if (vertical !== reverse) optionsList.reverse()\n\n return (\n <div\n className={className}\n data-orientation={vertical ? 'vertical' : 'horizontal'}\n style={\n {\n '--gap': cssLength(gap),\n // Each option inside is placed against this box.\n position: 'relative',\n ...style,\n } as CSSProperties\n }\n {...props}\n >\n <Placement name=\"Slider.Marks\">\n {options\n ? optionsList.map(({ value, mark, label }, index) => {\n return (\n <MarksOption\n key={index}\n value={value}\n mark={mark}\n label={label ? undefined : null}\n />\n )\n })\n : children}\n </Placement>\n </div>\n )\n}\n","import {\n ComponentPropsWithoutRef,\n forwardRef,\n useImperativeHandle,\n useRef,\n CSSProperties,\n} from 'react'\n\nimport { useCheckPlacement } from '../_util/Placement'\nimport { VisuallyHiddenRangeInput } from '../_util/VisuallyHiddenRangeInput'\n\nimport { useSliderContext } from './context'\n\nimport type { CSSVariables } from '../../css-variables'\n\nexport interface SliderThumbProps {\n /**\n * Sets `--color`, for the theme to colour the thumb with. The size is not\n * set here but by `--thumb-size` on `Slider.Root`, so that the root can\n * leave the matching room around the track.\n */\n color?: string\n\n style?: CSSProperties & CSSVariables<'color' | 'translate'>\n}\n\nexport interface SliderThumbMethods {\n focus: () => void\n blur: () => void\n}\n\ntype Props = SliderThumbProps &\n Omit<ComponentPropsWithoutRef<'div'>, keyof SliderThumbProps>\n\nexport const Thumb = /* @__PURE__ */ forwardRef<SliderThumbMethods, Props>(\n function Thumb(\n {\n color,\n children,\n className,\n style,\n // Pulled out of `props` so that they land on the range input rather than\n // on the thumb: the input is the control, it takes the focus, and a name\n // on a plain div would be ignored. No declaration in `SliderThumbProps`\n // is needed for this — they are already on the div attributes it is\n // merged with, with the same types.\n 'aria-label': ariaLabel,\n 'aria-labelledby': ariaLabelledby,\n 'aria-describedby': ariaDescribedby,\n 'aria-valuetext': ariaValuetext,\n ...props\n },\n forwardedRef,\n ) {\n const inputRef = useRef<HTMLInputElement>(null)\n const {\n value,\n min,\n max,\n step,\n vertical,\n disabled,\n readonly,\n onChange,\n percent,\n thumbRef,\n } = useSliderContext()\n\n // The thumb is positioned against the track.\n useCheckPlacement('Slider.Thumb', 'Slider.Track')\n\n const methods = () => ({\n focus() {\n if (!disabled) inputRef.current?.focus()\n },\n blur() {\n inputRef.current?.blur()\n },\n })\n\n useImperativeHandle(forwardedRef, methods, [disabled])\n // Root focuses the thumb when a drag starts, wherever the user placed it.\n useImperativeHandle(thumbRef, methods, [disabled])\n\n return (\n <div\n className={className}\n data-disabled={disabled ? '' : undefined}\n data-readonly={readonly ? '' : undefined}\n {...props}\n style={{\n ...{ '--color': color },\n // Not a style but the mechanics of the position below: the thumb is\n // placed by a percentage of the track, which needs it out of flow\n // and measured from its own centre. `--translate` is there for a\n // thumb that should hang off its edge instead.\n position: 'absolute',\n translate: 'var(--translate, -50% -50%)',\n zIndex: 100,\n ...style,\n // Where the thumb sits is the component's decision, not a style: a\n // `left` from the caller would take it off the track, so it is\n // written after theirs.\n top: vertical ? `${percent}%` : '50%',\n left: !vertical ? `${percent}%` : '50%',\n }}\n >\n <VisuallyHiddenRangeInput\n ref={inputRef}\n value={value}\n min={min}\n max={max}\n step={step}\n disabled={disabled}\n aria-readonly={readonly}\n aria-orientation={vertical ? 'vertical' : 'horizontal'}\n aria-label={ariaLabel}\n aria-labelledby={ariaLabelledby}\n aria-describedby={ariaDescribedby}\n aria-valuetext={ariaValuetext}\n onChange={(event) => {\n if (readonly) {\n event.currentTarget.value = String(value)\n return\n }\n onChange?.(event.currentTarget.valueAsNumber)\n }}\n />\n {children}\n </div>\n )\n },\n)\n","import { ComponentPropsWithoutRef, CSSProperties, forwardRef } from 'react'\n\nimport { cssLength } from '@tremolo-ui/dom'\n\nimport { useComposedRefs } from '../../compose-refs'\nimport { Placement } from '../_util/Placement'\n\nimport { useSliderContext } from './context'\n\nimport type { CSSVariables } from '../../css-variables'\n\nexport interface SliderTrackProps {\n /**\n * How long the track is along the axis the slider runs. Sets `--length`;\n * the size the theme gives it stands when this is omitted.\n */\n length?: number | string\n /** How thick the track is across that axis. Sets `--thickness`. */\n thickness?: number | string\n\n /** Colour of the part from `min` to the value. Sets `--active`. */\n active?: string\n /** Colour of the rest of the track. Sets `--inactive`. */\n inactive?: string\n\n style?: CSSProperties &\n CSSVariables<'length' | 'thickness' | 'active' | 'inactive' | 'percent'>\n}\n\ntype Props = SliderTrackProps &\n Omit<ComponentPropsWithoutRef<'div'>, keyof SliderTrackProps>\n\nexport const Track = /* @__PURE__ */ forwardRef<HTMLDivElement, Props>(\n function Track(\n {\n length,\n thickness,\n active,\n inactive,\n children,\n className,\n style,\n ...props\n },\n forwardedRef,\n ) {\n const { vertical, reverse, disabled, percent, trackRef } =\n useSliderContext()\n\n // The track is what the pointer position is normalized against, so the\n // context ref is composed with any ref the caller passed.\n const composedRef = useComposedRefs<HTMLDivElement>(forwardedRef, trackRef)\n\n return (\n <div\n ref={composedRef}\n className={className}\n data-disabled={disabled ? '' : undefined}\n data-orientation={vertical ? 'vertical' : 'horizontal'}\n // Which end the value grows from. `percent` is already the position on\n // screen, so this only says which side of it is the filled one.\n data-flipped={vertical !== reverse ? '' : undefined}\n style={\n {\n '--active': active,\n '--inactive': inactive,\n '--length': cssLength(length),\n '--thickness': cssLength(thickness),\n // Where the value sits, for the theme to paint the fill with. The\n // component draws nothing itself: this is the one number CSS cannot\n // work out on its own.\n '--percent': `${percent}%`,\n // The thumb inside is placed against this box.\n position: 'relative',\n ...style,\n } as CSSProperties\n }\n {...props}\n >\n <Placement name=\"Slider.Track\">{children}</Placement>\n </div>\n )\n },\n)\n","import React, {\n ComponentPropsWithoutRef,\n CSSProperties,\n forwardRef,\n ReactNode,\n useCallback,\n useImperativeHandle,\n useMemo,\n useRef,\n} from 'react'\n\nimport {\n applyDelta,\n arrowKeyDirection,\n type AxisOptions,\n DEFAULT_DRAG_SENSITIVITY,\n DEFAULT_KEYBOARD_OPTIONS,\n DEFAULT_WHEEL_OPTIONS,\n type InputEventOption,\n type ModifierValue,\n selectModifier,\n valuePercent,\n wheelDirection,\n type XY,\n} from '@tremolo-ui/dom'\nimport { linearScale, type Scale } from '@tremolo-ui/functions'\n\nimport { useComposedRefs } from '../../compose-refs'\nimport { useCheckSteps } from '../../hooks/_internal/useCheckSteps'\nimport { useDragValue } from '../../hooks/useDragValue'\nimport { useWheel } from '../../hooks/useWheel'\n\nimport { SliderProvider } from './context'\nimport { Marks } from './Marks'\nimport { MarksOption } from './MarksOption'\nimport { Thumb, SliderThumbMethods } from './Thumb'\nimport { Track } from './Track'\n\nconst defaultExternalStyles: SliderProps['externalStyles'] = {\n cursor: 'pointer',\n}\n\nexport interface SliderProps {\n /** The current value. The slider shows only this, so update it from `onChange`. */\n value: number\n /** The value at the start of the travel. */\n min: number\n /** The value at the end of the travel. */\n max: number\n\n /**\n * Granularity of the value. A drag, the wheel and the arrow keys snap it to\n * multiples of `step`.\n *\n * @default 1\n */\n step?: number\n /**\n * How the value is distributed across the travel.\n *\n * Pick one of the scales from `@tremolo-ui/functions`: `linearScale`,\n * `exponentialScale`, `curveScale(n)`, `symmetricSkewScale(n)`, or\n * `skewScale(n)` for a value that has to match a JUCE parameter.\n *\n * @default linearScale\n */\n scale?: Scale\n /**\n * Run the slider vertically, with the value growing upwards. The range input\n * inside the thumb takes its `aria-orientation` from this.\n *\n * @default false\n */\n vertical?: boolean\n /**\n * Grow the value the other way: leftwards, or downwards when `vertical`.\n * The arrow keys follow the direction on screen.\n *\n * @default false\n */\n reverse?: boolean\n\n /**\n * The cursor to show while dragging. It is set on the dragged element, so it\n * stays while the pointer is outside the slider.\n *\n * @default { cursor: 'pointer' }\n */\n externalStyles?: {\n cursor?: CSSProperties['cursor']\n }\n /**\n * How much one notch of the wheel moves the value. It only acts while the\n * focus is inside, so that scrolling the page past the slider leaves it\n * alone.\n *\n * `['raw', n]` moves the value by `n`, and `['normalized', n]` by `n` of the\n * range between `min` and `max`. The result is snapped to `step`, except for\n * an amount set on a modifier key (`{ default: …, shift: … }`). `null` turns\n * the wheel off.\n *\n * @default ['raw', 1]\n */\n wheel?: ModifierValue<InputEventOption> | null\n /**\n * How much a drag moves the value, per modifier key.\n *\n * `1` is the pointer position itself, which is what a drag normally is here.\n * **Anything else turns the drag relative**: `0.1` makes the same movement\n * cover a tenth of the travel, so the value stops following the pointer and\n * starts moving a tenth as fast. Shift is bound to `0.1` by default, to\n * match what it does on the arrow keys.\n *\n * Pressing or releasing the key mid-drag does not disturb the value: the\n * travel so far is kept and the new sensitivity applies from there. **The\n * pointer and the value stay apart for the rest of the drag** — snapping\n * them back together on release would move the value nobody asked to move.\n *\n * @default { default: 1, shift: 0.1 }\n */\n dragSensitivity?: ModifierValue<number>\n\n /**\n * How much one arrow key press moves the value.\n *\n * `['raw', n]` moves the value by `n`, and `['normalized', n]` by `n` of the\n * range between `min` and `max`. The result is snapped to `step`, except for\n * an amount set on a modifier key, which is what lets shift move off the\n * grid. `null` turns the arrow keys off.\n *\n * The default moves by 1, and by 0.1 with shift. With a `step` above 1, raise\n * the amount to match: 1 would round straight back to where it started, and\n * a development build warns about it.\n *\n * @default { default: ['raw', 1], shift: ['raw', 0.1] }\n */\n keyboard?: ModifierValue<InputEventOption> | null\n\n /**\n * Make the slider unchangeable and remove it from the tab order.\n * The parts carry `data-disabled` while it is set.\n */\n disabled?: boolean\n /**\n * Make the value unchangeable.\n * The parts carry `data-readonly` while it is set.\n */\n readonly?: boolean\n /** Called with the new value when a drag, the wheel or an arrow key moves it. */\n onChange?: (value: number) => void\n /** Called when a drag starts, with the value where the track was pressed. */\n onDragStart?: (value: number) => void\n /** Called when the drag ends, with the value it ended on. */\n onDragEnd?: (value: number) => void\n /**\n * The slider renders exactly what you compose here; there is no default\n * markup to fall back to.\n *\n * @example\n * <Slider.Root value={value} min={0} max={100} onChange={setValue}>\n * <Slider.Track>\n * <Slider.Thumb aria-label=\"Level\" />\n * </Slider.Track>\n * </Slider.Root>\n */\n children: ReactNode\n}\n\nexport interface SliderMethods {\n focus: () => void\n blur: () => void\n}\n\ntype Props = SliderProps &\n Omit<ComponentPropsWithoutRef<'div'>, keyof SliderProps>\n\n/**\n * The wheel only acts while the focus is inside, so that scrolling a page past\n * the control does not change its value.\n */\nconst WHEEL_OPTIONS = { requireFocus: true }\n\nexport const Root = /* @__PURE__ */ forwardRef<SliderMethods, Props>(\n (\n {\n value,\n min,\n max,\n step = 1,\n scale = linearScale,\n vertical = false,\n reverse = false,\n externalStyles: _externalStyles,\n wheel = DEFAULT_WHEEL_OPTIONS,\n keyboard = DEFAULT_KEYBOARD_OPTIONS,\n dragSensitivity = DEFAULT_DRAG_SENSITIVITY,\n disabled = false,\n readonly = false,\n onChange,\n onDragStart,\n onDragEnd,\n className,\n style,\n children,\n onFocus,\n onBlur,\n onPointerDown,\n onKeyDown,\n ...props\n }: Props,\n forwardedRef,\n ) => {\n // -- state and ref ---\n const trackRef = useRef<HTMLDivElement>(null)\n const thumbRef = useRef<SliderThumbMethods>(null)\n // --- interpret props ---\n const externalStyles = { ...defaultExternalStyles, ..._externalStyles }\n const inactive = disabled || readonly\n\n // Measured from the left or the top, as CSS places things:\n // normal -> normal (right)\n // vertical -> reversed (up)\n // reverse -> reversed (left)\n // vertical & reverse -> normal (down)\n const displayReversed = vertical !== reverse\n const percent = valuePercent(value, { min, max, scale }, displayReversed)\n\n // --- internal functions ---\n // The pointer is normalized against the track on both axes; only the one\n // the slider runs along is read back. `AxisOptions` extends `ValueRange`,\n // so the same object also describes the scaling for `applyDelta`.\n const axis: AxisOptions = useMemo(\n () => ({ min, max, step, scale, reverse: displayReversed }),\n [min, max, step, scale, displayReversed],\n )\n\n useCheckSteps({ component: 'Slider', range: axis, keyboard, wheel })\n\n const handleKeyDown = useCallback(\n (event: React.KeyboardEvent<HTMLDivElement>) => {\n const direction = arrowKeyDirection(event.key)\n if (direction === null) return\n event.preventDefault()\n if (!keyboard || !onChange || inactive) return\n onChange(\n applyDelta(\n value,\n reverse ? -direction : direction,\n keyboard,\n axis,\n event,\n ),\n )\n },\n [keyboard, onChange, inactive, reverse, value, axis],\n )\n\n // --- hooks ---\n const valueOf = (v: XY<number>) => v[vertical ? 1 : 0]\n\n const { refCallback: dragRefCallback } = useDragValue<HTMLDivElement>({\n axis,\n baseElementRef: trackRef,\n cursor: inactive ? undefined : externalStyles.cursor,\n shouldStart: () => !inactive,\n sensitivity: (state) =>\n selectModifier(dragSensitivity, state.event).value,\n updateOnPointerDown: true,\n onChange: (v) => {\n if (inactive) return\n onChange?.(valueOf(v))\n },\n onDragStart: (v) => {\n if (inactive) return\n thumbRef.current?.focus()\n onDragStart?.(valueOf(v))\n },\n onDragEnd: (v) => {\n if (inactive) return\n onDragEnd?.(valueOf(v))\n },\n })\n\n const wheelRefCallback = useWheel<HTMLDivElement>((event) => {\n if (!wheel || !onChange || inactive) return\n // A notch the slider does not read — a sideways scroll on a vertical\n // slider — is left to the page rather than swallowed.\n const direction = wheelDirection(event, { horizontal: !vertical })\n if (direction === null) return\n event.preventDefault()\n onChange(\n applyDelta(value, reverse ? -direction : direction, wheel, axis, event),\n )\n }, WHEEL_OPTIONS)\n\n // Composed once, so React attaches the refs a single time instead of\n // detaching and re-attaching on every render.\n const rootRefCallback = useComposedRefs<HTMLDivElement>(\n dragRefCallback,\n wheelRefCallback,\n )\n\n const context = useMemo(\n () => ({\n value,\n min,\n max,\n step,\n scale,\n vertical,\n reverse,\n disabled,\n readonly,\n onChange,\n percent,\n trackRef,\n thumbRef,\n }),\n [\n value,\n min,\n max,\n step,\n scale,\n vertical,\n reverse,\n disabled,\n readonly,\n onChange,\n percent,\n ],\n )\n\n useImperativeHandle(forwardedRef, () => {\n return {\n focus() {\n if (!disabled) thumbRef.current?.focus()\n },\n blur() {\n thumbRef.current?.blur()\n },\n }\n }, [disabled])\n\n return (\n <SliderProvider value={context}>\n {/* oxlint-disable-next-line jsx-a11y/no-noninteractive-element-interactions -- the group is the pointer and keyboard event area, while the nested range input carries its control semantics */}\n <div\n className={className}\n ref={rootRefCallback}\n role=\"group\"\n // A press lands on the track or the thumb, neither of which can hold\n // focus, and the browser answers that by clearing the focus to the\n // body — undoing the focus the drag just gave the input. Taking the\n // focus here keeps it inside, and `onFocus` passes it to the input.\n tabIndex={-1}\n data-orientation={vertical ? 'vertical' : 'horizontal'}\n data-disabled={disabled ? '' : undefined}\n data-readonly={readonly ? '' : undefined}\n style={style}\n onPointerDown={onPointerDown}\n onKeyDown={(event) => {\n handleKeyDown(event)\n onKeyDown?.(event)\n }}\n onFocus={(event) => {\n if (!disabled) thumbRef.current?.focus()\n onFocus?.(event)\n }}\n onBlur={(event) => {\n thumbRef.current?.blur()\n onBlur?.(event)\n }}\n {...props}\n >\n {children}\n </div>\n </SliderProvider>\n )\n },\n)\n\n/**\n * Customizable slider\n */\nexport const Slider = {\n Root,\n Thumb,\n Track,\n Marks,\n MarksOption,\n}\n\nexport { useSliderContext, type SliderContextValue } from './context'\nexport { type SliderThumbMethods, type SliderThumbProps } from './Thumb'\nexport { type SliderTrackProps } from './Track'\nexport { type SliderMarksProps } from './Marks'\nexport { type SliderMarksOptionProps } from './MarksOption'\n","import { createContext, RefObject, useContext } from 'react'\n\nimport { type XY } from '@tremolo-ui/dom'\nimport type { Scale } from '@tremolo-ui/functions'\n\nimport type { XYPadThumbMethods } from './Thumb'\n\nexport type XYPadContextValue = {\n value: XY<number>\n min: XY<number>\n max: XY<number>\n step: XY<number>\n /** How the value of each axis is distributed across the travel. */\n scale: XY<Scale>\n reverse: XY<boolean>\n disabled: boolean\n readonly: boolean\n onChange?: (value: XY<number>) => void\n\n /**\n * Position of the thumb, 0-100 per axis, already accounting for `reverse`.\n */\n percent: XY<number>\n\n /** `Area` registers its element here; `Root` normalizes the pointer against it. */\n areaRef: RefObject<HTMLDivElement | null>\n /** `Thumb` registers itself here; `Root` focuses it when a drag starts. */\n thumbRef: RefObject<XYPadThumbMethods | null>\n}\n\nconst XYPadContext = /* @__PURE__ */ createContext<XYPadContextValue | null>(\n null,\n)\n\nexport const XYPadProvider = XYPadContext.Provider\n\n/**\n * Everything here is derived during render, so there is no state to keep in\n * sync: `value` comes from the props of `Root` and the rest follows from it.\n */\nexport function useXYPadContext(): XYPadContextValue\nexport function useXYPadContext<T>(selector: (state: XYPadContextValue) => T): T\nexport function useXYPadContext<T>(selector?: (state: XYPadContextValue) => T) {\n const context = useContext(XYPadContext)\n if (!context) throw new Error('Missing XYPadContext.Provider in the tree')\n return selector ? selector(context) : context\n}\n","import { ComponentPropsWithoutRef, CSSProperties, forwardRef } from 'react'\n\nimport { useComposedRefs } from '../../compose-refs'\nimport { Placement } from '../_util/Placement'\n\nimport { useXYPadContext } from './context'\n\nexport const Area = /* @__PURE__ */ forwardRef<\n HTMLDivElement,\n ComponentPropsWithoutRef<'div'>\n>(function Area({ children, className, style, ...props }, forwardedRef) {\n const { areaRef } = useXYPadContext()\n\n // The area is what the pointer position is normalized against, so the\n // context ref is composed with any ref the caller passed.\n const composedRef = useComposedRefs<HTMLDivElement>(forwardedRef, areaRef)\n\n return (\n <div\n ref={composedRef}\n className={className}\n style={\n {\n // The thumb inside is placed against this box.\n position: 'relative',\n ...style,\n } as CSSProperties\n }\n {...props}\n >\n <Placement name=\"XYPad.Area\">{children}</Placement>\n </div>\n )\n})\n","import {\n AriaAttributes,\n ComponentPropsWithoutRef,\n forwardRef,\n useImperativeHandle,\n useRef,\n CSSProperties,\n} from 'react'\n\nimport { toXY, type XYInput } from '@tremolo-ui/dom'\n\nimport { useCheckPlacement } from '../_util/Placement'\nimport { VisuallyHiddenRangeInput } from '../_util/VisuallyHiddenRangeInput'\n\nimport { useXYPadContext } from './context'\n\nimport type { CSSVariables } from '../../css-variables'\n\nexport interface XYPadThumbProps {\n /**\n * Sets `--color`, for the theme to colour the thumb with. The size is not\n * set here but by `--thumb-size` on `XYPad.Root`, so that the root can leave\n * the matching room around the area.\n */\n color?: string\n\n /**\n * The accessible name of each axis, as `[x, y]`. There are two range inputs\n * inside the thumb, so each takes a name of its own; a single string names\n * them both, which is rarely what you want.\n */\n 'aria-label'?: XYInput<AriaAttributes['aria-label']>\n /** The ids of what labels each axis, as `[x, y]` or one for both. */\n 'aria-labelledby'?: XYInput<AriaAttributes['aria-labelledby']>\n /** The ids of what describes each axis, as `[x, y]` or one for both. */\n 'aria-describedby'?: XYInput<AriaAttributes['aria-describedby']>\n /**\n * What the value of each axis means, when the number does not say it, as\n * `[x, y]` or one for both.\n */\n 'aria-valuetext'?: XYInput<AriaAttributes['aria-valuetext']>\n\n style?: CSSProperties & CSSVariables<'color' | 'translate'>\n}\n\nexport interface XYPadThumbMethods {\n focus: () => void\n blur: () => void\n}\n\ntype Props = XYPadThumbProps &\n Omit<ComponentPropsWithoutRef<'div'>, keyof XYPadThumbProps>\n\nexport const Thumb = /* @__PURE__ */ forwardRef<XYPadThumbMethods, Props>(\n function Thumb(\n {\n color,\n children,\n className,\n style,\n 'aria-label': ariaLabel,\n 'aria-labelledby': ariaLabelledby,\n 'aria-describedby': ariaDescribedby,\n 'aria-valuetext': ariaValuetext,\n ...props\n },\n forwardedRef,\n ) {\n const xInputRef = useRef<HTMLInputElement>(null)\n const yInputRef = useRef<HTMLInputElement>(null)\n const {\n value,\n min,\n max,\n step,\n disabled,\n readonly,\n onChange,\n percent,\n thumbRef,\n } = useXYPadContext()\n\n // The thumb is positioned against the area.\n useCheckPlacement('XYPad.Thumb', 'XYPad.Area')\n\n // Per axis, since the thumb holds one input for each.\n const labels = toXY(ariaLabel)\n const labelledby = toXY(ariaLabelledby)\n const describedby = toXY(ariaDescribedby)\n const valueText = toXY(ariaValuetext)\n\n const methods = () => ({\n focus() {\n if (!disabled) xInputRef.current?.focus()\n },\n blur() {\n xInputRef.current?.blur()\n yInputRef.current?.blur()\n },\n })\n\n useImperativeHandle(forwardedRef, methods, [disabled])\n // Root focuses the thumb when a drag starts, wherever the user placed it.\n useImperativeHandle(thumbRef, methods, [disabled])\n\n return (\n <div\n className={className}\n data-disabled={disabled ? '' : undefined}\n data-readonly={readonly ? '' : undefined}\n {...props}\n style={{\n ...{ '--color': color },\n // Not a style but the mechanics of the position below: the thumb is\n // placed by a percentage of the area, which needs it out of flow and\n // measured from its own centre.\n position: 'absolute',\n translate: 'var(--translate, -50% -50%)',\n zIndex: 100,\n ...style,\n // Where the thumb sits is the component's decision, not a style: a\n // `left` from the caller would take it off the area, so it is written\n // after theirs.\n left: `${percent[0]}%`,\n top: `${percent[1]}%`,\n }}\n >\n {([0, 1] as const).map((axis) => (\n <VisuallyHiddenRangeInput\n key={axis}\n ref={axis === 0 ? xInputRef : yInputRef}\n data-axis={axis === 0 ? 'x' : 'y'}\n value={value[axis]}\n min={min[axis]}\n max={max[axis]}\n step={step[axis]}\n disabled={disabled}\n aria-readonly={readonly}\n aria-orientation={axis === 0 ? 'horizontal' : 'vertical'}\n aria-label={labels[axis] ?? (axis === 0 ? 'x' : 'y')}\n aria-labelledby={labelledby[axis]}\n aria-describedby={describedby[axis]}\n aria-valuetext={valueText[axis]}\n onChange={(event) => {\n if (readonly) {\n event.currentTarget.value = String(value[axis])\n return\n }\n const next = [...value] as [number, number]\n next[axis] = event.currentTarget.valueAsNumber\n onChange?.(next)\n }}\n />\n ))}\n {children}\n </div>\n )\n },\n)\n","import {\n ComponentPropsWithoutRef,\n CSSProperties,\n forwardRef,\n ReactNode,\n Ref,\n useCallback,\n useImperativeHandle,\n useMemo,\n useRef,\n} from 'react'\n\nimport {\n applyDelta,\n arrowKeyMove,\n type AxisOptions,\n DEFAULT_DRAG_SENSITIVITY,\n DEFAULT_KEYBOARD_OPTIONS,\n DEFAULT_WHEEL_OPTIONS,\n InputEventOption,\n ModifierState,\n type ModifierValue,\n selectModifier,\n toXY,\n valuePercent,\n wheelMove,\n type XY,\n type XYInput,\n} from '@tremolo-ui/dom'\nimport { linearScale, type Scale } from '@tremolo-ui/functions'\n\nimport { useComposedRefs } from '../../compose-refs'\nimport { useCheckSteps } from '../../hooks/_internal/useCheckSteps'\nimport { useDragValue } from '../../hooks/useDragValue'\nimport { useWheel } from '../../hooks/useWheel'\n\nimport { Area } from './Area'\nimport { XYPadProvider } from './context'\nimport { Thumb, XYPadThumbMethods } from './Thumb'\n\nconst defaultExternalStyles: XYPadProps['externalStyles'] = {\n cursor: 'pointer',\n}\n\n/**\n * Two-dimensional slider component.\n *\n * The per-axis settings mirror `Slider`, given as `[x, y]` tuples. A plain\n * value applies to both axes.\n */\nexport interface XYPadProps {\n /** The current value as `[x, y]`. The pad shows only this, so update it from `onChange`. */\n value: XY<number>\n /** The value at the start of each axis, as `[x, y]` or one number for both. */\n min: XYInput<number>\n /** The value at the end of each axis, as `[x, y]` or one number for both. */\n max: XYInput<number>\n\n /**\n * Granularity of each axis, as `[x, y]` or one number for both. A drag, the\n * wheel and the arrow keys snap the value to multiples of it.\n *\n * @default 1\n */\n step?: XYInput<number>\n /**\n * How the value of each axis is distributed across the travel, as `[x, y]`\n * or one scale for both.\n *\n * Pick one of the scales from `@tremolo-ui/functions`: `linearScale`,\n * `exponentialScale`, `curveScale(n)`, `symmetricSkewScale(n)`, or\n * `skewScale(n)` for a value that has to match a JUCE parameter.\n *\n * @default linearScale\n */\n scale?: XYInput<Scale>\n /**\n * Grow an axis the other way, as `[x, y]` or one for both. By default x grows\n * rightwards and y downwards. The arrow keys follow the direction on screen.\n *\n * @default false\n */\n reverse?: XYInput<boolean>\n\n /**\n * How much one notch of the wheel moves the value. It only acts while the\n * focus is inside, so that scrolling the page past the pad leaves it alone.\n * Scrolling sideways, or with shift held, moves x; otherwise it moves y.\n *\n * `['raw', n]` moves the value by `n`, and `['normalized', n]` by `n` of the\n * range of that axis. The result is snapped to `step`, except for an amount\n * set on a modifier key (`{ default: …, alt: … }`). `null` turns the wheel\n * off.\n *\n * @default ['raw', 1]\n */\n wheel?: ModifierValue<InputEventOption> | null\n /**\n * How much a drag moves the value, per modifier key.\n *\n * `1` is the pointer position itself, which is what a drag normally is here.\n * **Anything else turns the drag relative**: `0.1` makes the same movement\n * cover a tenth of the travel, so the value stops following the pointer and\n * starts moving a tenth as fast. Shift is bound to `0.1` by default, to\n * match what it does on the arrow keys.\n *\n * Pressing or releasing the key mid-drag does not disturb the value: the\n * travel so far is kept and the new sensitivity applies from there. **The\n * pointer and the value stay apart for the rest of the drag** — snapping\n * them back together on release would move the value nobody asked to move.\n *\n * @default { default: 1, shift: 0.1 }\n */\n dragSensitivity?: ModifierValue<number>\n\n /**\n * How much one arrow key press moves the value. Left and right move x, up\n * and down move y.\n *\n * `['raw', n]` moves the value by `n`, and `['normalized', n]` by `n` of the\n * range of that axis. The result is snapped to `step`, except for an amount\n * set on a modifier key, which is what lets shift move off the grid. `null`\n * turns the arrow keys off.\n *\n * The default moves by 1, and by 0.1 with shift. With a `step` above 1, raise\n * the amount to match: 1 would round straight back to where it started, and\n * a development build warns about it.\n *\n * @default { default: ['raw', 1], shift: ['raw', 0.1] }\n */\n keyboard?: ModifierValue<InputEventOption> | null\n\n /**\n * The cursor to show while dragging. It is set on the dragged element, so it\n * stays while the pointer is outside the pad.\n *\n * @default { cursor: 'pointer' }\n */\n externalStyles?: {\n cursor?: CSSProperties['cursor']\n }\n\n /**\n * Make the pad unchangeable and remove its thumb from the tab order.\n * The parts carry `data-disabled` while it is set.\n */\n disabled?: boolean\n /**\n * Make the pad unchangeable while leaving its thumb focusable.\n * The parts carry `data-readonly` while it is set.\n */\n readonly?: boolean\n\n /** Called with the new value when a drag, the wheel or an arrow key moves it. */\n onChange?: (value: XY<number>) => void\n /** Called when a drag starts, with the value where the area was pressed. */\n onDragStart?: (value: XY<number>) => void\n /** Called when the drag ends, with the value it ended on. */\n onDragEnd?: (value: XY<number>) => void\n\n /**\n * The pad renders exactly what you compose here; there is no default\n * markup to fall back to.\n *\n * @example\n * <XYPad.Root value={[x, y]} min={0} max={100} onChange={setValue}>\n * <XYPad.Area>\n * <XYPad.Thumb />\n * </XYPad.Area>\n * </XYPad.Root>\n */\n children: ReactNode\n}\n\nexport interface XYPadMethods {\n focus: () => void\n blur: () => void\n original: Ref<HTMLDivElement>\n}\n\ntype Props = XYPadProps &\n Omit<ComponentPropsWithoutRef<'div'>, keyof XYPadProps>\n\n/**\n * The wheel only acts while the focus is inside, so that scrolling a page past\n * the control does not change its value.\n */\nconst WHEEL_OPTIONS = { requireFocus: true }\n\nexport const Root = /* @__PURE__ */ forwardRef<XYPadMethods, Props>(\n (\n {\n value,\n min: _min,\n max: _max,\n step: _step = 1,\n scale: _scale = linearScale,\n reverse: _reverse = false,\n wheel = DEFAULT_WHEEL_OPTIONS,\n keyboard = DEFAULT_KEYBOARD_OPTIONS,\n dragSensitivity = DEFAULT_DRAG_SENSITIVITY,\n className,\n style,\n externalStyles: _externalStyles,\n disabled = false,\n readonly = false,\n onChange,\n onDragStart,\n onDragEnd,\n onPointerDown,\n onKeyDown,\n onFocus,\n onBlur,\n children,\n ...props\n }: Props,\n forwardedRef,\n ) => {\n // -- state and ref ---\n const rootRef = useRef<HTMLDivElement>(null)\n const areaRef = useRef<HTMLDivElement>(null)\n const thumbRef = useRef<XYPadThumbMethods>(null)\n\n // --- interpret props ---\n const externalStyles = { ...defaultExternalStyles, ..._externalStyles }\n const inactive = disabled || readonly\n\n const min = useMemo(() => toXY(_min), [_min])\n const max = useMemo(() => toXY(_max), [_max])\n const step = useMemo(() => toXY(_step), [_step])\n const scale = useMemo(() => toXY(_scale), [_scale])\n const reverse = useMemo(() => toXY(_reverse), [_reverse])\n\n const percent = useMemo(\n (): XY<number> =>\n [0, 1].map((i) =>\n valuePercent(\n value[i],\n { min: min[i], max: max[i], scale: scale[i] },\n reverse[i],\n ),\n ) as XY<number>,\n [value, min, max, scale, reverse],\n )\n\n // --- internal functions ---\n // `AxisOptions` extends `ValueRange`, so the same pair also describes the\n // scaling for `applyDelta`. Its `reverse` only concerns the drag, where\n // positions follow the screen; the key and wheel handlers below flip the\n // direction themselves.\n const axis = useMemo(\n (): XY<AxisOptions> =>\n [0, 1].map((i) => ({\n min: min[i],\n max: max[i],\n step: step[i],\n scale: scale[i],\n reverse: reverse[i],\n })) as XY<AxisOptions>,\n [min, max, step, scale, reverse],\n )\n\n useCheckSteps({\n component: 'XYPad',\n axis: 'x',\n range: axis[0],\n keyboard,\n wheel,\n })\n useCheckSteps({\n component: 'XYPad',\n axis: 'y',\n range: axis[1],\n keyboard,\n wheel,\n })\n\n const withAxis = useCallback(\n (axis: 0 | 1, next: number): XY<number> =>\n axis === 0 ? [next, value[1]] : [value[0], next],\n [value],\n )\n\n /** @param i 0 = x, 1 = y */\n const nudge = useCallback(\n (\n i: 0 | 1,\n direction: number,\n option: ModifierValue<InputEventOption>,\n modifiers: ModifierState,\n ): XY<number> =>\n withAxis(\n i,\n applyDelta(value[i], direction, option, axis[i], modifiers),\n ),\n [value, axis, withAxis],\n )\n\n const handleKeyDown = useCallback(\n (event: React.KeyboardEvent<HTMLDivElement>) => {\n // The key picks the axis, whichever of the two inputs holds the\n // focus: the focus lands on the x input, so reading the axis off the\n // input would leave the y axis with no keys at all.\n const move = arrowKeyMove(event.key)\n if (!move) return\n event.preventDefault()\n if (!onChange || inactive || !keyboard) return\n const { axis: i, direction } = move\n onChange(nudge(i, reverse[i] ? -direction : direction, keyboard, event))\n },\n [onChange, inactive, keyboard, reverse, nudge],\n )\n\n // --- hooks ---\n\n const { refCallback: dragRefCallback } = useDragValue<HTMLDivElement>({\n axis,\n baseElementRef: areaRef,\n sensitivity: (state) =>\n selectModifier(dragSensitivity, state.event).value,\n updateOnPointerDown: true,\n cursor: inactive ? undefined : externalStyles.cursor,\n shouldStart: () => !inactive,\n onChange: (v) => {\n if (inactive) return\n onChange?.(v)\n },\n onDragStart: (v) => {\n if (inactive) return\n thumbRef.current?.focus()\n onDragStart?.(v)\n },\n onDragEnd: (v) => {\n if (inactive) return\n onDragEnd?.(v)\n },\n })\n\n const wheelRefCallback = useWheel<HTMLDivElement>((event) => {\n if (!onChange || inactive || !wheel) return\n const move = wheelMove(event)\n if (!move) return\n event.preventDefault()\n const { axis: i, direction } = move\n onChange(nudge(i, reverse[i] ? -direction : direction, wheel, event))\n }, WHEEL_OPTIONS)\n\n // Composed once, so React attaches the refs a single time instead of\n // detaching and re-attaching on every render.\n const rootRefCallback = useComposedRefs<HTMLDivElement>(\n rootRef,\n dragRefCallback,\n wheelRefCallback,\n )\n\n const context = useMemo(\n () => ({\n value,\n min,\n max,\n step,\n scale,\n reverse,\n disabled,\n readonly,\n onChange,\n percent,\n areaRef,\n thumbRef,\n }),\n [\n value,\n min,\n max,\n step,\n scale,\n reverse,\n disabled,\n readonly,\n onChange,\n percent,\n ],\n )\n\n useImperativeHandle(forwardedRef, () => {\n return {\n focus() {\n if (!disabled) thumbRef.current?.focus()\n },\n blur() {\n thumbRef.current?.blur()\n },\n original: rootRef,\n }\n }, [disabled])\n\n return (\n <XYPadProvider value={context}>\n {/* oxlint-disable-next-line jsx-a11y/no-noninteractive-element-interactions -- the group handles pointer and keyboard input shared by its two range controls */}\n <div\n className={className}\n ref={rootRefCallback}\n role=\"group\"\n tabIndex={-1}\n data-disabled={disabled ? '' : undefined}\n data-readonly={readonly ? '' : undefined}\n style={style}\n onPointerDown={onPointerDown}\n onKeyDown={(event) => {\n handleKeyDown(event)\n onKeyDown?.(event)\n }}\n onFocus={(event) => {\n if (!disabled) thumbRef.current?.focus()\n onFocus?.(event)\n }}\n onBlur={(event) => {\n thumbRef.current?.blur()\n onBlur?.(event)\n }}\n {...props}\n >\n {children}\n </div>\n </XYPadProvider>\n )\n },\n)\n\n/**\n * Simple XYPad\n */\nexport const XYPad = {\n Root,\n Thumb,\n Area,\n}\n\nexport { type XYPadThumbProps, type XYPadThumbMethods } from './Thumb'\nexport { useXYPadContext, type XYPadContextValue } from './context'\n","import { DependencyList, useEffect, useRef } from 'react'\n\nimport { useCallbackRef } from './_internal/useCallbackRef'\n\nexport function useAnimationFrame(\n callback = () => {},\n deps: DependencyList = [],\n) {\n const reqIdRef = useRef(-1)\n // Read through a ref rather than depended on: `useAnimationFrame(() => ...)`\n // is a new function on every render, and a callback that renders would then\n // cancel and re-schedule its own loop on every frame. What restarts the loop\n // is the caller's `deps`, and nothing else.\n const runCallback = useCallbackRef(callback)\n\n useEffect(() => {\n // Kept inside the effect: as a `useCallback` the loop would have to\n // reference itself before it is declared, which the compiler rules reject.\n const loop = () => {\n reqIdRef.current = requestAnimationFrame(loop)\n runCallback()\n }\n\n reqIdRef.current = requestAnimationFrame(loop)\n return () => cancelAnimationFrame(reqIdRef.current)\n // oxlint-disable-next-line react-hooks/exhaustive-deps\n }, [runCallback, ...deps])\n}\n","import { useEffect, useState } from 'react'\n\nimport { createDrag, type DragState } from '@tremolo-ui/dom'\n\nimport { useCallbackRef } from './_internal/useCallbackRef'\n\nexport interface UseDragOptions {\n /**\n * Pixels the pointer has to travel before the drag counts as one.\n *\n * A click moves the pointer by a pixel or two, so without a threshold a\n * double click reports a drag between the two presses.\n *\n * @default 1\n */\n threshold?: number\n\n /** CSS cursor to show while dragging. Applied to the element itself. */\n cursor?: string\n\n /**\n * Hide the pointer and read its movement directly, instead of following it\n * around the screen.\n *\n * @see DragOptions.pointerLock\n * @default false\n */\n pointerLock?: boolean\n\n /**\n * Called on every move once the drag has started, with the pointer position\n * relative to the element and how far it moved since the last call.\n *\n * @param state the whole drag, for anything the four numbers leave out —\n * the pointer event and its modifier keys, most of all.\n */\n onDrag?: (\n x: number,\n y: number,\n deltaX: number,\n deltaY: number,\n state: DragState,\n ) => void\n /** Called once the pointer has moved past `threshold`, not on pointerdown. */\n onDragStart?: (state: DragState) => void\n /** Called when the pointer is released, only if the drag ever started. */\n onDragEnd?: (state: DragState) => void\n}\n\n/**\n * Track a pointer drag on an element.\n *\n * @returns a ref callback to attach to the element being dragged\n */\nexport function useDrag<T extends Element>({\n threshold = 1,\n cursor,\n pointerLock,\n onDrag,\n onDragStart,\n onDragEnd,\n}: UseDragOptions): (node: T | null) => void {\n const dragHandler = useCallbackRef(onDrag)\n const dragStartHandler = useCallbackRef(onDragStart)\n const dragEndHandler = useCallbackRef(onDragEnd)\n\n // The node is held in state rather than bound in the ref callback itself, so\n // that re-attaching the ref with the same node does not restart the drag.\n // React re-attaches on every render when the caller passes an inline ref.\n const [node, setNode] = useState<T | null>(null)\n\n useEffect(() => {\n if (!node) return\n\n const instance = createDrag(node, {\n threshold,\n cursor,\n pointerLock,\n onDragStart: (state) => dragStartHandler(state),\n onDrag: (state) =>\n dragHandler(state.x, state.y, state.deltaX, state.deltaY, state),\n onDragEnd: (state) => dragEndHandler(state),\n })\n\n return () => instance.destroy()\n }, [\n node,\n threshold,\n cursor,\n pointerLock,\n dragHandler,\n dragStartHandler,\n dragEndHandler,\n ])\n\n return setNode\n}\n","import { useCallback, useEffect, useRef } from 'react'\n\nimport { useCallbackRef } from './_internal/useCallbackRef'\n\ntype Target = EventTarget | null | (() => EventTarget | null)\ntype Options = boolean | AddEventListenerOptions\n\nexport function useEventListener<K extends keyof DocumentEventMap>(\n target: Target,\n event: K,\n handler: (event: DocumentEventMap[K]) => void,\n options?: Options,\n): VoidFunction\nexport function useEventListener<K extends keyof WindowEventMap>(\n target: Target,\n event: K,\n handler: (event: WindowEventMap[K]) => void,\n options?: Options,\n): VoidFunction\nexport function useEventListener<K extends keyof GlobalEventHandlersEventMap>(\n target: Target,\n event: K,\n handler: (event: GlobalEventHandlersEventMap[K]) => void,\n options?: Options,\n): VoidFunction\nexport function useEventListener(\n target: Target,\n event: string,\n handler: (event: Event) => void,\n options?: Options,\n) {\n const listener = useCallbackRef(handler)\n const cleanupRef = useRef<VoidFunction>(() => {})\n\n useEffect(() => {\n const node = typeof target === 'function' ? target() : (target ?? document)\n\n if (!node) return\n\n node.addEventListener(event, listener, options)\n const cleanup = () => {\n node.removeEventListener(event, listener, options)\n }\n cleanupRef.current = cleanup\n\n return () => {\n cleanup()\n if (cleanupRef.current === cleanup) cleanupRef.current = () => {}\n }\n }, [event, target, options, listener])\n\n return useCallback(() => cleanupRef.current(), [])\n}\n","import { useEffect, useRef } from 'react'\n\nexport function useInterval(callback: () => void, delay: number | null) {\n const savedCallback = useRef(callback)\n\n // Remember the latest callback if it changes.\n useEffect(() => {\n savedCallback.current = callback\n }, [callback])\n\n // Set up the interval.\n useEffect(() => {\n // Don't schedule if no delay is specified.\n // Note: 0 is a valid value for delay.\n if (delay === null) {\n return\n }\n\n const id = setInterval(() => {\n savedCallback.current()\n }, delay)\n\n return () => {\n clearInterval(id)\n }\n }, [delay])\n}\n","import { useEffect, useMemo, useSyncExternalStore } from 'react'\n\nimport {\n createMIDIAccess,\n type MIDIAccessInstance,\n type MIDIAccessOptions,\n type MIDIAccessState,\n} from '@tremolo-ui/dom'\n\nconst INITIAL_STATE: MIDIAccessState = {\n midiAccess: null,\n error: null,\n inputs: [],\n}\n\nfunction createMIDIAccessStore() {\n let instance: MIDIAccessInstance | null = null\n let unsubscribe: VoidFunction | null = null\n let state = INITIAL_STATE\n const listeners = new Set<VoidFunction>()\n\n function emit() {\n for (const listener of listeners) listener()\n }\n\n function connect() {\n const nextInstance = createMIDIAccess()\n instance = nextInstance\n state = nextInstance.getState()\n unsubscribe = nextInstance.subscribe(() => {\n state = nextInstance.getState()\n emit()\n })\n emit()\n return nextInstance\n }\n\n function disconnect(currentInstance: MIDIAccessInstance) {\n if (instance !== currentInstance) return\n\n unsubscribe?.()\n unsubscribe = null\n instance = null\n state = INITIAL_STATE\n currentInstance.destroy()\n emit()\n }\n\n function request(options?: MIDIAccessOptions) {\n instance?.request(options)\n }\n\n return {\n connect,\n disconnect,\n request,\n getState: () => state,\n getServerState: () => INITIAL_STATE,\n subscribe: (listener: VoidFunction) => {\n listeners.add(listener)\n return () => listeners.delete(listener)\n },\n }\n}\n\n/**\n * Hooks for requesting MIDI access in the browser. The first argument allows you to choose whether to request access on mount.\n *\n * `inputs` follows the devices: it changes as one is plugged in or unplugged,\n * so a list of devices needs no `statechange` listener of its own.\n *\n * `request` takes `{ sysex: true }` when the app needs system exclusive\n * messages. Browsers treat that as a separate, more sensitive permission, so\n * ask for it only when it is actually used.\n */\nexport function useMIDIAccess(requestOnMount = true) {\n const store = useMemo(() => createMIDIAccessStore(), [])\n\n const { midiAccess, error, inputs } = useSyncExternalStore(\n store.subscribe,\n store.getState,\n store.getServerState,\n )\n\n useEffect(() => {\n const instance = store.connect()\n return () => store.disconnect(instance)\n }, [store])\n\n useEffect(() => {\n if (requestOnMount) store.request()\n }, [store, requestOnMount])\n\n return { request: store.request, midiAccess, error, inputs }\n}\n","import { useEffect, useRef } from 'react'\n\nimport {\n createMIDIInput,\n type MIDIInputHandlers,\n type MIDIInputInstance,\n} from '@tremolo-ui/dom'\n\n/**\n * Handle MIDI input events. To be used with {@link useMIDIAccess}.\n *\n * The handlers are read fresh on every event, so writing them inline is fine:\n * the listeners are attached once per `midiAccess` and stay attached. Devices\n * plugged in later are picked up without anything having to be rebuilt.\n *\n * @example\n * const { midiAccess } = useMIDIAccess()\n * useMIDIInput(midiAccess, {\n * onNoteOnEvent: (note, velocity) => play(note, velocity / 127),\n * onNoteOffEvent: (note) => stop(note),\n * onControlChangeEvent: (controller, value) => {\n * if (controller === 1) setModulation(value / 127)\n * },\n * })\n */\nexport function useMIDIInput(\n midiAccess: MIDIAccess | null,\n handlers: MIDIInputHandlers,\n) {\n // Read when the instance is created. The effect below keeps it current, and\n // runs right after, so a stale handler is replaced within the same commit.\n const latest = useRef(handlers)\n const instanceRef = useRef<MIDIInputInstance | null>(null)\n\n useEffect(() => {\n const instance = createMIDIInput(midiAccess, latest.current)\n instanceRef.current = instance\n\n return () => {\n instanceRef.current = null\n instance.destroy()\n }\n }, [midiAccess])\n\n // Runs after every render: handlers come from props and are cheap to push,\n // and updating in place leaves the listeners untouched.\n useEffect(() => {\n latest.current = handlers\n instanceRef.current?.update(handlers)\n })\n}\n","import { useEffect, useRef } from 'react'\n\nimport { createMIDIMessage, type MIDIMessageInstance } from '@tremolo-ui/dom'\n\n/**\n * Hooks for when you want to process MIDI events in more detail than useMIDIInput.\n *\n * The handler is read fresh on every event, so writing it inline is fine: the\n * listeners are attached once per `midiAccess` and stay attached, and devices\n * plugged in later are picked up.\n */\nexport function useMIDIMessage(\n midiAccess: MIDIAccess | null,\n onMIDIMessage: (event: MIDIMessageEvent) => void,\n) {\n const latest = useRef(onMIDIMessage)\n const instanceRef = useRef<MIDIMessageInstance | null>(null)\n\n useEffect(() => {\n const instance = createMIDIMessage(midiAccess, (event) =>\n latest.current(event),\n )\n instanceRef.current = instance\n\n return () => {\n instanceRef.current = null\n instance.destroy()\n }\n }, [midiAccess])\n\n useEffect(() => {\n latest.current = onMIDIMessage\n })\n}\n"],"mappings":"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O;GAAU;GAAO;GAAY;GAAK;GAAK;EAAW,CAAC;EAE9D,MAAM,QAAQ,aAEV,WACA,QACA,cACG;GACH,YACE,iBAAiB,OAAO,WAAW,QAAQ,QAAQ,SAAS,CAC9D;EACF,GACA;GAAC;GAAa;GAAO;EAAM,CAC7B;EAGA,MAAM,mBAAmB,UACtB,UAAU;GACT,MAAM,YAAY,eAAe,KAAK;GACtC,IAAI,CAAC,SAAS,YAAY,cAAc,MAAM;GAC9C,MAAM,eAAe;GACrB,MAAM,WAAW,OAAO,KAAK;EAC/B,GACA,EAAE,cAAc,KAAK,CACvB;EAEA,MAAM,UAAU,eACP;GACL;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA,UAAU;GACV;GACA;GACA;GACA;EACF,IACA;GACE;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;EACF,CACF;EAEA,oBAAoB,oBAAoB;GACtC,OAAO;IACL,QAAQ;KACN,IAAI,CAAC,UAAU,SAAS,SAAS,MAAM;IACzC;IACA,OAAO;KACL,SAAS,SAAS,KAAK;IACzB;GACF;EACF,GAAG,CAAC,QAAQ,CAAC;EAEb,OACE,oBAAC,qBAAD;GAAqB,OAAO;GAC1B,UAAA,oBAAC,OAAD;IACE,KAAK;IACM;IACX,iBAAe,WAAW,KAAK,KAAA;IAC/B,iBAAe,WAAW,KAAK,KAAA;IACxB;IACP,GAAI;IAEH;GACE,CAAA;EACc,CAAA;CAEzB,CAOA;CACA;CACA;CACA;CACA;AACF;;;;AC3GA,MAAa,QAAQ,EAAE,MAAA,gBA7Na,WAClC,SAAS,KACP,EACE,WACA,YAAY,MACZ,UAAU,KACV,mBACA,yBAAyB,QACzB,YAAY,OACZ,gBAAgB,IAChB,SAAS,GACT,qBAAqB,KACrB,sBAAsB,IACtB,OACA,WACA,OACA,UACA,SACA,YACA,YAC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"}
|
|
1
|
+
{"version":3,"file":"index.js","names":["Root","Root","Thumb","WHEEL_OPTIONS","Root","Root","Root","Root","Thumb","WHEEL_OPTIONS","Root"],"sources":["../src/components/AnimationCanvas/index.tsx","../src/hooks/_internal/useCallbackRef.ts","../src/hooks/useDropZone.ts","../src/components/DropZone/index.tsx","../src/components/FileInput/context.tsx","../src/components/FileInput/Trigger.tsx","../src/components/FileInput/index.tsx","../src/hooks/_internal/useChangeGesture.ts","../src/hooks/_internal/useCheckSteps.ts","../src/hooks/useDragValue.ts","../src/hooks/useWheel.ts","../src/components/_util/Placement.tsx","../src/components/Knob/context.tsx","../src/components/Knob/ActiveLine.tsx","../src/components/Knob/InactiveLine.tsx","../src/components/Knob/SVGRoot.tsx","../src/components/Knob/Thumb.tsx","../src/components/Knob/index.tsx","../src/components/NumberInput/context.tsx","../src/hooks/useLongPress.ts","../src/components/NumberInput/StepperButton.tsx","../src/components/NumberInput/DecrementStepper.tsx","../src/components/NumberInput/IncrementStepper.tsx","../src/components/NumberInput/InputField.tsx","../src/components/NumberInput/Stepper.tsx","../src/components/NumberInput/index.tsx","../src/components/Piano/index.tsx","../src/components/PointsEditor/Background.tsx","../src/components/PointsEditor/context.tsx","../src/components/PointsEditor/Container.tsx","../src/components/_util/VisuallyHiddenRangeInput.tsx","../src/components/PointsEditor/Point.tsx","../src/components/PointsEditor/SelectionBox.tsx","../src/components/PointsEditor/index.tsx","../src/components/Slider/context.tsx","../src/components/Slider/MarksOption.tsx","../src/components/Slider/Marks.tsx","../src/components/Slider/Thumb.tsx","../src/components/Slider/Track.tsx","../src/components/Slider/index.tsx","../src/components/XYPad/context.tsx","../src/components/XYPad/Area.tsx","../src/components/XYPad/Thumb.tsx","../src/components/XYPad/index.tsx","../src/hooks/useAnimationFrame.ts","../src/hooks/useDrag.ts","../src/hooks/useEventListener.ts","../src/hooks/useMIDIAccess.ts","../src/hooks/useMIDIInput.ts","../src/hooks/useMIDIMessage.ts"],"sourcesContent":["import {\n ComponentPropsWithoutRef,\n forwardRef,\n useEffect,\n useRef,\n useState,\n} from 'react'\n\nimport {\n createAnimationCanvas,\n type AnimationCanvasInstance,\n type CanvasDrawFunction,\n type CanvasInitFunction,\n} from '@tremolo-ui/dom'\n\nimport { useComposedRefs } from '../../compose-refs'\n\nexport interface AnimationCanvasCommonProps {\n /**\n * Draw one frame. It is given the 2D context and the frame: the size in CSS\n * pixels, `count`, `deltaTime`, `elapsedTime` and `fps`.\n *\n * A new function replaces the old one in place, so writing it inline does\n * not restart anything.\n */\n draw: CanvasDrawFunction\n /**\n * Called once before the first frame, with the context and the size in CSS\n * pixels. Set up what every frame shares here.\n */\n init?: CanvasInitFunction\n /**\n * Redraw on every animation frame. Turn it off to draw only when there is a\n * reason to: when the canvas mounts, when it is resized, and when the\n * component re-renders.\n *\n * @default true\n */\n animate?: boolean\n /**\n * Read once, when the 2D context is created, so changing it later has no\n * effect. Passing a fresh object on every render is therefore harmless.\n *\n * @see https://developer.mozilla.org/docs/Web/API/HTMLCanvasElement/getContext#contextattributes\n */\n options?: CanvasRenderingContext2DSettings\n /**\n * Carry the drawing across a resize, so that the canvas does not blank for a\n * frame while the new size is drawn. A fixed canvas is resized too, when its\n * `width` or `height` changes.\n *\n * @default true\n */\n reduceFlickering?: boolean\n}\n\n/** A canvas of the size given in CSS pixels. This is the default. */\nexport interface AnimationCanvasFixedProps {\n /** Leave it out, or pass `false`, for a canvas of the size below. */\n resizable?: false\n /**\n * Width of the canvas in CSS pixels.\n * @default 100\n */\n width?: number\n /**\n * Height of the canvas in CSS pixels.\n * @default 100\n */\n height?: number\n}\n\n/**\n * A canvas that follows the size of its parent element, so the parent needs a\n * size of its own.\n */\nexport interface AnimationCanvasResizableProps {\n /**\n * Follow the size of the parent element instead of `width` and `height`.\n * Switching it rebuilds the canvas.\n */\n resizable: true\n /** Not accepted: the size comes from the parent element. */\n width?: never\n /** Not accepted: the size comes from the parent element. */\n height?: never\n}\n\nexport type AnimationCanvasProps = AnimationCanvasCommonProps &\n (AnimationCanvasFixedProps | AnimationCanvasResizableProps)\n\ntype Props = AnimationCanvasProps &\n Omit<\n ComponentPropsWithoutRef<'canvas'>,\n keyof AnimationCanvasCommonProps | 'resizable' | 'width' | 'height'\n >\n\n/**\n * A simple animatable canvas with requestAnimationFrame()\n */\nexport const AnimationCanvas = /* @__PURE__ */ forwardRef<\n HTMLCanvasElement,\n Props\n>(function AnimationCanvas(\n {\n // common\n draw,\n init,\n animate = true,\n options,\n reduceFlickering = true,\n // fixed\n width = 100,\n height = 100,\n // resizable\n resizable = false,\n // canvas props\n className,\n onContextMenu = (event) => event.preventDefault(),\n ...props\n },\n forwardedRef,\n) {\n // See useDrag for why the node is held in state rather than a ref: an inline\n // ref would be re-attached on every render and tear the instance down.\n const [node, setNode] = useState<HTMLCanvasElement | null>(null)\n // The caller's ref gets the same element.\n const composedRef = useComposedRefs<HTMLCanvasElement>(forwardedRef, setNode)\n\n // Read when the instance is created. The effect below keeps it current, and\n // runs right after, so a stale handler is replaced within the same commit.\n const latest = useRef({\n draw,\n init,\n animate,\n width,\n height,\n reduceFlickering,\n options,\n })\n const instanceRef = useRef<AnimationCanvasInstance | null>(null)\n\n // Runs before the creation effect below. This keeps a rebuilt instance on\n // the current render's size and handlers while updating an existing one in\n // place on ordinary renders.\n useEffect(() => {\n latest.current = {\n draw,\n init,\n animate,\n width,\n height,\n reduceFlickering,\n options,\n }\n instanceRef.current?.update({\n animate,\n size: { width, height },\n reduceFlickering,\n })\n })\n\n useEffect(() => {\n if (!node) return\n\n const current = latest.current\n const instance = createAnimationCanvas(node, {\n draw: (context, frame) => latest.current.draw(context, frame),\n init: (context, size) => latest.current.init?.(context, size),\n animate: current.animate,\n size: { width: current.width, height: current.height },\n reduceFlickering: current.reduceFlickering,\n resizable,\n contextAttributes: current.options,\n })\n instanceRef.current = instance\n\n return () => {\n instanceRef.current = null\n instance.destroy()\n }\n // Only `resizable` decides how the instance is wired, so it is the one\n // setting that rebuilds it. `options` is read from the ref above rather\n // than depended on: it is almost always written inline, and depending on\n // it would tear the canvas down on every render.\n }, [node, resizable])\n\n return (\n <canvas\n className={className}\n ref={composedRef}\n onContextMenu={onContextMenu}\n {...props}\n />\n )\n})\n","import { DependencyList, useCallback, useInsertionEffect, useRef } from 'react'\n\n/**\n * Internal\n * @private\n */\nexport function useCallbackRef<Args extends unknown[], Return>(\n callback: ((...args: Args) => Return) | undefined,\n deps: DependencyList = [],\n) {\n const callbackRef = useRef<typeof callback>(() => {\n throw new Error('Cannot call an event handler while rendering.')\n })\n\n useInsertionEffect(() => {\n callbackRef.current = callback\n })\n\n // `deps` comes from the caller, so it cannot be the array literal the\n // compiler rules expect.\n // oxlint-disable-next-line react-hooks/exhaustive-deps, react/use-memo\n return useCallback((...args: Args) => callbackRef.current?.(...args), deps)\n}\n","import { useEffect, useRef, useState } from 'react'\n\nimport {\n createDropZone,\n type DropZoneInstance,\n type DropZoneOptions,\n} from '@tremolo-ui/dom'\n\nimport { useCallbackRef } from './_internal/useCallbackRef'\n\n// `onStateChange` is left out: what it reports comes back from the hook as\n// `over` and `invalid`, which is what a component renders from.\nexport interface UseDropZoneOptions extends Omit<\n DropZoneOptions,\n 'onStateChange'\n> {}\n\n/**\n * Take files dropped onto an element.\n *\n * Any element can become a drop target this way — a waveform display, a track\n * lane, a canvas — without wrapping it in anything.\n *\n * @returns a ref callback for the element, and whether a drag is over it\n */\nexport function useDropZone<T extends Element>({\n accept,\n multiple,\n disabled,\n onDrop,\n onReject,\n}: UseDropZoneOptions = {}): {\n refCallback: (node: T | null) => void\n over: boolean\n invalid: boolean\n} {\n const dropHandler = useCallbackRef(onDrop)\n const rejectHandler = useCallbackRef(onReject)\n\n const [state, setState] = useState({ over: false, invalid: false })\n\n // See useDrag for why the node is held in state.\n const [node, setNode] = useState<T | null>(null)\n\n const instanceRef = useRef<DropZoneInstance | null>(null)\n\n // Read when the instance is created. The effect below keeps it current, and\n // runs right after, so a stale setting is replaced within the same commit.\n const latest = useRef({ accept, multiple, disabled })\n\n useEffect(() => {\n if (!node) return\n\n const instance = createDropZone(node, {\n ...latest.current,\n onDrop: (files, event) => dropHandler(files, event),\n onReject: (files, event) => rejectHandler(files, event),\n onStateChange: (next) => setState(next),\n })\n instanceRef.current = instance\n\n return () => {\n instanceRef.current = null\n instance.destroy()\n // The listeners are gone, so no `dragleave` is coming to clear this.\n setState({ over: false, invalid: false })\n }\n }, [node, dropHandler, rejectHandler])\n\n // Runs after every render: the settings come from props and are cheap to\n // push, and updating in place leaves a drag in progress untouched.\n useEffect(() => {\n latest.current = { accept, multiple, disabled }\n instanceRef.current?.update({ accept, multiple, disabled })\n })\n\n return { refCallback: setNode, over: state.over, invalid: state.invalid }\n}\n","import { ComponentPropsWithoutRef, forwardRef, ReactNode } from 'react'\n\nimport { useComposedRefs } from '../../compose-refs'\nimport { useDropZone } from '../../hooks/useDropZone'\n\nexport interface DropZoneProps {\n /**\n * Which files to take, written the way the `accept` attribute of a file\n * input is: a comma separated list of extensions (`.wav`), MIME types\n * (`audio/wav`) and type groups (`audio/*`).\n *\n * What does not match goes to `onReject` rather than `onDrop`. While the\n * drag is still in the air the element is marked `[data-invalid]`, as far as\n * the browser lets it be told apart — a rule written as an extension cannot\n * be decided until the drop.\n */\n accept?: string\n /**\n * Take more than one file from a single drop. With it off, only the first\n * accepted file is reported, as a file input without `multiple` does.\n *\n * @default false\n */\n multiple?: boolean\n /**\n * Refuse the drop. The drag is still swallowed rather than let through: an\n * unhandled drop makes the browser leave the page and open the file.\n *\n * @default false\n */\n disabled?: boolean\n\n /**\n * Called with the dropped files that match `accept`.\n *\n * @param event the drop, for anything the files leave out — where on the\n * element it landed, most of all.\n */\n onDrop?: (files: File[], event: DragEvent) => void\n /**\n * Called with the dropped files that do not match `accept`, so that the\n * reason can be shown. It comes after `onDrop` for the same drop, so a list\n * of rejected files can be cleared in `onDrop` and filled here.\n */\n onReject?: (files: File[], event: DragEvent) => void\n\n /** What the zone shows. */\n children: ReactNode\n}\n\ntype Props = DropZoneProps &\n Omit<ComponentPropsWithoutRef<'div'>, keyof DropZoneProps>\n\n/**\n * An area that takes files dropped onto it.\n *\n * To make an element you already have into a drop target, without wrapping it\n * in anything, use the `useDropZone` hook this is built on.\n */\nexport const Root = /* @__PURE__ */ forwardRef<HTMLDivElement, Props>(\n (\n {\n accept,\n multiple = false,\n disabled = false,\n onDrop,\n onReject,\n children,\n ...props\n },\n ref,\n ) => {\n const { refCallback, over, invalid } = useDropZone<HTMLDivElement>({\n accept,\n multiple,\n disabled,\n onDrop,\n onReject,\n })\n const composedRef = useComposedRefs<HTMLDivElement>(ref, refCallback)\n\n return (\n <div\n ref={composedRef}\n data-dragover={over ? '' : undefined}\n data-invalid={invalid ? '' : undefined}\n data-disabled={disabled ? '' : undefined}\n {...props}\n >\n {children}\n </div>\n )\n },\n)\n\n/**\n * An area that takes files dropped onto it.\n */\nexport const DropZone = {\n Root,\n}\n","import { createContext, useContext } from 'react'\n\nexport type FileInputContextValue = {\n /** The id of the hidden `<input type=\"file\">`, for `Trigger`'s `htmlFor`. */\n inputId: string\n disabled: boolean\n}\n\nconst FileInputContext =\n /* @__PURE__ */ createContext<FileInputContextValue | null>(null)\n\nexport const FileInputProvider = FileInputContext.Provider\n\nexport function useFileInputContext(): FileInputContextValue\nexport function useFileInputContext<T>(\n selector: (state: FileInputContextValue) => T,\n): T\nexport function useFileInputContext<T>(\n selector?: (state: FileInputContextValue) => T,\n) {\n const context = useContext(FileInputContext)\n if (!context) throw new Error('Missing FileInputContext.Provider in the tree')\n return selector ? selector(context) : context\n}\n","import { ComponentPropsWithoutRef, forwardRef, ReactNode } from 'react'\n\nimport { useFileInputContext } from './context'\n\nexport interface FileInputTriggerProps {\n /** What the trigger reads. It is the accessible name of the file input. */\n children: ReactNode\n}\n\ntype Props = FileInputTriggerProps &\n Omit<\n ComponentPropsWithoutRef<'label'>,\n keyof FileInputTriggerProps | 'htmlFor'\n >\n\n/**\n * Opens the file picker, and names the input for a screen reader.\n *\n * A `<label>` rather than a `<button>`: the browser forwards the click to the\n * input on its own, the input stays the one thing in the tab order, and its\n * accessible name comes from this text without an `aria-label` to keep in\n * step. A disabled input ignores the click, so nothing has to be intercepted.\n */\nexport const Trigger = /* @__PURE__ */ forwardRef<HTMLLabelElement, Props>(\n ({ children, ...props }, ref) => {\n const { inputId, disabled } = useFileInputContext()\n\n return (\n <label\n ref={ref}\n htmlFor={inputId}\n data-disabled={disabled ? '' : undefined}\n {...props}\n >\n {children}\n </label>\n )\n },\n)\n","import {\n ChangeEvent,\n ComponentPropsWithoutRef,\n forwardRef,\n ReactNode,\n useId,\n useMemo,\n} from 'react'\n\nimport {\n partitionByAccept,\n visuallyHiddenStyle,\n} from '@tremolo-ui/dom/internal'\n\nimport { FileInputProvider } from './context'\nimport { Trigger } from './Trigger'\n\nexport interface FileInputProps {\n /**\n * Which files to take, written the way the HTML attribute is: a comma\n * separated list of extensions (`.wav`), MIME types (`audio/wav`) and type\n * groups (`audio/*`).\n *\n * **The browser treats it as a hint to the picker, not a rule.** A person\n * can switch the picker to \"All Files\", so what arrives is checked again\n * here, and anything that does not match goes to `onReject` instead.\n */\n accept?: string\n /**\n * Let more than one file be picked at a time.\n * @default false\n */\n multiple?: boolean\n /**\n * Refuse to open the picker, and mark every part with `[data-disabled]`.\n * @default false\n */\n disabled?: boolean\n\n /**\n * Called with the files that were picked and match `accept`.\n *\n * It is not called at all when the picker is dismissed, or when every file\n * picked was rejected — an empty array never arrives, so there is no need to\n * tell \"nothing was chosen\" from \"the choice was cleared\".\n */\n onChange?: (files: File[]) => void\n /**\n * Called with the files that were picked and do not match `accept`, so that\n * the reason can be shown. Given alongside `onChange` when a selection held\n * both, and after it, so a list of rejected files can be cleared in\n * `onChange` and filled here.\n */\n onReject?: (files: File[]) => void\n\n /** Normally a `FileInput.Trigger`, and whatever shows what was picked. */\n children: ReactNode\n}\n\ntype Props = FileInputProps &\n Omit<ComponentPropsWithoutRef<'div'>, keyof FileInputProps>\n\n/**\n * Hands over the files a person picked.\n *\n * The `<input type=\"file\">` is here, kept out of sight but in the tab order,\n * because it is the control: it takes the focus, opens the picker on Enter,\n * and carries the accessible name. What is on screen is whatever the children\n * draw.\n */\nexport const Root = /* @__PURE__ */ forwardRef<HTMLDivElement, Props>(\n (\n {\n accept,\n multiple = false,\n disabled = false,\n onChange,\n onReject,\n children,\n // Pulled out of `props` so that they land on the input rather than on\n // the wrapper: it is the control, and a description is only announced\n // from the element that takes the focus. No declaration in\n // `FileInputProps` is needed for this — they are already on the div\n // attributes it is merged with, with the same types.\n 'aria-label': ariaLabel,\n 'aria-labelledby': ariaLabelledby,\n 'aria-describedby': ariaDescribedby,\n ...props\n },\n ref,\n ) => {\n const inputId = useId()\n\n const context = useMemo(() => ({ inputId, disabled }), [inputId, disabled])\n\n function handleChange(event: ChangeEvent<HTMLInputElement>) {\n const { accepted, rejected } = partitionByAccept(\n Array.from(event.target.files ?? []),\n accept,\n )\n\n // Picking the same file twice fires no second change event while the\n // value is still on the input, so it is cleared as soon as it is read.\n // Nothing is lost: the files are already in hand.\n event.target.value = ''\n\n if (accepted.length > 0) onChange?.(accepted)\n if (rejected.length > 0) onReject?.(rejected)\n }\n\n return (\n <FileInputProvider value={context}>\n <div ref={ref} data-disabled={disabled ? '' : undefined} {...props}>\n <input\n id={inputId}\n type=\"file\"\n accept={accept}\n multiple={multiple}\n disabled={disabled}\n style={visuallyHiddenStyle}\n aria-label={ariaLabel}\n aria-labelledby={ariaLabelledby}\n aria-describedby={ariaDescribedby}\n onChange={handleChange}\n />\n {children}\n </div>\n </FileInputProvider>\n )\n },\n)\n\n/**\n * Picks files, and hands them over as `File`s.\n */\nexport const FileInput = {\n Root,\n Trigger,\n}\n\nexport { useFileInputContext, type FileInputContextValue } from './context'\nexport { type FileInputTriggerProps } from './Trigger'\n","import {\n useCallback,\n useEffect,\n useInsertionEffect,\n useMemo,\n useRef,\n useState,\n} from 'react'\n\nimport {\n createChangeGesture,\n type ChangeGestureInstance,\n type ChangeSource,\n} from '@tremolo-ui/dom'\n\nexport interface ChangeGestureProps<V> {\n onChangeStart?: (value: V, source: ChangeSource) => void\n onChangeEnd?: (value: V, source: ChangeSource) => void\n changeEndDelay?: number\n}\n\nexport type ChangeGesture<V> = Pick<\n ChangeGestureInstance,\n 'hold' | 'pulse' | 'instant' | 'end' | 'active'\n> & {\n /**\n * Record a value the control has just reported through `onChange`, for\n * `onChangeEnd` to give. The prop lags behind until the parent renders, and\n * a drag ends in the same event as its last change.\n */\n changed: (value: V) => void\n}\n\n/**\n * The core's change gesture, reporting the control's value to\n * `onChangeStart` / `onChangeEnd`. The start gets the value before the change,\n * the end the last one reported.\n *\n * Internal\n * @private\n */\nexport function useChangeGesture<V>(\n value: V,\n { onChangeStart, onChangeEnd, changeEndDelay }: ChangeGestureProps<V>,\n inactive: boolean,\n): ChangeGesture<V> {\n const [instance] = useState(() => createChangeGesture())\n const latest = useRef({ value, onChangeStart, onChangeEnd })\n // What `onChangeEnd` gives: the value at the start, then each one reported.\n const lastValue = useRef(value)\n\n useInsertionEffect(() => {\n latest.current = { value, onChangeStart, onChangeEnd }\n })\n\n // The callbacks are handed over here rather than at creation: they read the\n // refs, which belong outside render. Nothing can start a gesture before the\n // element is mounted, so they are in place by the first event.\n useEffect(() => {\n instance.update({\n endDelay: changeEndDelay,\n onStart: (source) => {\n lastValue.current = latest.current.value\n latest.current.onChangeStart?.(latest.current.value, source)\n },\n onEnd: (source) =>\n latest.current.onChangeEnd?.(lastValue.current, source),\n })\n }, [instance, changeEndDelay])\n\n // A control that turns inactive mid-gesture, or goes away, should not leave\n // a host thinking it is still being touched.\n useEffect(() => {\n if (inactive) instance.end()\n }, [instance, inactive])\n useEffect(() => () => instance.end(), [instance])\n\n const changed = useCallback((next: V) => {\n lastValue.current = next\n }, [])\n\n return useMemo(\n () => ({\n hold: instance.hold,\n pulse: instance.pulse,\n instant: instance.instant,\n end: instance.end,\n active: instance.active,\n changed,\n }),\n [instance, changed],\n )\n}\n","import { useEffect } from 'react'\n\nimport { checkSteps, type CheckStepsOptions } from '@tremolo-ui/dom/internal'\n\n/**\n * Warn, in development only, when a key press or a wheel notch cannot produce\n * a change the user can see. The probing is `checkSteps` in the core.\n *\n * @internal\n */\nexport function useCheckSteps(options: CheckStepsOptions) {\n const { component, axis, range, keyboard, wheel } = options\n const { min, max, step, scale } = range ?? {}\n\n useEffect(() => {\n try {\n // Inline and first, so a bundler folds the comparison and drops the\n // whole block — and with it the only call to `checkSteps`, which then\n // falls out of the bundle too. See `useCheckPlacement` for why it\n // cannot go through a helper, and why it is wrapped.\n if (process.env.NODE_ENV === 'production') return\n for (const warning of checkSteps(options)) console.warn(warning)\n } catch {\n // No bundler substituted NODE_ENV, so a production build cannot be told\n // from a development one. Say nothing rather than break.\n }\n // `range` and `format` are rebuilt on most renders, so the effect is keyed\n // on what actually decides the outcome. `format` is left out on purpose:\n // an inline arrow function would make this run every render.\n // oxlint-disable-next-line react-hooks/exhaustive-deps\n }, [component, axis, min, max, step, scale, keyboard, wheel])\n}\n","import { RefObject, useEffect, useRef, useState } from 'react'\n\nimport {\n createDragValue,\n elementMapping,\n relativeMapping,\n type AxisOptions,\n type DragState,\n type DragValueInstance,\n type XY,\n type XYInput,\n} from '@tremolo-ui/dom'\n\nimport { useCallbackRef } from './_internal/useCallbackRef'\n\nexport interface UseDragValueOptions {\n /** Scaling of each axis; a single value applies to both. */\n axis: XYInput<AxisOptions>\n\n /**\n * Normalize the pointer against the bounding rect of this element, so that\n * the value *is* the position pointed at.\n *\n * Give either this or `getValue`.\n */\n baseElementRef?: RefObject<Element | null>\n\n /**\n * Move the value away from where it stood when the drag started, by the\n * distance dragged. The pointer position itself carries no meaning.\n *\n * Give either this or `baseElementRef`.\n */\n getValue?: () => XY<number>\n /**\n * Pixels of movement that span the whole range, with `getValue`.\n * @default 100\n */\n pixelRange?: XYInput<number>\n /**\n * How much the movement counts, read on every move. `0.1` makes the same\n * movement cover a tenth of the range, which is what a fine-adjustment\n * modifier wants.\n *\n * With `baseElementRef` the value is normally the position pointed at, so\n * anything but `1` turns the mapping relative and leaves the pointer and the\n * value apart for the rest of the drag.\n *\n * @see relativeMapping\n * @see elementMapping\n */\n sensitivity?: (state: DragState) => number\n\n /**\n * Move the value on pointerdown, before the pointer has moved at all. With\n * `baseElementRef` that makes a press jump the value to where it landed.\n *\n * @see DragValueOptions.updateOnPointerDown\n */\n updateOnPointerDown?: boolean\n /**\n * Pixels the pointer has to travel before the drag counts as one. The\n * default is 0, so the value follows the very first move.\n *\n * @default 0\n */\n threshold?: number\n /** CSS cursor to show while dragging. Applied to the element itself. */\n cursor?: string\n /**\n * Hide the pointer and read its movement directly, instead of following it\n * around the screen. Only with `getValue`: there is no pointer position to\n * normalize against a `baseElementRef` while the pointer is locked.\n *\n * @see DragOptions.pointerLock\n * @default false\n */\n pointerLock?: boolean\n /**\n * Decide whether a pointerdown starts a drag at all, before the pointer is\n * captured.\n *\n * @see DragOptions.shouldStart\n */\n shouldStart?: (event: PointerEvent) => boolean\n\n /**\n * Called with the new value whenever the drag moves it. The hook holds no\n * value of its own, so this is where it is stored.\n *\n * @param state the whole drag, for anything the value leaves out — the\n * pointer event and its modifier keys, most of all.\n */\n onChange?: (value: XY<number>, state: DragState) => void\n /** Called once the drag starts, with the value it starts from. */\n onDragStart?: (value: XY<number>, state: DragState) => void\n /** Called when the pointer is released, with the value it ends on. */\n onDragEnd?: (value: XY<number>, state: DragState) => void\n}\n\n/**\n * Drive a value with a pointer drag.\n *\n * @returns a ref callback for the element that starts the drag, and whether a drag is in progress\n */\nexport function useDragValue<T extends Element>(\n options: UseDragValueOptions,\n): { refCallback: (node: T | null) => void; dragging: boolean } {\n const {\n axis,\n baseElementRef,\n pixelRange,\n updateOnPointerDown,\n threshold,\n cursor,\n pointerLock,\n } = options\n\n if (!baseElementRef && !options.getValue) {\n throw new Error(\n 'useDragValue: give either baseElementRef or getValue, so that the drag has something to move',\n )\n }\n\n const [dragging, setDragging] = useState(false)\n\n const valueGetter = useCallbackRef(options.getValue)\n const changeHandler = useCallbackRef(options.onChange)\n const dragStartHandler = useCallbackRef(options.onDragStart)\n const dragEndHandler = useCallbackRef(options.onDragEnd)\n\n // Read when the instance is created. The effect below keeps it current, and\n // runs right after, so a stale setting is replaced within the same commit.\n const latest = useRef(options)\n\n const instanceRef = useRef<DragValueInstance | null>(null)\n\n // See useDrag for why the node is held in state.\n const [node, setNode] = useState<T | null>(null)\n\n const [pixelRangeX, pixelRangeY] = Array.isArray(pixelRange)\n ? pixelRange\n : [pixelRange, pixelRange]\n\n useEffect(() => {\n if (!node) return\n\n const instance = createDragValue(node, {\n axis: latest.current.axis,\n mapping: baseElementRef\n ? elementMapping(() => baseElementRef.current, {\n // Read through the ref so that a changed setting reaches a drag\n // already in progress.\n sensitivity: (state) => latest.current.sensitivity?.(state) ?? 1,\n })\n : relativeMapping({\n pixelRange: [pixelRangeX ?? 100, pixelRangeY ?? 100],\n // Read through the ref so that a changed setting reaches a drag\n // already in progress.\n sensitivity: (state) => latest.current.sensitivity?.(state) ?? 1,\n }),\n getValue: () => valueGetter() ?? [0, 0],\n updateOnPointerDown: latest.current.updateOnPointerDown,\n threshold: latest.current.threshold,\n cursor: latest.current.cursor,\n pointerLock: latest.current.pointerLock,\n shouldStart: (event) => latest.current.shouldStart?.(event) ?? true,\n onChange: (value, state) => changeHandler(value, state),\n onDragStart: (value, state) => {\n setDragging(true)\n dragStartHandler(value, state)\n },\n onDragEnd: (value, state) => {\n setDragging(false)\n dragEndHandler(value, state)\n },\n })\n instanceRef.current = instance\n\n return () => {\n instanceRef.current = null\n instance.destroy()\n }\n }, [\n node,\n baseElementRef,\n pixelRangeX,\n pixelRangeY,\n valueGetter,\n changeHandler,\n dragStartHandler,\n dragEndHandler,\n ])\n\n // Runs after every render: the settings come from props and are cheap to\n // push, and updating in place leaves a drag in progress untouched.\n useEffect(() => {\n latest.current = options\n instanceRef.current?.update({\n axis,\n updateOnPointerDown,\n threshold,\n cursor,\n pointerLock,\n })\n })\n\n return { refCallback: setNode, dragging }\n}\n","import { RefObject, useEffect, useState } from 'react'\n\nimport { createWheel, type WheelOptions } from '@tremolo-ui/dom'\n\nimport { useCallbackRef } from './_internal/useCallbackRef'\n\n// `onWheel` is left out: this hook takes the handler as its first argument, so\n// the option would be a second way to say the same thing.\nexport interface UseWheelOptions extends Omit<WheelOptions, 'onWheel'> {\n /**\n * Listen on this element rather than on the one the returned ref callback is\n * attached to, and ignore that callback.\n *\n * For a control made of several movable parts: a wheel event only reaches\n * what the cursor is over, so a listener per part responds only while the\n * cursor is over that same part. Listening on the element they share lets\n * every part see the event, and each one decide whether it is the one to act.\n *\n * The element is read when the listener is attached, so a ref filled in by a\n * parent is fine as long as the parent is above in the tree.\n */\n target?: RefObject<Element | null>\n}\n\n/**\n * Listen to wheel events on an element.\n *\n * The listener is not passive, so the handler may call `preventDefault()`.\n *\n * @returns a ref callback to attach to the element, unused when `target` is given\n */\nexport function useWheel<T extends Element>(\n onWheel: (event: WheelEvent) => void,\n { requireFocus = false, target }: UseWheelOptions = {},\n): (node: T | null) => void {\n const wheelHandler = useCallbackRef(onWheel)\n\n // See useDrag for why the node is held in state.\n const [node, setNode] = useState<T | null>(null)\n\n useEffect(() => {\n const element = target ? target.current : node\n if (!element) return\n\n const instance = createWheel(element, {\n onWheel: (event) => wheelHandler(event),\n requireFocus,\n })\n\n return () => instance.destroy()\n }, [node, target, wheelHandler, requireFocus])\n\n return setNode\n}\n","import { createContext, ReactNode, useContext, useEffect } from 'react'\n\n/**\n * The subcomponent that established the current placement, if any.\n *\n * @internal\n */\nconst PlacementContext = /* @__PURE__ */ createContext<string | null>(null)\n\n/**\n * Marks its children as being inside `name`, for {@link useCheckPlacement}.\n *\n * @internal\n */\nexport function Placement({\n name,\n children,\n}: {\n name: string\n children: ReactNode\n}) {\n return (\n <PlacementContext.Provider value={name}>\n {children}\n </PlacementContext.Provider>\n )\n}\n\n/**\n * Warn, in development only, when `child` is not rendered inside `parent`.\n *\n * A compound component renders its children exactly as they are composed, so a\n * subcomponent put at the wrong level still renders — it only loses whatever\n * its layout depended on, and does so silently. `Slider.Thumb` outside\n * `Slider.Track` keeps its `position: absolute` but resolves it against the\n * page rather than the track, and neither the build nor the tests notice.\n *\n * @internal\n */\nexport function useCheckPlacement(child: string, parent: string) {\n const found = useContext(PlacementContext)\n\n useEffect(() => {\n try {\n // `process.env.NODE_ENV` is written out here, inline and first, so that a\n // bundler substituting it folds the comparison and drops this whole\n // block — the message strings with it. Reading the flag through a helper\n // or a module constant leaves the branch in the bundle instead: neither\n // folds, which was measured with esbuild rather than assumed.\n //\n // The read is wrapped because `process` may not exist at all. The\n // package is built with `platform: 'neutral'`, so a page loading the ESM\n // straight from a CDN has no bundler to substitute it, and a bare\n // reference would throw a `ReferenceError` mid-effect.\n if (process.env.NODE_ENV !== 'production' && found !== parent) {\n console.warn(\n `[tremolo-ui] ${child} has to be rendered inside ${parent}` +\n (found === null ? '.' : `, but it is inside ${found}.`) +\n ' It renders either way, so nothing fails — its position just' +\n ' comes out wrong.',\n )\n }\n } catch {\n // No bundler substituted NODE_ENV, so there is no way to tell a\n // production build from a development one. Say nothing rather than break.\n }\n }, [found, child, parent])\n}\n","import { createContext, useContext } from 'react'\n\nimport { type KnobAngles } from '@tremolo-ui/dom'\nimport { type Scale } from '@tremolo-ui/functions'\n\nexport type KnobConfig = {\n value: number\n min: number\n max: number\n step: number\n scale: Scale\n startValue: number\n /** angle range [degree] */\n angleRange: number\n}\n\nexport type KnobContextValue = KnobConfig & KnobAngles\n\nconst KnobContext = /* @__PURE__ */ createContext<KnobContextValue | null>(null)\n\nexport const KnobProvider = KnobContext.Provider\n\n/**\n * Everything here is derived during render, so there is no state to keep in\n * sync: `value` comes from the props of `Root` and the rest follows from it.\n */\nexport function useKnobContext(): KnobContextValue\nexport function useKnobContext<T>(selector: (state: KnobContextValue) => T): T\nexport function useKnobContext<T>(selector?: (state: KnobContextValue) => T) {\n const context = useContext(KnobContext)\n if (!context) throw new Error('Missing KnobContext.Provider in the tree')\n return selector ? selector(context) : context\n}\n","import { SVGProps } from 'react'\n\nimport { knobArcPath, knobArcRadius } from '@tremolo-ui/dom'\n\nimport { useCheckPlacement } from '../_util/Placement'\n\nimport { useKnobContext } from './context'\n\nexport function ActiveLine({\n stroke = 'currentColor',\n strokeWidth = 6,\n className,\n ...props\n}: Omit<SVGProps<SVGPathElement>, 'd'>) {\n useCheckPlacement('Knob.ActiveLine', 'Knob.SVGRoot')\n\n const r2 = useKnobContext((s) => s.r2)\n const r3 = useKnobContext((s) => s.r3)\n\n const radius = knobArcRadius(strokeWidth)\n\n return (\n <path\n className={className}\n d={knobArcPath(r2, r3, radius)}\n fill=\"none\"\n stroke={stroke}\n strokeWidth={strokeWidth}\n {...props}\n />\n )\n}\n","import { SVGProps } from 'react'\n\nimport { knobArcPath, knobArcRadius } from '@tremolo-ui/dom'\n\nimport { useCheckPlacement } from '../_util/Placement'\n\nimport { useKnobContext } from './context'\n\nexport function InactiveLine({\n stroke = 'currentColor',\n strokeWidth = 6,\n className,\n ...props\n}: Omit<SVGProps<SVGPathElement>, 'd'>) {\n useCheckPlacement('Knob.InactiveLine', 'Knob.SVGRoot')\n\n const min = useKnobContext((s) => s.min)\n const max = useKnobContext((s) => s.max)\n const startValue = useKnobContext((s) => s.startValue)\n\n const r1 = useKnobContext((s) => s.r1)\n const r2 = useKnobContext((s) => s.r2)\n const r3 = useKnobContext((s) => s.r3)\n const r4 = useKnobContext((s) => s.r4)\n\n const radius = knobArcRadius(strokeWidth)\n\n return (\n <>\n {startValue > min && (\n <path\n className={className}\n d={knobArcPath(r1, r2, radius)}\n fill=\"none\"\n stroke={stroke}\n strokeWidth={strokeWidth}\n {...props}\n />\n )}\n {startValue < max && (\n <path\n className={className}\n d={knobArcPath(r3, r4, radius)}\n fill=\"none\"\n stroke={stroke}\n strokeWidth={strokeWidth}\n {...props}\n />\n )}\n </>\n )\n}\n","import { ReactNode, SVGProps } from 'react'\n\nimport { KNOB_VIEWBOX_SIZE } from '@tremolo-ui/dom'\n\nimport { Placement } from '../_util/Placement'\n\nexport interface KnobSVGRootProps {\n /**\n * `<Knob.InactiveLine />`, `<Knob.ActiveLine />` and `<Knob.Thumb />` go\n * here, in the order you want them painted.\n */\n children: ReactNode\n}\n\nexport function SVGRoot({\n children,\n style,\n ...props\n}: KnobSVGRootProps & Omit<SVGProps<SVGSVGElement>, keyof KnobSVGRootProps>) {\n return (\n <svg\n viewBox={`0 0 ${KNOB_VIEWBOX_SIZE} ${KNOB_VIEWBOX_SIZE}`}\n style={{\n display: 'block',\n ...style,\n }}\n {...props}\n >\n <Placement name=\"Knob.SVGRoot\">{children}</Placement>\n </svg>\n )\n}\n","import { SVGProps } from 'react'\n\nimport { KNOB_VIEWBOX_SIZE } from '@tremolo-ui/dom'\nimport { clamp } from '@tremolo-ui/functions'\n\nimport { useCheckPlacement } from '../_util/Placement'\n\nimport { useKnobContext } from './context'\n\nexport interface KnobThumbProps {\n /**\n * Fill colour of the circle.\n * @default 'currentColor'\n */\n color?: string\n /**\n * Colour of the line that points at the value.\n * @default 'currentColor'\n */\n lineColor?: string\n /**\n * Diameter of the circle, as a percentage of the knob.\n * @default 84\n */\n size?: number\n /**\n * Thickness of the line, as a percentage of the knob.\n * @default 6\n */\n lineWeight?: number\n /**\n * How far down the line reaches, as a percentage of the knob from its top.\n * The line starts at the edge of the circle.\n * @default 35\n */\n lineLength?: number\n\n /**\n * Classes for what the thumb draws inside itself: `line` is the line that\n * points at the value.\n */\n classes?: {\n line?: string\n }\n}\n\nexport function Thumb({\n className,\n\n color = 'currentColor',\n lineColor = 'currentColor',\n size = 84,\n lineWeight = 6,\n lineLength = 35,\n classes,\n\n ...props\n}: KnobThumbProps & Omit<SVGProps<SVGSVGElement>, 'd' | keyof KnobThumbProps>) {\n useCheckPlacement('Knob.Thumb', 'Knob.SVGRoot')\n\n const angleRange = useKnobContext((s) => s.angleRange)\n\n const p = useKnobContext((s) => s.p)\n const r1 = useKnobContext((s) => s.r1)\n\n return (\n <svg className={className} {...props}>\n <circle cx=\"50%\" cy=\"50%\" r={`${size / 2}%`} fill={color} />\n <line\n className={classes?.line}\n x1=\"50%\"\n y1={`${(KNOB_VIEWBOX_SIZE - clamp(size, 0, 100)) / 2}%`}\n x2=\"50%\"\n y2={`${lineLength}%`}\n stroke={lineColor}\n strokeWidth={`${lineWeight}%`}\n // NOTE\n // https://bugs.webkit.org/show_bug.cgi?id=201854\n // https://developer.mozilla.org/en-US/docs/Web/SVG/Reference/Attribute/transform-origin#browser_compatibility\n style={{\n transform: `rotate(${r1 + p * angleRange}deg)`,\n transformOrigin: '50% 50%',\n }}\n />\n </svg>\n )\n}\n","import {\n ComponentPropsWithoutRef,\n CSSProperties,\n forwardRef,\n ReactNode,\n Ref,\n useCallback,\n useImperativeHandle,\n useMemo,\n useRef,\n} from 'react'\n\nimport {\n type AxisOptions,\n type ChangeSource,\n DEFAULT_DRAG_SENSITIVITY,\n DEFAULT_KEYBOARD_OPTIONS,\n DEFAULT_WHEEL_OPTIONS,\n type InputEventOption,\n knobAngles,\n type ModifierValue,\n type XY,\n} from '@tremolo-ui/dom'\nimport {\n applyDelta,\n arrowKeyDirection,\n cssLength,\n selectModifier,\n wheelDirection,\n} from '@tremolo-ui/dom/internal'\nimport { linearScale, type Scale, type ValueRange } from '@tremolo-ui/functions'\n\nimport { useComposedRefs } from '../../compose-refs'\nimport { useChangeGesture } from '../../hooks/_internal/useChangeGesture'\nimport { useCheckSteps } from '../../hooks/_internal/useCheckSteps'\nimport { useDragValue } from '../../hooks/useDragValue'\nimport { useWheel } from '../../hooks/useWheel'\n\nimport { ActiveLine } from './ActiveLine'\nimport { KnobProvider } from './context'\nimport { InactiveLine } from './InactiveLine'\nimport { SVGRoot } from './SVGRoot'\nimport { Thumb } from './Thumb'\n\nimport type { CSSVariables } from '../../css-variables'\n\nexport interface KnobProps {\n /** The current value. The knob shows only this, so update it from `onChange`. */\n value: number\n /** The value with the knob turned all the way down. */\n min: number\n /** The value with the knob turned all the way up. */\n max: number\n\n /**\n * Granularity of the value. A drag, the wheel and the arrow keys snap it to\n * multiples of `step`.\n *\n * @default 1\n */\n step?: number\n /**\n * How the value is distributed across the travel.\n *\n * Pick one of the scales from `@tremolo-ui/functions`: `linearScale`,\n * `exponentialScale`, `curveScale(n)`, `symmetricSkewScale(n)`, or\n * `skewScale(n)` for a value that has to match a JUCE parameter.\n *\n * @default linearScale\n */\n scale?: Scale\n /**\n * The value a double click restores. `null` turns the double click off.\n * @default startValue\n */\n resetValue?: number | null\n\n /**\n * Where the active arc starts. Put it at the centre of a bipolar control,\n * such as a pan knob, so that the arc grows from there either way.\n * @default min\n */\n startValue?: number\n\n /**\n * Width and height of the knob. Sets `--knob-size`; the size the theme\n * gives it stands when this is omitted.\n */\n size?: number | string\n\n /**\n * The cursor to show while dragging. It is set on the dragged element, so it\n * stays while the pointer is outside the knob.\n *\n * @default 'grabbing'\n */\n dragCursor?: CSSProperties['cursor']\n /**\n * How much one notch of the wheel moves the value. It only acts while the\n * focus is inside, so that scrolling the page past the knob leaves it alone.\n *\n * `['raw', n]` moves the value by `n`, and `['normalized', n]` by `n` of the\n * range between `min` and `max`. The result is snapped to `step`, except for\n * an amount set on a modifier key (`{ default: …, shift: … }`). `null` turns\n * the wheel off.\n *\n * @default ['raw', 1]\n */\n wheel?: ModifierValue<InputEventOption> | null\n /**\n * How much a drag moves the value, per modifier key.\n *\n * `1` is the normal travel of 100px for the whole range; `0.1` makes the\n * same movement cover a tenth of it. Shift is bound to `0.1` by default, to\n * match what it does on the arrow keys.\n *\n * Pressing or releasing the key mid-drag does not disturb the value: the\n * travel so far is kept and the new sensitivity applies from there. It takes\n * effect on the next movement, since a key on its own produces no pointer\n * event.\n *\n * @default { default: 1, shift: 0.1 }\n */\n dragSensitivity?: ModifierValue<number>\n\n /**\n * Hide the cursor while dragging and read the pointer movement directly,\n * rather than letting it wander off across the screen.\n *\n * A knob does not care where the pointer is, only how far it moved, and\n * letting it wander costs twice: the cursor ends up far from the knob it is\n * holding, and **the drag stops at the edge of the screen**, where the\n * operating system pins the pointer and the coordinates stop changing. A\n * `dragSensitivity` below 1 reaches that edge quickly.\n *\n * Off by default because it is not free: the browser shows its own notice,\n * Esc takes the lock back, and the request needs a user gesture and can be\n * refused. A refused request is not an error — the drag simply carries on as\n * an ordinary one.\n *\n * @default false\n */\n pointerLock?: boolean\n\n /**\n * How much one arrow key press moves the value.\n *\n * `['raw', n]` moves the value by `n`, and `['normalized', n]` by `n` of the\n * range between `min` and `max`. The result is snapped to `step`, except for\n * an amount set on a modifier key, which is what lets shift move off the\n * grid. `null` turns the arrow keys off.\n *\n * The default moves by 1, and by 0.1 with shift. With a `step` above 1, raise\n * the amount to match: 1 would round straight back to where it started, and\n * a development build warns about it.\n *\n * @default { default: ['raw', 1], shift: ['raw', 0.1] }\n */\n keyboard?: ModifierValue<InputEventOption> | null\n\n /**\n * Make the knob unchangeable and remove it from the tab order.\n * The parts carry `data-disabled` while it is set.\n */\n disabled?: boolean\n /**\n * Make the knob unchangeable while leaving it focusable.\n * The parts carry `data-readonly` while it is set.\n */\n readOnly?: boolean\n\n /**\n * How far the knob turns from `min` to `max`, in degrees, centred on the\n * top.\n * @default 270\n */\n angleRange?: number\n\n /**\n * Called with the new value when a drag, the wheel, an arrow key or a double\n * click moves it.\n */\n onChange?: (value: number) => void\n /**\n * Called when a change of the value starts — a drag, a double click, the first wheel notch\n * or arrow key — with the value\n * before it and what it is made with. A host recording automation can treat\n * the control as touched from here until `onChangeEnd`.\n */\n onChangeStart?: (value: number, source: ChangeSource) => void\n /**\n * Called when the change ends, with the value it ended on: on release, or\n * `changeEndDelay` after the last wheel notch or arrow key.\n */\n onChangeEnd?: (value: number, source: ChangeSource) => void\n /**\n * How long after the last wheel notch or arrow key the change counts as\n * over, in milliseconds. Neither has an event that says it is done.\n *\n * @default 500\n */\n changeEndDelay?: number\n\n /**\n * The knob renders exactly what you compose here; there is no default\n * markup to fall back to.\n *\n * @example\n * <Knob.Root value={value} min={0} max={100} onChange={setValue}>\n * <Knob.SVGRoot>\n * <Knob.InactiveLine />\n * <Knob.ActiveLine />\n * <Knob.Thumb />\n * </Knob.SVGRoot>\n * </Knob.Root>\n */\n children: ReactNode\n\n style?: CSSProperties & CSSVariables<'knob-size'>\n\n /**\n * Receives `focus` and `blur`. They do nothing while the knob is disabled,\n * where the element's own `focus()` would still focus it. `ref` reaches the\n * root element itself.\n */\n actionsRef?: Ref<KnobMethods>\n}\n\nexport interface KnobMethods {\n focus: () => void\n blur: () => void\n}\n\ntype Props = KnobProps & Omit<ComponentPropsWithoutRef<'div'>, keyof KnobProps>\n\n/**\n * The wheel only acts while the focus is inside, so that scrolling a page past\n * the control does not change its value.\n */\nconst WHEEL_OPTIONS = { requireFocus: true }\n\nexport const Root = /* @__PURE__ */ forwardRef<HTMLDivElement, Props>(\n (\n {\n value,\n min,\n max,\n step = 1,\n scale = linearScale,\n resetValue,\n startValue = min,\n size,\n dragCursor = 'grabbing',\n wheel = DEFAULT_WHEEL_OPTIONS,\n keyboard = DEFAULT_KEYBOARD_OPTIONS,\n dragSensitivity = DEFAULT_DRAG_SENSITIVITY,\n pointerLock = false,\n disabled = false,\n readOnly = false,\n angleRange = 270,\n onChange,\n onChangeStart,\n onChangeEnd,\n changeEndDelay,\n onKeyDown,\n onPointerDown,\n onDoubleClick,\n className,\n style,\n children,\n actionsRef,\n ...props\n }: Props,\n forwardedRef,\n ) => {\n const elmRef = useRef<HTMLDivElement>(null)\n\n const inactive = disabled || readOnly\n\n // --- internal functions ---\n const range: ValueRange = useMemo(\n () => ({ min, max, step, scale }),\n [min, max, step, scale],\n )\n\n useCheckSteps({ component: 'Knob', range, keyboard, wheel })\n\n const gesture = useChangeGesture(\n value,\n { onChangeStart, onChangeEnd, changeEndDelay },\n inactive,\n )\n const change = useCallback(\n (next: number) => {\n gesture.changed(next)\n onChange?.(next)\n },\n [gesture, onChange],\n )\n\n const handleKeyDown = useCallback(\n (event: React.KeyboardEvent<HTMLOrSVGElement>) => {\n if (!keyboard || !onChange || inactive) return\n const direction = arrowKeyDirection(event.key)\n if (direction === null) return\n event.preventDefault()\n gesture.pulse('keyboard')\n change(applyDelta(value, direction, keyboard, range, event))\n },\n [keyboard, onChange, inactive, value, range, gesture, change],\n )\n\n // --- hooks ---\n // The knob has no travel of its own: the value moves away from where it\n // stood when the drag started, 100px of movement spanning the whole range.\n // Only the vertical axis carries a value, reversed so that dragging up\n // raises it.\n const axis = useMemo(\n (): XY<AxisOptions> => [range, { ...range, reverse: true }],\n [range],\n )\n\n const { refCallback: dragRefCallback, dragging } = useDragValue<\n HTMLElement | SVGElement\n >({\n axis,\n getValue: () => [value, value],\n sensitivity: (state) =>\n selectModifier(dragSensitivity, state.event).value,\n threshold: 1,\n cursor: inactive ? undefined : dragCursor,\n pointerLock: inactive ? false : pointerLock,\n shouldStart: () => !inactive,\n onChange: (v) => {\n if (inactive) return\n change(v[1])\n },\n onDragStart: () => {\n if (!inactive) gesture.hold('pointer')\n },\n onDragEnd: () => gesture.end(),\n })\n\n const wheelRefCallback = useWheel<HTMLElement>((event) => {\n if (!wheel || inactive) return\n // A notch the knob does not read — a sideways scroll — is left to the\n // page rather than swallowed.\n const direction = wheelDirection(event)\n if (direction === null) return\n event.preventDefault()\n if (!onChange) return\n gesture.pulse('wheel')\n change(applyDelta(value, direction, wheel, range, event))\n }, WHEEL_OPTIONS)\n\n // Composed once, so React attaches the refs a single time instead of\n // detaching and re-attaching on every render.\n const rootRefCallback = useComposedRefs<HTMLDivElement>(\n forwardedRef,\n elmRef,\n dragRefCallback,\n wheelRefCallback,\n )\n\n const context = useMemo(() => {\n const config = { value, min, max, step, scale, startValue, angleRange }\n return { ...config, ...knobAngles(config) }\n }, [value, min, max, step, scale, startValue, angleRange])\n\n useImperativeHandle(actionsRef, () => {\n return {\n focus() {\n if (!disabled) elmRef.current?.focus()\n },\n blur() {\n elmRef.current?.blur()\n },\n }\n }, [disabled])\n\n return (\n <KnobProvider value={context}>\n <div\n ref={rootRefCallback}\n className={className}\n tabIndex={disabled ? -1 : 0}\n role=\"slider\"\n aria-valuenow={value}\n aria-valuemin={min}\n aria-valuemax={max}\n aria-disabled={disabled}\n aria-readonly={readOnly}\n data-disabled={disabled ? '' : undefined}\n data-readonly={readOnly ? '' : undefined}\n data-dragging={dragging ? '' : undefined}\n style={\n {\n '--knob-size': cssLength(size),\n ...style,\n } as CSSProperties\n }\n onPointerDown={onPointerDown}\n onDoubleClick={(event) => {\n if (!inactive && resetValue !== null && onChange) {\n gesture.instant('doubleClick', () =>\n change(resetValue ?? startValue),\n )\n }\n onDoubleClick?.(event)\n }}\n onKeyDown={(event) => {\n handleKeyDown(event)\n onKeyDown?.(event)\n }}\n {...props}\n >\n {children}\n </div>\n </KnobProvider>\n )\n },\n)\n\n/**\n * Interactive rotary knob component implemented in SVG.\n */\nexport const Knob = {\n Root,\n SVGRoot,\n InactiveLine,\n ActiveLine,\n Thumb,\n}\n\nexport { useKnobContext, type KnobContextValue } from './context'\nexport { type KnobSVGRootProps } from './SVGRoot'\nexport { type KnobThumbProps } from './Thumb'\n","import { createContext, CSSProperties, RefObject, useContext } from 'react'\n\nimport {\n type InputEventOption,\n type ModifierState,\n type ModifierValue,\n} from '@tremolo-ui/dom'\nimport { type Scale, type ValueRange } from '@tremolo-ui/functions'\n\nexport type NumberInputContextValue = {\n value: number\n\n /**\n * The range the caller asked for. Left undefined when unbounded, which is\n * what `aria-valuemin` / `aria-valuemax` and the steppers need to tell apart\n * from a range that happens to sit at the edge of the safe integers.\n */\n min?: number\n max?: number\n step: number\n /** How the value is distributed across the travel. */\n scale: Scale\n\n disabled: boolean\n readOnly: boolean\n clampValue: boolean\n\n /**\n * The effective scaling for `applyDelta`, with unbounded ends filled in.\n * Ignores `min` / `max` when `clampValue` is off.\n */\n range: ValueRange\n /** The effective range for raw input, whose open ends cover finite numbers. */\n rawRange: ValueRange\n\n keyboard: ModifierValue<InputEventOption> | null\n /** Pixels of vertical drag on `Stepper` that move the value by one `step`. */\n drag: number | null\n /** How much a `Stepper` drag counts, per modifier key. */\n dragSensitivity: ModifierValue<number>\n /** Whether a `Stepper` drag hides the pointer and reads its movement. */\n pointerLock: boolean\n /** The cursor shown while a `Stepper` is dragged. */\n dragCursor: CSSProperties['cursor']\n\n /** What `InputField` selects when it takes focus. */\n selectOnFocus: 'all' | 'number' | 'none'\n /** Whether `InputField` shows the plain value while it has focus. */\n unformatOnFocus: boolean\n /** Whether `InputField` puts the caret back after an arrow key steps the value. */\n keepCaretOnStep: boolean\n /** Whether Enter leaves `InputField` as well as committing. */\n blurOnEnter: boolean\n\n /** What the input shows: the draft while editing, the formatted value otherwise. */\n text: string\n editing: boolean\n /** Whether the value sits outside `min` / `max`. Not judged while editing. */\n outOfRange: boolean\n /** Whether a stepper would have no effect, for `aria-disabled`. */\n atMin: boolean\n atMax: boolean\n\n format: (value: number) => string\n parse: (text: string) => number\n\n /** Typing. Replaces the draft and reports the parsed value, unclamped. */\n setDraft: (text: string) => void\n /** Blur or Enter. Optionally clamps the draft, reports it, and drops it. */\n commitDraft: () => void\n /** Drops the draft and replaces the value: steppers, wheel, keyboard, drag. */\n changeValue: (next: number) => void\n /** Moves the value by one `option` in `direction`, normally +1 or -1. */\n nudge: (\n direction: number,\n option: ModifierValue<InputEventOption>,\n modifiers?: ModifierState,\n ) => void\n\n /** `InputField` registers itself here; `Root` focuses it through its methods. */\n inputRef: RefObject<HTMLInputElement | null>\n}\n\nconst NumberInputContext =\n /* @__PURE__ */ createContext<NumberInputContextValue | null>(null)\n\nexport const NumberInputProvider = NumberInputContext.Provider\n\n/**\n * The only state behind this is the editing draft; `value` comes from the props\n * of `Root` and everything else is derived during render.\n */\nexport function useNumberInputContext(): NumberInputContextValue\nexport function useNumberInputContext<T>(\n selector: (state: NumberInputContextValue) => T,\n): T\nexport function useNumberInputContext<T>(\n selector?: (state: NumberInputContextValue) => T,\n) {\n const context = useContext(NumberInputContext)\n if (!context)\n throw new Error('Missing NumberInputContext.Provider in the tree')\n return selector ? selector(context) : context\n}\n\n/**\n * The change gesture of `Root`, for the parts that change the value. Not on\n * the public context: it is how the parts report, not something to read.\n */\nexport type NumberInputGestureValue = {\n /** A press on a stepper, held until the pointer is released anywhere. */\n press: () => void\n /** An arrow key in `InputField`. */\n key: () => void\n}\n\nconst NumberInputGestureContext =\n /* @__PURE__ */ createContext<NumberInputGestureValue | null>(null)\n\nexport const NumberInputGestureProvider = NumberInputGestureContext.Provider\n\nexport function useNumberInputGesture(): NumberInputGestureValue | null {\n return useContext(NumberInputGestureContext)\n}\n\n/** Set by `Stepper` once a drag has actually moved, so the steppers stand down. */\nexport type StepperContextValue = {\n /** Whether the drag in progress has moved the value. */\n moved: () => boolean\n}\n\nconst StepperContext =\n /* @__PURE__ */ createContext<StepperContextValue | null>(null)\n\nexport const StepperProvider = StepperContext.Provider\n\nexport function useStepperContext(): StepperContextValue | null {\n return useContext(StepperContext)\n}\n","import { useCallback, useEffect, useRef } from 'react'\n\nimport { createLongPress, type LongPressInstance } from '@tremolo-ui/dom'\n\nimport { useCallbackRef } from './_internal/useCallbackRef'\n\n/**\n * Repeat `callback` while a pointer is held down: once on the press, then\n * every `interval` after `initialDelay`. The repeat itself is\n * `createLongPress` in `@tremolo-ui/dom`.\n *\n * Returns the function that starts a press, for an `onPointerDown`.\n */\nexport function useLongPress(\n callback: () => void,\n initialDelay = 500,\n interval = 40,\n) {\n // Updated before layout effects run, so that a press started from one\n // right after a re-render already calls the new callback.\n const onPress = useCallbackRef(callback)\n const instanceRef = useRef<LongPressInstance | null>(null)\n\n useEffect(() => {\n instanceRef.current?.update({ delay: initialDelay, interval })\n })\n\n useEffect(() => {\n const instance = createLongPress({\n onPress: () => onPress(),\n delay: initialDelay,\n interval,\n })\n instanceRef.current = instance\n return () => {\n instanceRef.current = null\n instance.destroy()\n }\n // Created once: the options are pushed with update() above, so that a new\n // delay does not end a press in progress.\n // oxlint-disable-next-line react-hooks/exhaustive-deps\n }, [])\n\n return useCallback(\n (event?: Pick<PointerEvent, 'button' | 'pointerId'>) =>\n instanceRef.current?.start(event),\n [],\n )\n}\n","import { ComponentPropsWithoutRef, ReactNode, CSSProperties } from 'react'\n\nimport { useLongPress } from '../../hooks/useLongPress'\n\nimport {\n useNumberInputContext,\n useNumberInputGesture,\n useStepperContext,\n} from './context'\n\nimport type { CSSVariables } from '../../css-variables'\n\n/**\n * `--stepper-icon-size` is read by the default arrow inside, and set on the\n * button so that it inherits there.\n */\nexport type StepperButtonProps = Omit<\n ComponentPropsWithoutRef<'div'>,\n 'style'\n> & {\n style?: CSSProperties & CSSVariables<'stepper-icon-size'>\n}\n\ntype Props = StepperButtonProps\n\n/**\n * Shared body of `IncrementStepper` and `DecrementStepper`: the two differ only\n * in which way they move the value and which arrow they draw.\n */\nexport function StepperButton({\n direction,\n variant,\n icon,\n className,\n style,\n children,\n onPointerDown,\n ...props\n}: Props & {\n direction: 1 | -1\n variant: 'increment' | 'decrement'\n icon: ReactNode\n}) {\n const { step, disabled, readOnly, atMin, atMax, nudge } =\n useNumberInputContext()\n const stepper = useStepperContext()\n const gesture = useNumberInputGesture()\n\n // Not memoized: `useLongPress` only ever calls it through a ref, so a fresh\n // identity per render costs nothing.\n const press = useLongPress(() => {\n // Once the pointer has actually travelled, the drag on `Stepper` owns the\n // value; repeating on top of it would move it twice.\n if (stepper?.moved()) return\n nudge(direction, ['raw', step])\n })\n\n return (\n <div\n className={className}\n role=\"button\"\n tabIndex={-1}\n // A bare arrow has no accessible name of its own. Overridable, since a\n // caller may need it in their own language.\n aria-label={direction > 0 ? 'Increment' : 'Decrement'}\n aria-disabled={disabled || (direction > 0 ? atMax : atMin)}\n // `role=\"button\"` does not take aria-readonly, so the state reaches the\n // styles through the data attribute alone.\n data-disabled={\n disabled || (direction > 0 ? atMax : atMin) ? '' : undefined\n }\n data-readonly={readOnly ? '' : undefined}\n style={style}\n onPointerDown={(event) => {\n if (!disabled && !readOnly) {\n gesture?.press()\n press(event)\n }\n onPointerDown?.(event)\n }}\n {...props}\n >\n {children ?? icon}\n </div>\n )\n}\n\nexport function StepperArrow({ up }: { up: boolean }) {\n return (\n <svg\n // The arrow the component draws when no children are given, so it sizes\n // itself rather than asking a stylesheet to.\n style={{\n width: 'var(--stepper-icon-size)',\n height: 'var(--stepper-icon-size)',\n }}\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n >\n <polyline points={up ? '18 15 12 9 6 15' : '6 9 12 15 18 9'} />\n </svg>\n )\n}\n","import {\n StepperArrow,\n StepperButton,\n type StepperButtonProps,\n} from './StepperButton'\n\n/** Lowers the value by one `step`, repeating while held. */\nexport function DecrementStepper(props: StepperButtonProps) {\n return (\n <StepperButton\n direction={-1}\n variant=\"decrement\"\n icon={<StepperArrow up={false} />}\n {...props}\n />\n )\n}\n","import {\n StepperArrow,\n StepperButton,\n type StepperButtonProps,\n} from './StepperButton'\n\n/** Raises the value by one `step`, repeating while held. */\nexport function IncrementStepper(props: StepperButtonProps) {\n return (\n <StepperButton\n direction={1}\n variant=\"increment\"\n icon={<StepperArrow up />}\n {...props}\n />\n )\n}\n","import {\n ComponentPropsWithoutRef,\n forwardRef,\n useEffect,\n useLayoutEffect,\n useRef,\n useState,\n} from 'react'\n\nimport {\n caretAtDecimalOffset,\n caretDecimalOffset,\n numberSpan,\n} from '@tremolo-ui/dom/internal'\n\nimport { useComposedRefs } from '../../compose-refs'\n\nimport { useNumberInputContext, useNumberInputGesture } from './context'\n\n// The value belongs to `NumberInput.Root`, and the field is always text.\ntype Props = Omit<\n ComponentPropsWithoutRef<'input'>,\n 'type' | 'value' | 'defaultValue'\n>\n\n/**\n * The text field of a `NumberInput`, and the only place the value can be typed.\n *\n * While the user types, their own text stands rather than `format(value)`, so\n * that a half-finished entry is not rewritten under the caret.\n *\n * How it behaves on focus, Enter and the arrow keys is set on `Root`\n * (`selectOnFocus`, `unformatOnFocus`, `keepCaretOnStep`, `blurOnEnter`).\n */\nexport const InputField = /* @__PURE__ */ forwardRef<HTMLInputElement, Props>(\n function InputField(\n { className, style, onChange, onFocus, onBlur, onKeyDown, ...props },\n forwardedRef,\n ) {\n const {\n value,\n min,\n max,\n step,\n disabled,\n readOnly,\n keyboard,\n selectOnFocus,\n unformatOnFocus,\n keepCaretOnStep,\n blurOnEnter,\n text,\n editing,\n outOfRange,\n setDraft,\n commitDraft,\n nudge,\n inputRef,\n } = useNumberInputContext()\n\n const gesture = useNumberInputGesture()\n const [focused, setFocused] = useState(false)\n\n // Only until the first keystroke: from then on the draft is the user's own\n // text and stands on its own, formatted or not.\n const shown = unformatOnFocus && focused && !editing ? String(value) : text\n\n // Selecting has to wait for that swap. Called from the focus handler it\n // would run against the formatted text still in the input and cover the\n // wrong characters.\n useEffect(() => {\n if (!focused || selectOnFocus === 'none') return\n const input = inputRef.current\n if (!input) return\n if (selectOnFocus === 'all') {\n input.setSelectionRange(0, input.value.length)\n } else {\n const span = numberSpan(input.value)\n if (span) input.setSelectionRange(span.start, span.end)\n }\n // Deliberately not re-run as the text changes: that would drag the\n // selection back over what the user is typing.\n }, [focused, selectOnFocus, inputRef])\n\n /**\n * Where to put the caret once the stepped value has been rendered, and the\n * text it was measured against.\n */\n const caretAfterStep = useRef<{ offset: number; from: string } | null>(null)\n\n // A layout effect rather than a plain one: the caret is moved before the\n // browser paints, so it is never seen at the end of the text first.\n useLayoutEffect(() => {\n const pending = caretAfterStep.current\n if (!pending) return\n caretAfterStep.current = null\n\n const input = inputRef.current\n // The step may have been clamped away, leaving the text as it was. The\n // caret has not moved either, since the key press was prevented.\n if (!input || input.value === pending.from) return\n\n const caret = caretAtDecimalOffset(input.value, pending.offset)\n input.setSelectionRange(caret, caret)\n })\n\n const composedRef = useComposedRefs<HTMLInputElement>(\n forwardedRef,\n inputRef,\n )\n\n return (\n <input\n ref={composedRef}\n className={className}\n // Not type=\"number\": that brings native spinners and a value the browser\n // parses itself, neither of which survives a unit suffix.\n type=\"text\"\n inputMode=\"decimal\"\n role=\"spinbutton\"\n value={shown}\n disabled={disabled}\n readOnly={readOnly}\n aria-disabled={disabled}\n aria-readonly={readOnly}\n data-disabled={disabled ? '' : undefined}\n data-readonly={readOnly ? '' : undefined}\n aria-valuenow={value}\n aria-valuemin={min}\n aria-valuemax={max}\n // The formatted text, even while the field shows the plain number: it is\n // the one that says what the value means.\n aria-valuetext={text}\n step={step}\n data-out-of-range={outOfRange ? '' : undefined}\n style={style}\n onChange={(event) => {\n // The text is the user's own now, so a caret measured against the\n // stepped one no longer means anything.\n caretAfterStep.current = null\n setDraft(event.currentTarget.value)\n onChange?.(event)\n }}\n onFocus={(event) => {\n setFocused(true)\n onFocus?.(event)\n }}\n onBlur={(event) => {\n setFocused(false)\n // Nothing was typed, so there is no draft and this returns at once —\n // taking focus and leaving again never commits anything.\n commitDraft()\n onBlur?.(event)\n }}\n onKeyDown={(event) => {\n const key = event.key\n if (key === 'Enter') {\n commitDraft()\n if (blurOnEnter) event.currentTarget.blur()\n } else if (\n keyboard &&\n !disabled &&\n !readOnly &&\n (key === 'ArrowUp' || key === 'ArrowDown') &&\n // Arrow keys pick a candidate while an IME is converting. Stepping\n // the value there would fight the conversion, and moving the caret\n // would break it outright.\n !event.nativeEvent.isComposing\n ) {\n event.preventDefault()\n const input = event.currentTarget\n if (keepCaretOnStep && input.selectionStart !== null) {\n caretAfterStep.current = {\n offset: caretDecimalOffset(input.value, input.selectionStart),\n from: input.value,\n }\n }\n gesture?.key()\n nudge(key === 'ArrowUp' ? 1 : -1, keyboard, event)\n }\n onKeyDown?.(event)\n }}\n {...props}\n />\n )\n },\n)\n","import {\n ComponentPropsWithoutRef,\n forwardRef,\n useEffect,\n useMemo,\n useRef,\n useState,\n} from 'react'\n\nimport {\n createStepperDrag,\n type StepperDragInstance,\n} from '@tremolo-ui/dom/internal'\n\nimport { useComposedRefs } from '../../compose-refs'\n\nimport {\n StepperProvider,\n useNumberInputContext,\n useNumberInputGesture,\n} from './context'\n\ntype Props = ComponentPropsWithoutRef<'div'>\n\n/**\n * The area the steppers sit in, and a drag handle in its own right: dragging it\n * up and down moves the value one `step` every `drag` pixels. The counting is\n * `createStepperDrag` in the core.\n *\n * The drag lives here rather than on `InputField` because `createDrag` turns\n * off text selection on whatever element it is attached to.\n */\nexport const Stepper = /* @__PURE__ */ forwardRef<HTMLDivElement, Props>(\n function Stepper(\n { className, style, children, onPointerDown, ...props },\n forwardedRef,\n ) {\n const {\n value,\n disabled,\n readOnly,\n drag,\n dragSensitivity,\n pointerLock,\n dragCursor,\n rawRange,\n changeValue,\n } = useNumberInputContext()\n // Only attached while it can do something: `createDrag` puts\n // `touch-action: none` on the element, and a stepper that cannot be\n // dragged has no reason to stop the page scrolling under a finger.\n const enabled = drag !== null && !disabled && !readOnly\n\n // See useDrag for why the node is held in state rather than a ref.\n const [node, setNode] = useState<HTMLDivElement | null>(null)\n const instanceRef = useRef<StepperDragInstance | null>(null)\n const latest = useRef({ value, changeValue })\n const options = {\n range: rawRange,\n pixels: drag ?? 1,\n sensitivity: dragSensitivity,\n pointerLock,\n cursor: dragCursor,\n }\n const optionsRef = useRef(options)\n\n // Runs after every render.\n useEffect(() => {\n latest.current = { value, changeValue }\n optionsRef.current = options\n instanceRef.current?.update(options)\n })\n\n useEffect(() => {\n if (!node || !enabled) return\n const instance = createStepperDrag(node, {\n ...optionsRef.current,\n getValue: () => latest.current.value,\n onChange: (next) => latest.current.changeValue(next),\n })\n instanceRef.current = instance\n return () => {\n instanceRef.current = null\n instance.destroy()\n }\n }, [node, enabled])\n\n const gesture = useNumberInputGesture()\n const composedRef = useComposedRefs<HTMLDivElement>(forwardedRef, setNode)\n\n const context = useMemo(\n () => ({ moved: () => instanceRef.current?.moved() ?? false }),\n [],\n )\n\n return (\n <StepperProvider value={context}>\n <div\n ref={composedRef}\n className={className}\n style={style}\n onPointerDown={(event) => {\n // A drag on the stepper is a press like any other: held from here\n // until the pointer is released.\n if (enabled) gesture?.press()\n onPointerDown?.(event)\n }}\n {...props}\n >\n {children}\n </div>\n </StepperProvider>\n )\n },\n)\n","import {\n ComponentPropsWithoutRef,\n CSSProperties,\n forwardRef,\n ReactNode,\n Ref,\n useCallback,\n useImperativeHandle,\n useMemo,\n useRef,\n useState,\n} from 'react'\n\nimport {\n type ChangeSource,\n DEFAULT_DRAG_SENSITIVITY,\n DEFAULT_KEYBOARD_OPTIONS,\n DEFAULT_WHEEL_OPTIONS,\n InputEventOption,\n ModifierState,\n type ModifierValue,\n} from '@tremolo-ui/dom'\nimport {\n commitNumberInputText,\n numberInputBounds,\n numberInputRanges,\n nudgeNumberInput,\n parseNumberText,\n wheelDirection,\n} from '@tremolo-ui/dom/internal'\nimport { linearScale, type Scale } from '@tremolo-ui/functions'\n\nimport { useComposedRefs } from '../../compose-refs'\nimport { useChangeGesture } from '../../hooks/_internal/useChangeGesture'\nimport { useCheckSteps } from '../../hooks/_internal/useCheckSteps'\nimport { useWheel } from '../../hooks/useWheel'\n\nimport { NumberInputGestureProvider, NumberInputProvider } from './context'\nimport { DecrementStepper } from './DecrementStepper'\nimport { IncrementStepper } from './IncrementStepper'\nimport { InputField } from './InputField'\nimport { Stepper } from './Stepper'\n\nexport interface NumberInputProps {\n /**\n * The value. What the input shows is `format(value)`, except while the user\n * is typing, when their own text stands until it is committed.\n */\n value: number\n\n /**\n * Lowest value. Leave it out for no lower bound.\n *\n * It is enforced when a value is committed or stepped, while `clampValue` is\n * on. A value below it carries `data-out-of-range` on `InputField`.\n */\n min?: number\n /**\n * Highest value. Leave it out for no upper bound.\n *\n * It is enforced when a value is committed or stepped, while `clampValue` is\n * on. A value above it carries `data-out-of-range` on `InputField`.\n */\n max?: number\n /**\n * Granularity of the value. The steppers, a drag on `Stepper`, the wheel and\n * the arrow keys snap it to multiples of `step`; a typed value is left as it\n * is.\n *\n * @default 1\n */\n step?: number\n /**\n * How the value is distributed across the travel of a drag or a\n * `'normalized'` wheel / keyboard nudge.\n *\n * @default linearScale\n */\n scale?: Scale\n\n /**\n * Render the value as text. Plain digits by default.\n *\n * `unitFormat` from `@tremolo-ui/functions` builds this and `parse` together\n * for a unit, as a pair to spread into the root.\n *\n * @example\n * <NumberInput.Root {...unitFormat('Hz', { digits: 2 })} value={v} />\n */\n format?: (value: number) => string\n /**\n * Read a value back out of the text. Has to undo `format`.\n *\n * Text with no number in it reads as `NaN`, which leaves the value alone.\n * The default, `parseNumberText` from `@tremolo-ui/dom`, reads the number\n * with the unit around it left out, and is `NaN` for one it cannot read\n * whole: `1,000`, `1:30`, or a number with a unit in front, such as `L 30`.\n */\n parse?: (text: string) => number\n\n /**\n * Keep the value within `min` and `max` when it is committed or stepped.\n * Typing is never clamped, so that a value can be entered digit by digit.\n * @default true\n */\n clampValue?: boolean\n\n /**\n * How much one notch of the wheel moves the value. It only acts while the\n * focus is inside, so that scrolling the page past the input leaves it\n * alone.\n *\n * `['raw', n]` moves the value by `n`, and `['normalized', n]` by `n` of the\n * range between `min` and `max`. The result is snapped to `step`, except for\n * an amount set on a modifier key (`{ default: …, shift: … }`). `null` turns\n * the wheel off.\n *\n * @default ['raw', 1]\n */\n wheel?: ModifierValue<InputEventOption> | null\n /**\n * How much one arrow key press moves the value.\n *\n * `['raw', n]` moves the value by `n`, and `['normalized', n]` by `n` of the\n * range between `min` and `max`. The result is snapped to `step`, except for\n * an amount set on a modifier key, which is what lets shift move off the\n * grid. `null` turns the arrow keys off.\n *\n * The default moves by 1, and by 0.1 with shift. With a `step` above 1, raise\n * the amount to match: 1 would round straight back to where it started, and\n * a development build warns about it.\n *\n * @default { default: ['raw', 1], shift: ['raw', 0.1] }\n */\n keyboard?: ModifierValue<InputEventOption> | null\n /**\n * Pixels of vertical drag on `Stepper` that move the value by one `step`.\n * `null` turns the drag off.\n * @default 1\n */\n drag?: number | null\n\n /**\n * How much a `Stepper` drag counts, per modifier key.\n *\n * `1` is `drag` pixels per `step`; `0.1` makes the same movement cover a\n * tenth of that. Shift is bound to `0.1` by default, to match what it does\n * on the arrow keys.\n *\n * A modifier entry is not snapped to `step`, which is what lets a finer\n * amount move at all.\n *\n * @default { default: 1, shift: 0.1 }\n */\n dragSensitivity?: ModifierValue<number>\n\n /**\n * Hide the cursor while dragging a `Stepper` and read the pointer movement\n * directly, rather than letting it wander off across the screen.\n *\n * The drag is already relative, so the pointer position carries nothing —\n * but it still runs into the edge of the screen, where the operating system\n * pins it and the coordinates stop changing.\n *\n * Off by default: the browser shows its own notice, Esc takes the lock back,\n * and the request can be refused. A refused request is not an error, and the\n * drag carries on as an ordinary one.\n *\n * @default false\n */\n pointerLock?: boolean\n /**\n * The cursor to show while dragging a `Stepper`. It is set on the stepper\n * being dragged, so it stays while the pointer is outside it.\n *\n * @default 'ns-resize'\n */\n dragCursor?: CSSProperties['cursor']\n\n /**\n * Select the text when `InputField` takes focus: `'all'` selects all of it,\n * `'number'` only the number, leaving the unit the format put around it,\n * and `'none'` leaves the caret where the click put it.\n * @default 'none'\n */\n selectOnFocus?: 'all' | 'number' | 'none'\n /**\n * Show the plain value while `InputField` has focus, dropping whatever\n * `format` put around it: an input reading `1.23kHz` shows `1230` to be\n * typed over.\n *\n * The number shown is the value itself, not the number inside the formatted\n * text. Those differ whenever the format scales — `1.23` out of `1.23kHz`\n * would read back as 1.23 and lose a factor of a thousand — and it is also\n * why a rounded display no longer becomes the value: `1.6` shown as `2Hz`\n * offers `1.6` for editing, not `2`.\n *\n * @default false\n */\n unformatOnFocus?: boolean\n /**\n * Put the caret in `InputField` back where it was after an arrow key steps\n * the value.\n *\n * A controlled input whose `value` is replaced drops the caret at the end,\n * so without this the second press of a repeated step always acts on the\n * last digit. With it, the digit under the caret stays under the caret and\n * a column can be held while stepping.\n *\n * The position is measured from the decimal point rather than from either\n * end, so it survives the number growing or shrinking: the caret between\n * `9` and `.9` is still between `10` and `.0`.\n *\n * It only restores the caret. Which digit it sits on does not change the\n * size of the step — that is `keyboard`'s to say.\n *\n * @default false\n */\n keepCaretOnStep?: boolean\n /**\n * Commit and leave `InputField` when Enter is pressed. Enter commits either\n * way.\n * @default true\n */\n blurOnEnter?: boolean\n\n /**\n * Make the input unchangeable and remove it from the tab order.\n * The parts carry `data-disabled` while it is set.\n */\n disabled?: boolean\n /**\n * Make the value unchangeable.\n * The parts carry `data-readonly` while it is set.\n */\n readOnly?: boolean\n\n /**\n * Called with the new value. While the user types, it is called for every\n * entry that reads as a number, unclamped; committing the entry calls it\n * again if clamping changes the value.\n */\n onChange?: (value: number) => void\n /**\n * Called when a change of the value starts — a press on a stepper, the\n * first wheel notch, arrow key or typed character — with the value before\n * it and what it is made with. A host recording automation can treat the\n * input as touched from here until `onChangeEnd`.\n */\n onChangeStart?: (value: number, source: ChangeSource) => void\n /**\n * Called when the change ends, with the value it ended on: when the stepper\n * is released, when typed text is committed, or `changeEndDelay` after the\n * last wheel notch or arrow key.\n */\n onChangeEnd?: (value: number, source: ChangeSource) => void\n /**\n * How long after the last wheel notch, arrow key or typed character the\n * change counts as over, in milliseconds. None of them has an event that\n * says it is done.\n *\n * @default 500\n */\n changeEndDelay?: number\n\n /**\n * The input renders exactly what you compose here; there is no default\n * markup to fall back to.\n *\n * @example\n * <NumberInput.Root value={value} min={0} max={100} onChange={setValue}>\n * <NumberInput.InputField />\n * <NumberInput.Stepper>\n * <NumberInput.IncrementStepper />\n * <NumberInput.DecrementStepper />\n * </NumberInput.Stepper>\n * </NumberInput.Root>\n */\n children: ReactNode\n\n /**\n * Receives `focus` and `blur`, which act on the input of `InputField` — the\n * element that takes the focus — and do nothing while\n * the input is disabled. `ref` reaches the root element itself.\n */\n actionsRef?: Ref<NumberInputMethods>\n}\n\nexport interface NumberInputMethods {\n focus: () => void\n blur: () => void\n}\n\nconst defaultFormat = (value: number) => String(value)\n\ntype Props = NumberInputProps &\n Omit<ComponentPropsWithoutRef<'div'>, keyof NumberInputProps>\n\nexport const Root = /* @__PURE__ */ forwardRef<HTMLDivElement, Props>(\n (\n {\n value,\n min,\n max,\n step = 1,\n scale = linearScale,\n format: formatProp,\n parse: parseProp,\n clampValue = true,\n wheel = DEFAULT_WHEEL_OPTIONS,\n keyboard = DEFAULT_KEYBOARD_OPTIONS,\n drag = 1,\n dragSensitivity = DEFAULT_DRAG_SENSITIVITY,\n pointerLock = false,\n dragCursor = 'ns-resize',\n selectOnFocus = 'none',\n unformatOnFocus = false,\n keepCaretOnStep = false,\n blurOnEnter = true,\n disabled = false,\n readOnly = false,\n className,\n style,\n onChange,\n onChangeStart,\n onChangeEnd,\n changeEndDelay,\n children,\n actionsRef,\n ...props\n }: Props,\n forwardedRef,\n ) => {\n // --- state and ref ---\n const inputRef = useRef<HTMLInputElement>(null)\n /**\n * The text being typed. The only state here: it is not a copy of `value`,\n * but the half-finished entry that has no value to be derived from yet.\n */\n const [draft, setDraft] = useState<string | null>(null)\n const inactive = disabled || readOnly\n\n const gesture = useChangeGesture(\n value,\n { onChangeStart, onChangeEnd, changeEndDelay },\n inactive,\n )\n\n // --- interpret props ---\n const format = formatProp ?? defaultFormat\n const parse = parseProp ?? parseNumberText\n\n const { normalized: range, raw: rawRange } = useMemo(\n () => numberInputRanges({ min, max, step, scale, clampValue }),\n [clampValue, min, max, step, scale],\n )\n const ranges = useMemo(\n () => ({ normalized: range, raw: rawRange }),\n [range, rawRange],\n )\n\n // The pipeline range stands in the safe-integer range when an end is\n // open, and there is no travel to sample across that. Probing is skipped\n // rather than run against a range nobody set.\n const probeRange = useMemo(\n () =>\n min !== undefined && max !== undefined && min < max\n ? { min, max, step, scale }\n : null,\n [min, max, step, scale],\n )\n\n useCheckSteps({\n component: 'NumberInput',\n range: probeRange,\n keyboard,\n wheel,\n format,\n })\n\n const text = draft ?? format(value)\n const editing = draft !== null\n const bounds = numberInputBounds(value, { min, max, clampValue })\n const { atMin, atMax } = bounds\n const outOfRange = !editing && bounds.outOfRange\n\n // --- internal functions ---\n const handleDraft = useCallback(\n (next: string) => {\n if (inactive) return\n setDraft(next)\n // Deliberately unclamped: clamping here would make \"1500\" impossible\n // to type into an input whose max is 100.\n const parsed = parse(next)\n if (!Number.isFinite(parsed)) return\n if (parsed !== value) {\n gesture.pulse('keyboard')\n gesture.changed(parsed)\n }\n onChange?.(parsed)\n },\n [inactive, parse, onChange, value, gesture],\n )\n\n const changeValue = useCallback(\n (next: number) => {\n if (inactive) return\n setDraft(null)\n if (next === value) return\n gesture.changed(next)\n onChange?.(next)\n },\n [inactive, value, onChange, gesture],\n )\n\n const commitDraft = useCallback(() => {\n if (draft === null || inactive) return\n const committed = commitNumberInputText(draft, parse, {\n min,\n max,\n clampValue,\n })\n // Text with no number in it is not a value. Dropping the draft puts the\n // input back to what it was showing.\n if (committed === null) {\n setDraft(null)\n return\n }\n // The typing was a gesture of its own, and committing is where it ends.\n if (committed !== value) gesture.pulse('keyboard')\n changeValue(committed)\n gesture.end()\n }, [\n draft,\n inactive,\n parse,\n clampValue,\n min,\n max,\n changeValue,\n value,\n gesture,\n ])\n\n const nudge = useCallback(\n (\n direction: number,\n option: ModifierValue<InputEventOption>,\n modifiers?: ModifierState,\n ) => {\n changeValue(\n nudgeNumberInput(value, direction, option, ranges, modifiers),\n )\n },\n [changeValue, value, ranges],\n )\n\n // --- hooks ---\n const wheelRefCallback = useWheel<HTMLDivElement>(\n (event) => {\n const direction = wheelDirection(event)\n if (!wheel || inactive || direction === null) return\n event.preventDefault()\n gesture.pulse('wheel')\n nudge(direction, wheel, event)\n },\n { requireFocus: true },\n )\n\n const context = useMemo(\n () => ({\n value,\n min,\n max,\n step,\n scale,\n disabled,\n readOnly,\n clampValue,\n range,\n rawRange,\n keyboard,\n text,\n editing,\n outOfRange,\n dragSensitivity,\n pointerLock,\n dragCursor,\n selectOnFocus,\n unformatOnFocus,\n keepCaretOnStep,\n blurOnEnter,\n atMin,\n atMax,\n drag,\n format,\n parse,\n setDraft: handleDraft,\n commitDraft,\n changeValue,\n nudge,\n inputRef,\n }),\n [\n value,\n min,\n max,\n step,\n scale,\n disabled,\n readOnly,\n clampValue,\n range,\n rawRange,\n keyboard,\n text,\n editing,\n outOfRange,\n atMin,\n atMax,\n drag,\n dragSensitivity,\n pointerLock,\n dragCursor,\n selectOnFocus,\n unformatOnFocus,\n keepCaretOnStep,\n blurOnEnter,\n format,\n parse,\n handleDraft,\n commitDraft,\n changeValue,\n nudge,\n ],\n )\n\n // Composed once, so React attaches the refs a single time instead of\n // detaching and re-attaching on every render.\n const rootRefCallback = useComposedRefs<HTMLDivElement>(\n forwardedRef,\n wheelRefCallback,\n )\n\n useImperativeHandle(actionsRef, () => {\n return {\n focus() {\n if (!disabled) inputRef.current?.focus()\n },\n blur() {\n inputRef.current?.blur()\n },\n }\n }, [disabled])\n\n const pressed = useRef(false)\n const gestureContext = useMemo(\n () => ({\n press: () => {\n if (inactive) return\n gesture.hold('pointer')\n // A press on a stepper reaches `Stepper` too; one release will do.\n if (pressed.current) return\n pressed.current = true\n // Released wherever the pointer ends up: the stepper drag captures\n // it, and a press that wanders off the button still ends.\n const release = () => {\n pressed.current = false\n gesture.end()\n window.removeEventListener('pointerup', release)\n window.removeEventListener('pointercancel', release)\n }\n window.addEventListener('pointerup', release)\n window.addEventListener('pointercancel', release)\n },\n key: () => {\n if (!inactive) gesture.pulse('keyboard')\n },\n }),\n [gesture, inactive],\n )\n\n return (\n <NumberInputProvider value={context}>\n <NumberInputGestureProvider value={gestureContext}>\n <div\n ref={rootRefCallback}\n className={className}\n data-disabled={disabled ? '' : undefined}\n data-readonly={readOnly ? '' : undefined}\n style={style}\n {...props}\n >\n {children}\n </div>\n </NumberInputGestureProvider>\n </NumberInputProvider>\n )\n },\n)\n\n/**\n * Input with some useful functions for entering numerical values.\n */\nexport const NumberInput = {\n Root,\n InputField,\n Stepper,\n IncrementStepper,\n DecrementStepper,\n}\n\nexport { useNumberInputContext, type NumberInputContextValue } from './context'\n","import {\n ComponentPropsWithoutRef,\n CSSProperties,\n forwardRef,\n ReactNode,\n Ref,\n useEffect,\n useImperativeHandle,\n useMemo,\n useRef,\n useState,\n} from 'react'\n\nimport {\n createPianoInput,\n type KeyboardShortcuts,\n type KeyboardShortcutsScope,\n notePosition,\n type NoteRange,\n type PianoInputInstance,\n type PianoLayout,\n} from '@tremolo-ui/dom'\nimport {\n blackKeyWidth,\n fitWhiteKeyWidth,\n getNoteRangeArray,\n pianoWidth,\n} from '@tremolo-ui/dom/internal'\nimport { isWhiteKey, noteKey } from '@tremolo-ui/functions'\n\nimport { useComposedRefs } from '../../compose-refs'\n\n/**\n * What {@link PianoProps.keyProps} may return for one key.\n *\n * `data-*` attributes are spelled out because TypeScript only allows them on\n * JSX syntax, not on an object type, and selecting on one is the usual way to\n * mark a key out.\n */\nexport type KeyAttributes = ComponentPropsWithoutRef<'div'> &\n Record<`data-${string}`, string | number | boolean | undefined>\n\n/** What a key is, when {@link PianoProps.label} or `keyProps` is asked about it. */\nexport interface KeyState {\n /** Position in the note range, counting from `noteRange.first`. */\n index: number\n keyType: 'white' | 'black'\n /** Whether the note is currently sounding. */\n active: boolean\n /** Whether the note is above {@link PianoProps.midiMax} and cannot sound. */\n disabled: boolean\n}\n\nexport interface PianoProps {\n /**\n * Classes for what a key draws inside itself: `keyLabelWrapper`, and\n * `keyLabel` around what `label` returns. The key itself takes a class\n * through `keyProps`, which is also where the note and its state are.\n */\n classes?: {\n keyLabelWrapper?: string\n keyLabel?: string\n }\n\n /**\n * The notes to draw keys for, as MIDI note numbers from `first` to `last`,\n * both included.\n */\n noteRange: NoteRange\n\n /**\n * Let a pointer slide from one key to the next while it is down. With it\n * off, the key that was pressed sounds until the pointer is released.\n *\n * @default true\n */\n glissando?: boolean\n\n /**\n * Highest note that can sound. Keys above it carry `data-disabled`.\n *\n * @default 127\n */\n midiMax?: number\n\n /**\n * Play notes from the computer keyboard: `keys[i]` plays\n * `noteRange.first + i`. `SHORTCUTS` has ready-made layouts, which assume\n * `noteRange.first` is a C. Where they listen is `keyboardShortcutsScope`.\n */\n keyboardShortcuts?: KeyboardShortcuts\n\n /**\n * Where keyboard shortcuts listen. `root` handles keys only while the Piano\n * root or one of its descendants has focus; `window` handles them anywhere\n * on the page except in editable elements.\n *\n * @default 'root'\n */\n keyboardShortcutsScope?: KeyboardShortcutsScope\n\n /**\n * Follow the width of the parent element, deriving the width of a white key\n * from it. {@link PianoProps.whiteKeyWidth} is ignored.\n *\n * @default false\n */\n resizable?: boolean\n\n /**\n * Width of a white key in pixels, not counting `keyGap`. Ignored while\n * `resizable` is on.\n * @default 40\n */\n whiteKeyWidth?: number\n /**\n * Space between two white keys, in pixels.\n * @default 1\n */\n keyGap?: number\n /**\n * Width of a black key, as a fraction of {@link PianoProps.whiteKeyWidth}.\n * @default 0.65\n */\n blackKeyWidthRatio?: number\n /**\n * Height of a black key, as a fraction of the height of the keyboard.\n * @default 0.6\n */\n blackKeyHeightRatio?: number\n\n /**\n * What to draw inside a key. `''`, `null` and `undefined` leave it bare, so\n * a layout with gaps — `SHORTCUTS.HOME_ROW_NATURAL`, say — needs no\n * special casing.\n */\n label?: (note: number, state: KeyState) => ReactNode\n\n /**\n * Extra props for one key, by note: a class, a style, a `data-*` attribute\n * to select on.\n *\n * The geometry of the key (`left`, `width`, `height`) is applied after the\n * returned `style` and cannot be overridden, so a key cannot be drawn\n * somewhere other than where it responds.\n *\n * @example highlight the notes of a scale\n * ```tsx\n * keyProps={(note) => ({ 'data-in-scale': inScale(note, root, 'major') ? '' : undefined })}\n * ```\n */\n keyProps?: (note: number, state: KeyState) => KeyAttributes\n\n /**\n * Called when a note starts sounding, whether a pointer, a keyboard shortcut\n * or `playNote` asked for it. A note held by several of them at once is\n * reported once. `velocity` is what `playNote` was given, and `undefined`\n * otherwise.\n */\n onPlayNote?: (note: number, velocity?: number) => void\n /** Called once everything holding a note has let go of it. */\n onStopNote?: (note: number) => void\n\n /**\n * Receives `playNote` and `stopNote`, which press and release a key as the\n * pointer or the keyboard would. `ref` reaches the root element itself.\n */\n actionsRef?: Ref<PianoMethods>\n}\n\nexport interface PianoMethods {\n playNote: (note: number, velocity?: number) => void\n stopNote: (note: number) => void\n}\n\ntype Props = PianoProps &\n Omit<ComponentPropsWithoutRef<'div'>, keyof PianoProps>\n\nexport const Root = /* @__PURE__ */ forwardRef<HTMLDivElement, Props>(\n function Root(\n {\n noteRange,\n glissando = true,\n midiMax = 127,\n keyboardShortcuts,\n keyboardShortcutsScope = 'root',\n resizable = false,\n whiteKeyWidth = 40,\n keyGap = 1,\n blackKeyWidthRatio = 0.65,\n blackKeyHeightRatio = 0.6,\n style,\n className,\n label,\n keyProps,\n classes,\n onPlayNote,\n onStopNote,\n actionsRef,\n ...props\n },\n forwardedRef,\n ) {\n // See useDrag for why the node is held in state rather than a ref: an inline\n // ref would be re-attached on every render and tear the instance down.\n const [node, setNode] = useState<HTMLDivElement | null>(null)\n const [activeNotes, setActiveNotes] = useState<number[]>([])\n /** Set while `resizable` is on, measured from the parent. */\n const [resizedKeyWidth, setResizedKeyWidth] = useState(whiteKeyWidth)\n\n const notes = useMemo(() => getNoteRangeArray(noteRange), [noteRange])\n\n const layout: PianoLayout = useMemo(\n () => ({\n noteRange,\n whiteKeyWidth: resizable ? resizedKeyWidth : whiteKeyWidth,\n keyGap,\n blackKeyWidthRatio,\n blackKeyHeightRatio,\n }),\n [\n noteRange,\n resizable,\n resizedKeyWidth,\n whiteKeyWidth,\n keyGap,\n blackKeyWidthRatio,\n blackKeyHeightRatio,\n ],\n )\n\n // Read when the instance is created. The effect below keeps it current, and\n // runs right after, so a stale handler is replaced within the same commit.\n const latest = useRef({\n layout,\n glissando,\n midiMax,\n keyboardShortcuts,\n keyboardShortcutsScope,\n onPlayNote,\n onStopNote,\n })\n const instanceRef = useRef<PianoInputInstance | null>(null)\n useEffect(() => {\n if (!node) return\n\n const instance = createPianoInput(node, {\n layout: latest.current.layout,\n glissando: latest.current.glissando,\n midiMax: latest.current.midiMax,\n keyboardShortcuts: latest.current.keyboardShortcuts,\n keyboardShortcutsScope: latest.current.keyboardShortcutsScope,\n onPlayNote: (note, velocity) =>\n latest.current.onPlayNote?.(note, velocity),\n onStopNote: (note) => latest.current.onStopNote?.(note),\n onActiveNotesChange: setActiveNotes,\n })\n instanceRef.current = instance\n\n return () => {\n instanceRef.current = null\n instance.destroy()\n }\n // Only the element decides how the instance is wired. Everything else is\n // pushed with update() below, so that changing the layout mid-drag — the\n // parent being resized under `resizable`, say — does not abort the drag.\n }, [node])\n\n // Runs after every render.\n useEffect(() => {\n latest.current = {\n layout,\n glissando,\n midiMax,\n keyboardShortcuts,\n keyboardShortcutsScope,\n onPlayNote,\n onStopNote,\n }\n instanceRef.current?.update({\n layout,\n glissando,\n midiMax,\n keyboardShortcuts,\n keyboardShortcutsScope,\n })\n })\n\n // By value, so that a range written inline does not reconnect the\n // observer on every render.\n const { first, last } = noteRange\n useEffect(() => {\n if (!resizable || !node) return\n const parent = node.parentElement\n if (!parent) throw new Error(\"doesn't have a parent element.\")\n\n const resizeObserver = new ResizeObserver(() => {\n setResizedKeyWidth(\n fitWhiteKeyWidth(node.clientWidth, {\n noteRange: { first, last },\n keyGap,\n blackKeyWidthRatio,\n }),\n )\n })\n resizeObserver.observe(parent)\n return () => resizeObserver.disconnect()\n }, [resizable, node, first, last, keyGap, blackKeyWidthRatio])\n\n const rootRefCallback = useComposedRefs<HTMLDivElement>(\n forwardedRef,\n setNode,\n )\n\n useImperativeHandle(\n actionsRef,\n () => ({\n playNote: (note, velocity) =>\n instanceRef.current?.noteOn(note, { source: 'api', velocity }),\n stopNote: (note) =>\n instanceRef.current?.noteOff(note, { source: 'api' }),\n }),\n [],\n )\n\n return (\n <div\n ref={rootRefCallback}\n className={className}\n data-resizable={resizable ? '' : undefined}\n role=\"group\"\n // The group can own keyboard shortcuts and must receive focus.\n // oxlint-disable-next-line jsx-a11y/no-noninteractive-tabindex\n tabIndex={0}\n style={\n {\n // Computed from the layout rather than chosen, so it stays inline.\n width: resizable ? '100%' : pianoWidth(layout),\n // The keys inside are placed against this box.\n position: 'relative',\n ...style,\n } as CSSProperties\n }\n {...props}\n >\n {notes.map((note, index) => {\n const keyType = isWhiteKey(note) ? 'white' : 'black'\n const state: KeyState = {\n index,\n keyType,\n active: activeNotes.includes(note),\n disabled: note > midiMax,\n }\n\n const {\n className: keyClassName,\n style: keyStyle,\n ...rest\n } = keyProps?.(note, state) ?? {}\n\n const content = label?.(note, state)\n\n return (\n <div\n key={note}\n className={keyClassName}\n data-note={note}\n data-note-key={noteKey(note)}\n data-active={state.active ? '' : undefined}\n data-disabled={state.disabled ? '' : undefined}\n {...rest}\n style={{\n // The key is placed and sized from the layout below, which\n // needs it out of flow; the black keys sit over the white\n // ones whichever order they are drawn in.\n position: 'absolute',\n zIndex: keyType === 'white' ? 1 : 2,\n ...keyStyle,\n left: notePosition(note, layout),\n width:\n keyType === 'white'\n ? layout.whiteKeyWidth\n : blackKeyWidth(layout),\n height:\n keyType === 'white'\n ? '100%'\n : `${blackKeyHeightRatio * 100}%`,\n }}\n >\n {content !== '' && content !== null && content !== undefined && (\n <div className={classes?.keyLabelWrapper}>\n <div className={classes?.keyLabel}>{content}</div>\n </div>\n )}\n </div>\n )\n })}\n </div>\n )\n },\n)\n\n/**\n * Customizable piano component.\n */\nexport const Piano = { Root }\n","import { ComponentPropsWithoutRef } from 'react'\n\nexport function Background({\n className,\n children,\n style,\n ...props\n}: ComponentPropsWithoutRef<'div'>) {\n return (\n <div\n className={className}\n style={{\n // Behind the points, filling the editor: what it is for, rather than\n // a look. The order of the layers holds whichever way they are\n // written, so the z-index is part of the same mechanics.\n position: 'absolute',\n inset: 0,\n zIndex: 0,\n ...style,\n }}\n {...props}\n >\n {children}\n </div>\n )\n}\n","import { createContext, CSSProperties, RefObject, useContext } from 'react'\n\nimport {\n type InputEventOption,\n type ModifierValue,\n type PointsEditorInstance,\n type SelectionBoxRect,\n} from '@tremolo-ui/dom'\n\nexport type PointsEditorContextValue = {\n disabled: boolean\n readOnly: boolean\n /** Inherited by every `Point`; `null` turns the wheel off. */\n wheel: ModifierValue<InputEventOption> | null\n /** Inherited by every `Point`; `null` turns the keyboard off. */\n keyboard: ModifierValue<InputEventOption> | null\n /** Inherited by every `Point`. See `PointsEditorProps.dragSensitivity`. */\n dragSensitivity: ModifierValue<number>\n /** Inherited by every `Point`. See `PointsEditorProps.dragCursor`. */\n dragCursor: CSSProperties['cursor']\n /**\n * `Container` registers its element here; `Point` normalizes the pointer\n * against it, so a point is placed by its position within the container.\n */\n containerRef: RefObject<HTMLDivElement | null>\n /** Whether points can be selected at all. See `PointsEditorProps.selectable`. */\n selectable: boolean\n /** Ids of the points currently selected. Always empty while `selectable` is off. */\n selection: readonly string[]\n /**\n * The selection and the moves, from `createPointsEditor` in\n * `@tremolo-ui/dom`: every `Point` registers with it, and a press, a drag,\n * a key or the wheel goes through it so that a selection moves as one.\n */\n editor: PointsEditorInstance\n /** The selection box, while one is being dragged. */\n selectionBox: SelectionBoxRect | null\n}\n\nconst PointsEditorContext =\n /* @__PURE__ */ createContext<PointsEditorContextValue | null>(null)\n\nexport const PointsEditorProvider = PointsEditorContext.Provider\n\n/**\n * The settings `Root` was given, for the subcomponents to read. A point's\n * value still belongs to the `Point` that draws it; what the root keeps is\n * which points are selected, and a registry of who they are.\n */\nexport function usePointsEditorContext(): PointsEditorContextValue\nexport function usePointsEditorContext<T>(\n selector: (state: PointsEditorContextValue) => T,\n): T\nexport function usePointsEditorContext<T>(\n selector?: (state: PointsEditorContextValue) => T,\n) {\n const context = useContext(PointsEditorContext)\n if (!context)\n throw new Error('Missing PointsEditorContext.Provider in the tree')\n return selector ? selector(context) : context\n}\n","import { ComponentPropsWithoutRef, forwardRef } from 'react'\n\nimport { POINT_AXIS, wheelMove } from '@tremolo-ui/dom/internal'\n\nimport { useComposedRefs } from '../../compose-refs'\nimport { useDragValue } from '../../hooks/useDragValue'\nimport { useWheel } from '../../hooks/useWheel'\nimport { Placement } from '../_util/Placement'\n\nimport { usePointsEditorContext } from './context'\n\ntype Props = ComponentPropsWithoutRef<'div'>\n\nexport const Container = /* @__PURE__ */ forwardRef<HTMLDivElement, Props>(\n function Container({ children, className, style, ...props }, forwardedRef) {\n const { containerRef, disabled, selectable, editor } =\n usePointsEditorContext()\n const { refCallback: dragRefCallback } = useDragValue<HTMLDivElement>({\n axis: POINT_AXIS,\n baseElementRef: containerRef,\n // A press that landed on a point belongs to that point. Declining here\n // rather than in onDragStart matters: by then the container would already\n // have taken the pointer capture away from the point.\n shouldStart: (event) =>\n !disabled && !editor.isPointElement(event.target as Element | null),\n onDragStart: ([x, y], state) => {\n editor.beginSelectionBox({ x, y }, state.event)\n },\n onChange: ([x, y]) => editor.moveSelectionBox({ x, y }),\n onDragEnd: () => editor.endSelectionBox(),\n })\n\n // One listener for the whole editor rather than one per point: a wheel\n // event only reaches what the cursor is over, and a point is a 16px\n // target, so the focused point takes it from anywhere over the editor —\n // the way it does for Slider and XYPad.\n useWheel(\n (event) => {\n // Scrolling up moves the point towards y = 0; shift switches to x.\n const move = wheelMove(event)\n if (!move) return\n const axis = move.axis === 0 ? 'x' : 'y'\n if (editor.nudgeFocusedPoint(axis, move.direction, event)) {\n event.preventDefault()\n }\n },\n { target: containerRef },\n )\n\n // The container is what the pointer position is normalized against, so the\n // context ref is composed with any ref the caller passed.\n // Only attached when there is a selection to draw: `createDrag` puts\n // `touch-action: none` on whatever it holds, and an editor that cannot\n // select has no reason to stop the page scrolling under a finger.\n const composedRef = useComposedRefs<HTMLDivElement>(\n forwardedRef,\n containerRef,\n selectable && !disabled ? dragRefCallback : undefined,\n )\n\n return (\n <div\n ref={composedRef}\n className={className}\n style={{\n // The points inside are placed against this box, over the\n // background and under the selection box.\n position: 'absolute',\n inset: 0,\n zIndex: 10,\n ...style,\n }}\n {...props}\n >\n <Placement name=\"PointsEditor.Container\">{children}</Placement>\n </div>\n )\n },\n)\n","import { ComponentPropsWithoutRef, forwardRef } from 'react'\n\nimport { visuallyHiddenStyle } from '@tremolo-ui/dom/internal'\n\ntype Props = Omit<ComponentPropsWithoutRef<'input'>, 'type' | 'style'>\n\nexport const VisuallyHiddenRangeInput = /* @__PURE__ */ forwardRef<\n HTMLInputElement,\n Props\n>((props, ref) => (\n <input {...props} ref={ref} type=\"range\" style={visuallyHiddenStyle} />\n))\n","import {\n ComponentPropsWithoutRef,\n CSSProperties,\n useEffect,\n useId,\n useRef,\n useState,\n} from 'react'\n\nimport {\n type ChangeSource,\n type InputEventOption,\n type ModifierValue,\n type PointPosition,\n type PointsEditorPoint,\n} from '@tremolo-ui/dom'\nimport {\n arrowKeyMove,\n clampPoint,\n POINT_AXIS,\n selectModifier,\n} from '@tremolo-ui/dom/internal'\n\nimport { useComposedRefs } from '../../compose-refs'\nimport { useChangeGesture } from '../../hooks/_internal/useChangeGesture'\nimport { useDragValue } from '../../hooks/useDragValue'\nimport { useCheckPlacement } from '../_util/Placement'\nimport { VisuallyHiddenRangeInput } from '../_util/VisuallyHiddenRangeInput'\n\nimport { usePointsEditorContext } from './context'\n\nimport type { CSSVariables } from '../../css-variables'\n\nexport interface PointsEditorPointProps<T extends PointPosition> {\n /**\n * Where the point is, as `{ x, y }` from 0 to 1 on each axis, with `y`\n * growing downwards.\n */\n value: T\n /**\n * How the selection refers to this point. One is generated when it is left\n * out, which lasts as long as the point is mounted — give your own if the\n * selection has to survive a remount, or be recognised in your own state.\n */\n id?: string\n /** The lowest position the point can take, per axis. An axis left out is 0. */\n min?: Partial<PointPosition>\n /** The highest position the point can take, per axis. An axis left out is 1. */\n max?: Partial<PointPosition>\n\n /** Sets `--color`, for the theme to colour the point with. */\n color?: string\n\n /** Overrides the `disabled` of `PointsEditor.Root`. */\n disabled?: boolean\n /** Overrides the `readOnly` of `PointsEditor.Root`. */\n readOnly?: boolean\n\n /** Overrides the `wheel` of `PointsEditor.Root`. */\n wheel?: ModifierValue<InputEventOption> | null\n /** Overrides the `keyboard` of `PointsEditor.Root`. */\n keyboard?: ModifierValue<InputEventOption> | null\n\n /**\n * The accessible name of each axis. There are two range inputs inside the\n * point, so this takes one name per axis; a single string names them both.\n */\n 'aria-label'?: string | Partial<Record<'x' | 'y', string>>\n /** What the value of each axis means, when the number does not say it. */\n 'aria-valuetext'?: string | Partial<Record<'x' | 'y', string>>\n\n /**\n * Called with the new position when the point is dragged or moved by the\n * arrow keys or the wheel, including when it moves along with a selection.\n */\n onChange?: (value: PointPosition) => void\n /**\n * Called when a change of this point starts — a drag on it, the first wheel\n * notch or arrow key while it has the focus — with where it is and what the\n * change is made with. The points that move along with a selection report\n * through `onChange` only: the change belongs to the point being operated.\n */\n onChangeStart?: (value: PointPosition, source: ChangeSource) => void\n /**\n * Called when that change ends, with where the point is: on release, or\n * `changeEndDelay` after the last wheel notch or arrow key.\n */\n onChangeEnd?: (value: PointPosition, source: ChangeSource) => void\n /**\n * How long after the last wheel notch or arrow key the change counts as\n * over, in milliseconds. Neither has an event that says it is done.\n *\n * @default 500\n */\n changeEndDelay?: number\n\n style?: CSSProperties & CSSVariables<'color' | 'translate'>\n}\n\n/**\n * One setting for both axes, or one per axis. The point holds a range input\n * for each, so anything that names or describes it comes in a pair.\n */\nfunction perAxis(\n value: string | Partial<Record<'x' | 'y', string>> | undefined,\n axis: 'x' | 'y',\n) {\n return typeof value === 'string' ? value : value?.[axis]\n}\n\nexport function Point<T extends PointPosition>({\n value,\n children,\n id: idProp,\n min,\n max,\n color,\n\n disabled: _disabled,\n readOnly: _readOnly,\n wheel: _wheel,\n keyboard: _keyboard,\n 'aria-label': ariaLabel,\n 'aria-valuetext': ariaValuetext,\n\n onChange,\n onChangeStart,\n onChangeEnd,\n changeEndDelay,\n\n className,\n style,\n onPointerDown,\n onKeyDown,\n onFocus,\n ...props\n}: PointsEditorPointProps<T> &\n Omit<ComponentPropsWithoutRef<'div'>, keyof PointsEditorPointProps<T>>) {\n const {\n containerRef,\n dragCursor,\n disabled: rootDisabled,\n readOnly: rootReadOnly,\n wheel: rootWheel,\n keyboard: rootKeyboard,\n dragSensitivity,\n selection,\n editor,\n } = usePointsEditorContext()\n\n const generatedId = useId()\n const id = idProp ?? generatedId\n const selected = selection.includes(id)\n\n const disabled = _disabled ?? rootDisabled\n const readOnly = _readOnly ?? rootReadOnly\n const inactive = disabled || readOnly\n // `null` means \"no event\" and has to survive the fallback, so `??` is not\n // enough: only an omitted prop inherits from the root.\n const wheel = _wheel === undefined ? rootWheel : _wheel\n const keyboard = _keyboard === undefined ? rootKeyboard : _keyboard\n\n useCheckPlacement('PointsEditor.Point', 'PointsEditor.Container')\n\n const current = clampPoint(value, min, max)\n const gesture = useChangeGesture(\n current,\n { onChangeStart, onChangeEnd, changeEndDelay },\n inactive,\n )\n // Every move reaches the point through here, whatever moved it, so the\n // last one is what `onChangeEnd` gives.\n const handleChange = (next: PointPosition) => {\n gesture.changed(next)\n onChange?.(next)\n }\n\n // Compared against the focus below, so the point needs its own element.\n const [element, setElement] = useState<HTMLDivElement | null>(null)\n const xInputRef = useRef<HTMLInputElement>(null)\n const yInputRef = useRef<HTMLInputElement>(null)\n\n // What the editor needs to move this point along with the rest of a\n // selection. Rewritten after every render rather than kept in the registry\n // itself: the value changes on every frame of a drag.\n const registration = useRef<PointsEditorPoint>({\n value,\n min,\n max,\n readonly: inactive,\n onChange: onChange ? handleChange : undefined,\n element,\n wheel,\n beforeWheel: () => gesture.pulse('wheel'),\n })\n useEffect(() => {\n registration.current = {\n value,\n min,\n max,\n readonly: inactive,\n onChange: onChange ? handleChange : undefined,\n element,\n wheel,\n beforeWheel: () => gesture.pulse('wheel'),\n }\n })\n\n useEffect(\n () => editor.registerPoint(id, () => registration.current),\n [id, editor],\n )\n\n /** Where the pointer was when the drag started, to measure the move from. */\n const pointerOrigin = useRef<PointPosition | null>(null)\n\n // The value is the position itself: no scaling, and no rounding to a step.\n const { refCallback: dragRefCallback, dragging } =\n useDragValue<HTMLDivElement>({\n axis: POINT_AXIS,\n baseElementRef: containerRef,\n sensitivity: (state) =>\n selectModifier(dragSensitivity, state.event).value,\n cursor: inactive ? undefined : dragCursor,\n shouldStart: () => !disabled,\n // The value is a move rather than a position: the point keeps the offset\n // it was grabbed at, and everything else selected moves with it by the\n // same amount.\n onChange: ([x, y]) => {\n const origin = pointerOrigin.current\n if (!origin) return\n editor.movePointDrag({ x: x - origin.x, y: y - origin.y })\n },\n onDragStart: ([x, y], state) => {\n // The press decides the selection before the snapshot is taken, so it\n // happens here rather than in an onPointerDown: a native listener on\n // the element runs before React's, and the two would disagree.\n editor.beginPointDrag(id, state.event)\n pointerOrigin.current = { x, y }\n xInputRef.current?.focus()\n\n if (!inactive) gesture.hold('pointer')\n },\n onDragEnd: () => {\n pointerOrigin.current = null\n gesture.end()\n },\n })\n\n const refCallback = useComposedRefs<HTMLDivElement>(\n dragRefCallback,\n setElement,\n )\n\n const handleKeyDown = (event: React.KeyboardEvent<HTMLDivElement>) => {\n // The key picks the axis, whichever of the two inputs holds the focus:\n // the focus lands on the x input, so reading the axis off the input would\n // leave the y axis with no keys at all. y grows downwards, so ArrowUp\n // moves the point towards 0.\n const move = arrowKeyMove(event.key)\n if (!move) return\n event.preventDefault()\n if (!onChange || inactive || !keyboard) return\n gesture.pulse('keyboard')\n editor.nudgePoint(\n id,\n move.axis === 0 ? 'x' : 'y',\n move.direction,\n keyboard,\n event,\n )\n }\n\n return (\n // The visual point has two values, so its semantics live on the two range\n // inputs nested inside it rather than on this drag handle.\n // oxlint-disable-next-line jsx-a11y/no-static-element-interactions\n <div\n ref={refCallback}\n className={className}\n data-disabled={disabled ? '' : undefined}\n data-readonly={readOnly ? '' : undefined}\n // A press lands on the point, which cannot hold focus, and the browser\n // answers that by clearing the focus to the body — undoing the focus the\n // drag just gave the input. Taking the focus here keeps it inside, and\n // it is passed on to the input below.\n tabIndex={-1}\n data-dragging={dragging ? '' : undefined}\n data-selected={selected ? '' : undefined}\n style={\n {\n '--color': color,\n // The mechanics of the position below: a point is placed by its\n // position in the container, measured from its own centre.\n position: 'absolute',\n translate: 'var(--translate, -50% -50%)',\n ...style,\n // Where the point is: the value, not a style.\n left: `${value.x * 100}%`,\n top: `${value.y * 100}%`,\n } as CSSProperties\n }\n onPointerDown={onPointerDown}\n onFocus={(event) => {\n // The point itself is not the control: its semantics live on the\n // inputs, so whatever reaches it is handed to the first of them.\n if (!disabled && event.target === event.currentTarget) {\n xInputRef.current?.focus()\n }\n onFocus?.(event)\n }}\n onKeyDown={(event) => {\n handleKeyDown(event)\n onKeyDown?.(event)\n }}\n {...props}\n >\n {(['x', 'y'] as const).map((axis) => (\n <VisuallyHiddenRangeInput\n key={axis}\n ref={axis === 'x' ? xInputRef : yInputRef}\n data-axis={axis}\n value={current[axis]}\n min={min?.[axis] ?? 0}\n max={max?.[axis] ?? 1}\n step=\"any\"\n disabled={disabled}\n aria-readonly={readOnly}\n aria-orientation={axis === 'x' ? 'horizontal' : 'vertical'}\n aria-label={perAxis(ariaLabel, axis) ?? axis}\n aria-valuetext={perAxis(ariaValuetext, axis)}\n onChange={(event) => {\n if (readOnly) {\n event.currentTarget.value = String(current[axis])\n return\n }\n const delta = event.currentTarget.valueAsNumber - value[axis]\n editor.nudgeSelection(id, {\n x: axis === 'x' ? delta : 0,\n y: axis === 'y' ? delta : 0,\n })\n }}\n />\n ))}\n {children}\n </div>\n )\n}\n","import { ComponentPropsWithoutRef, CSSProperties } from 'react'\n\nimport { useCheckPlacement } from '../_util/Placement'\n\nimport { usePointsEditorContext } from './context'\n\n/**\n * The box a drag on empty space draws, and what it covers is selected.\n *\n * Nothing is rendered while no drag is running, and leaving it out leaves the\n * editor without one — the selection still works, it just cannot be seen.\n */\nexport function SelectionBox({\n children,\n className,\n style,\n ...props\n}: ComponentPropsWithoutRef<'div'>) {\n useCheckPlacement('PointsEditor.SelectionBox', 'PointsEditor.Container')\n\n const selectionBox = usePointsEditorContext((s) => s.selectionBox)\n if (!selectionBox) return null\n\n return (\n <div\n className={className}\n {...props}\n style={\n {\n // Drawn over the points while a drag runs, and taking none of the\n // pointer: the drag underneath is what it is reporting on.\n position: 'absolute',\n zIndex: 20,\n pointerEvents: 'none',\n ...style,\n // Where the box is and how big it is: the drag, not a style, so it\n // is written after whatever the caller passed.\n left: `${selectionBox.x * 100}%`,\n top: `${selectionBox.y * 100}%`,\n width: `${selectionBox.width * 100}%`,\n height: `${selectionBox.height * 100}%`,\n } as CSSProperties\n }\n >\n {children}\n </div>\n )\n}\n","import {\n ComponentPropsWithoutRef,\n CSSProperties,\n forwardRef,\n ReactNode,\n useEffect,\n useMemo,\n useRef,\n useState,\n} from 'react'\n\nimport {\n createPointsEditor,\n DEFAULT_DRAG_SENSITIVITY,\n type InputEventOption,\n type ModifierValue,\n type SelectionBoxRect,\n} from '@tremolo-ui/dom'\nimport {\n POINTS_EDITOR_DEFAULT_KEYBOARD,\n POINTS_EDITOR_DEFAULT_WHEEL,\n} from '@tremolo-ui/dom/internal'\n\nimport { Background } from './Background'\nimport { Container } from './Container'\nimport { PointsEditorProvider } from './context'\nimport { Point } from './Point'\nimport { SelectionBox } from './SelectionBox'\n\n/** One array for every editor with selection turned off, so memos hold still. */\nconst EMPTY: readonly string[] = []\n\nexport interface PointsEditorProps {\n /**\n * Make the points unchangeable and remove them from the tab order.\n * The parts carry `data-disabled` while it is set.\n */\n disabled?: boolean\n /**\n * Make the points unmovable.\n * The parts carry `data-readonly` while it is set.\n */\n readOnly?: boolean\n\n /**\n * The cursor to show while dragging a point. It is set on the dragged\n * point, so it stays while the pointer is outside it.\n *\n * @default 'grabbing'\n */\n dragCursor?: CSSProperties['cursor']\n\n /**\n * How much one notch of the wheel moves the focused `Point`. Scrolling\n * sideways, or with shift held, moves x; otherwise it moves y.\n *\n * A position runs from 0 to 1, so `['normalized', n]` and `['raw', n]` both\n * move it by `n` of the editor. `null` turns the wheel off, and a `Point`\n * can override it with a `wheel` of its own.\n *\n * @default ['normalized', 0.01]\n */\n wheel?: ModifierValue<InputEventOption> | null\n /**\n * How much one arrow key press moves a `Point`. Left and right move x, up\n * and down move y.\n *\n * A position runs from 0 to 1, so `['normalized', n]` and `['raw', n]` both\n * move it by `n` of the editor; give a map to set an amount per modifier\n * key. `null` turns the arrow keys off, and a `Point` can override it with a\n * `keyboard` of its own.\n *\n * @default { default: ['normalized', 0.01], shift: ['normalized', 0.001] }\n */\n keyboard?: ModifierValue<InputEventOption> | null\n\n /**\n * How much a drag moves a `Point`, per modifier key.\n *\n * `1` is the pointer position itself, which is what dragging a point\n * normally is. **Anything else turns the drag relative**: `0.1` makes the\n * same movement cover a tenth of the editor, so the point stops following\n * the pointer and starts moving a tenth as fast. Shift is bound to `0.1` by\n * default, to match what it does on the arrow keys.\n *\n * Pressing or releasing the key mid-drag does not disturb the point: the\n * travel so far is kept and the new sensitivity applies from there. **The\n * pointer and the point stay apart for the rest of the drag** — snapping\n * them back together on release would move the point nobody asked to move.\n *\n * @default { default: 1, shift: 0.1 }\n */\n dragSensitivity?: ModifierValue<number>\n\n /**\n * Let points be selected, and a selection be moved as one.\n *\n * Off by default, because it changes what a press and a drag mean: a press\n * on empty space starts a selection box rather than doing nothing, and a drag\n * on a point moves everything else that is selected. An editor whose points\n * each mean something different — the four handles of an ADSR envelope, say\n * — has nothing to gain from moving them together.\n *\n * **A selection calls `onChange` on several points in the same tick**, so\n * each of them has to update from the previous state rather than from a\n * value captured in the render:\n *\n * ```jsx\n * onChange={(v) => setPoints((prev) => ({ ...prev, [id]: v }))}\n * ```\n *\n * Written the other way round — `setPoints({ ...points, [id]: v })` — every\n * call but the last is thrown away, and only one point appears to move.\n *\n * @default false\n */\n selectable?: boolean\n\n /**\n * Ids of the selected points, to hold the selection yourself. Leave it out\n * and the editor keeps its own.\n *\n * A `Point` takes its id from its `id` prop, or generates one that lasts as\n * long as it is mounted.\n */\n selection?: string[]\n /** The selection to start with, when the editor keeps its own. */\n defaultSelection?: string[]\n /** Called whenever the selection changes, controlled or not. */\n onSelectionChange?: (selection: string[]) => void\n\n /**\n * The editor renders exactly what you compose here; there is no default\n * markup to fall back to.\n *\n * @example\n * <PointsEditor.Root>\n * <PointsEditor.Background>\n * <svg viewBox=\"0 0 200 100\">...</svg>\n * </PointsEditor.Background>\n * <PointsEditor.Container>\n * {points.map((point, i) => (\n * <PointsEditor.Point key={i} value={point} onChange={...} />\n * ))}\n * </PointsEditor.Container>\n * </PointsEditor.Root>\n */\n children: ReactNode\n}\n\ntype Props = PointsEditorProps &\n Omit<ComponentPropsWithoutRef<'div'>, keyof PointsEditorProps>\n\nexport const Root = /* @__PURE__ */ forwardRef<HTMLDivElement, Props>(\n (\n {\n disabled = false,\n readOnly = false,\n wheel = POINTS_EDITOR_DEFAULT_WHEEL,\n keyboard = POINTS_EDITOR_DEFAULT_KEYBOARD,\n dragSensitivity = DEFAULT_DRAG_SENSITIVITY,\n selectable = false,\n selection: selectionProp,\n defaultSelection,\n onSelectionChange,\n dragCursor = 'grabbing',\n style,\n className,\n children,\n ...props\n },\n forwardedRef,\n ) => {\n const containerRef = useRef<HTMLDivElement>(null)\n // Picked apart so that the memo below depends on values rather than on the\n // object literal a caller writes inline, which is new on every render.\n\n // --- selection ---\n // The selection lives here, where React state can hold it; which points a\n // press or a box selects, and how a selection moves, is the core's.\n const controlled = selectionProp !== undefined\n const [ownSelection, setOwnSelection] = useState<string[]>(\n defaultSelection ?? [],\n )\n // Nothing is selected while selection is off, so a drag picks up only the\n // point it started on and `data-selected` never turns on.\n const selection = selectable ? (selectionProp ?? ownSelection) : EMPTY\n\n const [selectionBox, setSelectionBox] = useState<SelectionBoxRect | null>(\n null,\n )\n\n const [editor] = useState(() =>\n createPointsEditor({ onSelectionBoxChange: setSelectionBox }),\n )\n\n // Drags read the selection from a native event handler, which runs after\n // the commit, so pushing it from an effect is current by the time it\n // matters. The handler goes the same way: it closes over this render.\n useEffect(() => {\n editor.update({\n selectable,\n selection,\n onSelectionChange: (next) => {\n if (!controlled) setOwnSelection(next)\n onSelectionChange?.(next)\n },\n })\n })\n\n useEffect(() => () => editor.destroy(), [editor])\n\n const context = useMemo(\n () => ({\n disabled,\n readOnly,\n wheel,\n keyboard,\n dragSensitivity,\n dragCursor,\n containerRef,\n selectable,\n selection,\n editor,\n selectionBox,\n }),\n [\n disabled,\n readOnly,\n wheel,\n keyboard,\n dragSensitivity,\n dragCursor,\n selectable,\n selection,\n editor,\n selectionBox,\n ],\n )\n\n return (\n <PointsEditorProvider value={context}>\n <div\n ref={forwardedRef}\n className={className}\n data-disabled={disabled ? '' : undefined}\n data-readonly={readOnly ? '' : undefined}\n style={\n {\n // The layers inside are placed against this box.\n position: 'relative',\n ...style,\n } as CSSProperties\n }\n {...props}\n >\n {children}\n </div>\n </PointsEditorProvider>\n )\n },\n)\n\n/**\n * Multiple Point Controller\n */\nexport const PointsEditor = {\n Root,\n Background,\n Container,\n Point,\n SelectionBox,\n}\n\nexport {\n usePointsEditorContext,\n type PointsEditorContextValue,\n} from './context'\nexport { type PointsEditorPointProps } from './Point'\n","import { createContext, RefObject, useContext } from 'react'\n\nimport type { Scale } from '@tremolo-ui/functions'\n\nimport type { SliderThumbMethods } from './Thumb'\n\nexport type SliderContextValue = {\n value: number\n min: number\n max: number\n step: number\n /** How the value is distributed across the travel. */\n scale: Scale\n orientation: 'horizontal' | 'vertical'\n reverse: boolean\n disabled: boolean\n readOnly: boolean\n onChange?: (value: number) => void\n /**\n * Position of the thumb, 0-100, already accounting for the display direction\n * implied by `orientation` and `reverse`.\n */\n percent: number\n\n /** `Track` registers its element here; `Root` normalizes the pointer against it. */\n trackRef: RefObject<HTMLDivElement | null>\n /** `Thumb` registers itself here; `Root` focuses it when a drag starts. */\n thumbRef: RefObject<SliderThumbMethods | null>\n}\n\nconst SliderContext = /* @__PURE__ */ createContext<SliderContextValue | null>(\n null,\n)\n\nexport const SliderProvider = SliderContext.Provider\n\n/**\n * Everything here is derived during render, so there is no state to keep in\n * sync: `value` comes from the props of `Root` and the rest follows from it.\n */\nexport function useSliderContext(): SliderContextValue\nexport function useSliderContext<T>(\n selector: (state: SliderContextValue) => T,\n): T\nexport function useSliderContext<T>(\n selector?: (state: SliderContextValue) => T,\n) {\n const context = useContext(SliderContext)\n if (!context) throw new Error('Missing SliderContext.Provider in the tree')\n return selector ? selector(context) : context\n}\n","import { ComponentPropsWithoutRef, CSSProperties } from 'react'\n\nimport { valuePercent } from '@tremolo-ui/dom'\nimport { cssLength } from '@tremolo-ui/dom/internal'\n\nimport { useCheckPlacement } from '../_util/Placement'\n\nimport { useSliderContext } from './context'\n\nimport type { CSSVariables } from '../../css-variables'\n\nexport interface SliderMarksOptionProps {\n /** Where the mark sits, on the same scale as the thumb. */\n value: number\n\n /**\n * Draw the mark itself. Turn it off to show the label alone.\n * @default true\n */\n mark?: boolean\n /**\n * Text shown next to the mark. Leave it out to show the value; `null` leaves\n * the label out, and an empty string draws an empty one.\n */\n label?: number | string | null\n /** Thickness of the mark. Sets `--thickness`. */\n thickness?: number | string\n /** Length of the mark. Sets `--length`. */\n length?: number | string\n /** Space between the mark and the label. Sets `--gap`. */\n gap?: number | string\n /** Classes for the mark and the label inside the option. */\n classes?: {\n mark?: string\n label?: string\n }\n /** Styles for the mark and the label inside the option. */\n styles?: {\n mark?: CSSProperties\n label?: CSSProperties\n }\n\n style?: CSSProperties &\n CSSVariables<'thickness' | 'length' | 'gap' | 'translate'>\n}\n\nexport function MarksOption({\n value,\n mark = true,\n label,\n thickness,\n length,\n gap,\n classes,\n styles,\n className,\n style,\n ...props\n}: SliderMarksOptionProps &\n Omit<ComponentPropsWithoutRef<'div'>, keyof SliderMarksOptionProps>) {\n useCheckPlacement('Slider.MarksOption', 'Slider.Marks')\n\n const min = useSliderContext((s) => s.min)\n const max = useSliderContext((s) => s.max)\n const scale = useSliderContext((s) => s.scale)\n const vertical = useSliderContext((s) => s.orientation === 'vertical')\n const reverse = useSliderContext((s) => s.reverse)\n\n // The marks have to sit on the same curve the thumb runs along.\n const percent = valuePercent(value, { min, max, scale }, vertical !== reverse)\n\n return (\n <div\n className={className}\n style={\n {\n // The mark and the label read these, so they are set once here.\n '--thickness': cssLength(thickness),\n '--length': cssLength(length),\n '--gap': cssLength(gap),\n // The mechanics of the position below, as on the thumb.\n position: 'absolute',\n translate: vertical\n ? 'var(--translate, 0 -50%)'\n : 'var(--translate, -50% 0)',\n zIndex: 10,\n ...style,\n // Where the mark belongs on the track: the value, not a style.\n left: !vertical ? `${percent}%` : undefined,\n top: vertical ? `${percent}%` : undefined,\n } as CSSProperties\n }\n data-orientation={vertical ? 'vertical' : 'horizontal'}\n {...props}\n >\n {mark && (\n <div\n className={classes?.mark}\n style={styles?.mark}\n data-orientation={vertical ? 'vertical' : 'horizontal'}\n ></div>\n )}\n {label !== null && (\n <div className={classes?.label} style={styles?.label}>\n {label ?? value}\n </div>\n )}\n </div>\n )\n}\n","import { ComponentPropsWithoutRef, CSSProperties } from 'react'\n\nimport { type MarksOptions } from '@tremolo-ui/dom'\nimport { cssLength, sliderMarks } from '@tremolo-ui/dom/internal'\n\nimport { Placement } from '../_util/Placement'\n\nimport { useSliderContext } from './context'\nimport { MarksOption } from './MarksOption'\n\nimport type { CSSVariables } from '../../css-variables'\n\nexport interface SliderMarksProps {\n /**\n * Space between the marks and the track. Sets `--gap`; the theme's own\n * spacing stands when this is omitted.\n */\n gap?: number | string\n /**\n * Build the marks instead of writing `Slider.MarksOption` out: a number puts\n * one every that many, `'step'` one every `step`, and `{ per, mark, label }`\n * also turns the mark or the label off for all of them. `children` is\n * ignored while it is set.\n *\n * `'step'` makes one per step, which is a great many for a fine `step`.\n */\n options?: MarksOptions\n\n style?: CSSProperties & CSSVariables<'gap'>\n}\n\nexport function Marks({\n gap,\n options,\n children,\n className,\n style,\n ...props\n}: SliderMarksProps &\n Omit<ComponentPropsWithoutRef<'div'>, keyof SliderMarksProps>) {\n const min = useSliderContext((s) => s.min)\n const max = useSliderContext((s) => s.max)\n const step = useSliderContext((s) => s.step)\n const vertical = useSliderContext((s) => s.orientation === 'vertical')\n const reverse = useSliderContext((s) => s.reverse)\n\n const optionsList = options ? sliderMarks(options, min, max, step) : []\n if (vertical !== reverse) optionsList.reverse()\n\n return (\n <div\n className={className}\n data-orientation={vertical ? 'vertical' : 'horizontal'}\n style={\n {\n '--gap': cssLength(gap),\n // Each option inside is placed against this box.\n position: 'relative',\n ...style,\n } as CSSProperties\n }\n {...props}\n >\n <Placement name=\"Slider.Marks\">\n {options\n ? optionsList.map(({ value, mark, label }, index) => {\n return (\n <MarksOption\n key={index}\n value={value}\n mark={mark}\n label={label ? undefined : null}\n />\n )\n })\n : children}\n </Placement>\n </div>\n )\n}\n","import {\n ComponentPropsWithoutRef,\n CSSProperties,\n forwardRef,\n Ref,\n useImperativeHandle,\n useRef,\n} from 'react'\n\nimport { useCheckPlacement } from '../_util/Placement'\nimport { VisuallyHiddenRangeInput } from '../_util/VisuallyHiddenRangeInput'\n\nimport { useSliderContext } from './context'\n\nimport type { CSSVariables } from '../../css-variables'\n\nexport interface SliderThumbProps {\n /**\n * Sets `--color`, for the theme to colour the thumb with. The size is not\n * set here but by `--thumb-size` on `Slider.Root`, so that the root can\n * leave the matching room around the track.\n */\n color?: string\n\n style?: CSSProperties & CSSVariables<'color' | 'translate'>\n\n /**\n * Receives `focus` and `blur` for the range input inside, which do nothing\n * while the slider is disabled. `ref` reaches the thumb element itself.\n */\n actionsRef?: Ref<SliderThumbMethods>\n}\n\nexport interface SliderThumbMethods {\n focus: () => void\n blur: () => void\n}\n\ntype Props = SliderThumbProps &\n Omit<ComponentPropsWithoutRef<'div'>, keyof SliderThumbProps>\n\nexport const Thumb = /* @__PURE__ */ forwardRef<HTMLDivElement, Props>(\n function Thumb(\n {\n color,\n children,\n className,\n style,\n // Pulled out of `props` so that they land on the range input rather than\n // on the thumb: the input is the control, it takes the focus, and a name\n // on a plain div would be ignored. No declaration in `SliderThumbProps`\n // is needed for this — they are already on the div attributes it is\n // merged with, with the same types.\n 'aria-label': ariaLabel,\n 'aria-labelledby': ariaLabelledby,\n 'aria-describedby': ariaDescribedby,\n 'aria-valuetext': ariaValuetext,\n actionsRef,\n ...props\n },\n forwardedRef,\n ) {\n const inputRef = useRef<HTMLInputElement>(null)\n const {\n value,\n min,\n max,\n step,\n orientation,\n disabled,\n readOnly,\n onChange,\n percent,\n thumbRef,\n } = useSliderContext()\n\n // The thumb is positioned against the track.\n useCheckPlacement('Slider.Thumb', 'Slider.Track')\n\n const methods = () => ({\n focus() {\n if (!disabled) inputRef.current?.focus()\n },\n blur() {\n inputRef.current?.blur()\n },\n })\n\n useImperativeHandle(actionsRef, methods, [disabled])\n // Root focuses the thumb when a drag starts, wherever the user placed it.\n useImperativeHandle(thumbRef, methods, [disabled])\n\n return (\n <div\n ref={forwardedRef}\n className={className}\n data-disabled={disabled ? '' : undefined}\n data-readonly={readOnly ? '' : undefined}\n {...props}\n style={{\n ...{ '--color': color },\n // Not a style but the mechanics of the position below: the thumb is\n // placed by a percentage of the track, which needs it out of flow\n // and measured from its own centre. `--translate` is there for a\n // thumb that should hang off its edge instead.\n position: 'absolute',\n translate: 'var(--translate, -50% -50%)',\n zIndex: 100,\n ...style,\n // Where the thumb sits is the component's decision, not a style: a\n // `left` from the caller would take it off the track, so it is\n // written after theirs.\n top: orientation === 'vertical' ? `${percent}%` : '50%',\n left: orientation === 'vertical' ? '50%' : `${percent}%`,\n }}\n >\n <VisuallyHiddenRangeInput\n ref={inputRef}\n value={value}\n min={min}\n max={max}\n step={step}\n disabled={disabled}\n aria-readonly={readOnly}\n aria-orientation={orientation}\n aria-label={ariaLabel}\n aria-labelledby={ariaLabelledby}\n aria-describedby={ariaDescribedby}\n aria-valuetext={ariaValuetext}\n onChange={(event) => {\n if (readOnly) {\n event.currentTarget.value = String(value)\n return\n }\n onChange?.(event.currentTarget.valueAsNumber)\n }}\n />\n {children}\n </div>\n )\n },\n)\n","import { ComponentPropsWithoutRef, CSSProperties, forwardRef } from 'react'\n\nimport { cssLength } from '@tremolo-ui/dom/internal'\n\nimport { useComposedRefs } from '../../compose-refs'\nimport { Placement } from '../_util/Placement'\n\nimport { useSliderContext } from './context'\n\nimport type { CSSVariables } from '../../css-variables'\n\nexport interface SliderTrackProps {\n /**\n * How long the track is along the axis the slider runs. Sets `--length`;\n * the size the theme gives it stands when this is omitted.\n */\n length?: number | string\n /** How thick the track is across that axis. Sets `--thickness`. */\n thickness?: number | string\n\n /** Colour of the part from `min` to the value. Sets `--active-color`. */\n activeColor?: string\n /** Colour of the rest of the track. Sets `--inactive-color`. */\n inactiveColor?: string\n\n style?: CSSProperties &\n CSSVariables<\n 'length' | 'thickness' | 'active-color' | 'inactive-color' | 'percent'\n >\n}\n\ntype Props = SliderTrackProps &\n Omit<ComponentPropsWithoutRef<'div'>, keyof SliderTrackProps>\n\nexport const Track = /* @__PURE__ */ forwardRef<HTMLDivElement, Props>(\n function Track(\n {\n length,\n thickness,\n activeColor,\n inactiveColor,\n children,\n className,\n style,\n ...props\n },\n forwardedRef,\n ) {\n const { orientation, reverse, disabled, percent, trackRef } =\n useSliderContext()\n\n // The track is what the pointer position is normalized against, so the\n // context ref is composed with any ref the caller passed.\n const composedRef = useComposedRefs<HTMLDivElement>(forwardedRef, trackRef)\n\n return (\n <div\n ref={composedRef}\n className={className}\n data-disabled={disabled ? '' : undefined}\n data-orientation={orientation}\n // Which end the value grows from. `percent` is already the position on\n // screen, so this only says which side of it is the filled one.\n data-flipped={(orientation === 'vertical') !== reverse ? '' : undefined}\n style={\n {\n '--active-color': activeColor,\n '--inactive-color': inactiveColor,\n '--length': cssLength(length),\n '--thickness': cssLength(thickness),\n // Where the value sits, for the theme to paint the fill with. The\n // component draws nothing itself: this is the one number CSS cannot\n // work out on its own.\n '--percent': `${percent}%`,\n // The thumb inside is placed against this box.\n position: 'relative',\n ...style,\n } as CSSProperties\n }\n {...props}\n >\n <Placement name=\"Slider.Track\">{children}</Placement>\n </div>\n )\n },\n)\n","import React, {\n ComponentPropsWithoutRef,\n CSSProperties,\n forwardRef,\n ReactNode,\n Ref,\n useCallback,\n useImperativeHandle,\n useMemo,\n useRef,\n} from 'react'\n\nimport {\n type AxisOptions,\n type ChangeSource,\n DEFAULT_DRAG_SENSITIVITY,\n DEFAULT_KEYBOARD_OPTIONS,\n DEFAULT_WHEEL_OPTIONS,\n type InputEventOption,\n type ModifierValue,\n valuePercent,\n type XY,\n} from '@tremolo-ui/dom'\nimport {\n applyDelta,\n arrowKeyDirection,\n selectModifier,\n wheelDirection,\n} from '@tremolo-ui/dom/internal'\nimport { linearScale, type Scale } from '@tremolo-ui/functions'\n\nimport { useComposedRefs } from '../../compose-refs'\nimport { useChangeGesture } from '../../hooks/_internal/useChangeGesture'\nimport { useCheckSteps } from '../../hooks/_internal/useCheckSteps'\nimport { useDragValue } from '../../hooks/useDragValue'\nimport { useWheel } from '../../hooks/useWheel'\n\nimport { SliderProvider } from './context'\nimport { Marks } from './Marks'\nimport { MarksOption } from './MarksOption'\nimport { Thumb, SliderThumbMethods } from './Thumb'\nimport { Track } from './Track'\n\nexport interface SliderProps {\n /** The current value. The slider shows only this, so update it from `onChange`. */\n value: number\n /** The value at the start of the travel. */\n min: number\n /** The value at the end of the travel. */\n max: number\n\n /**\n * Granularity of the value. A drag, the wheel and the arrow keys snap it to\n * multiples of `step`.\n *\n * @default 1\n */\n step?: number\n /**\n * How the value is distributed across the travel.\n *\n * Pick one of the scales from `@tremolo-ui/functions`: `linearScale`,\n * `exponentialScale`, `curveScale(n)`, `symmetricSkewScale(n)`, or\n * `skewScale(n)` for a value that has to match a JUCE parameter.\n *\n * @default linearScale\n */\n scale?: Scale\n /**\n * Which way the slider runs. A vertical slider grows upwards. Every part\n * carries the same value as `data-orientation`, and the range input inside\n * the thumb takes its `aria-orientation` from it.\n *\n * @default 'horizontal'\n */\n orientation?: 'horizontal' | 'vertical'\n /**\n * Grow the value the other way: leftwards, or downwards when vertical.\n * The arrow keys follow the direction on screen.\n *\n * @default false\n */\n reverse?: boolean\n\n /**\n * The cursor to show while dragging. It is set on the dragged element, so it\n * stays while the pointer is outside the slider.\n *\n * @default 'pointer'\n */\n dragCursor?: CSSProperties['cursor']\n /**\n * How much one notch of the wheel moves the value. It only acts while the\n * focus is inside, so that scrolling the page past the slider leaves it\n * alone.\n *\n * `['raw', n]` moves the value by `n`, and `['normalized', n]` by `n` of the\n * range between `min` and `max`. The result is snapped to `step`, except for\n * an amount set on a modifier key (`{ default: …, shift: … }`). `null` turns\n * the wheel off.\n *\n * @default ['raw', 1]\n */\n wheel?: ModifierValue<InputEventOption> | null\n /**\n * How much a drag moves the value, per modifier key.\n *\n * `1` is the pointer position itself, which is what a drag normally is here.\n * **Anything else turns the drag relative**: `0.1` makes the same movement\n * cover a tenth of the travel, so the value stops following the pointer and\n * starts moving a tenth as fast. Shift is bound to `0.1` by default, to\n * match what it does on the arrow keys.\n *\n * Pressing or releasing the key mid-drag does not disturb the value: the\n * travel so far is kept and the new sensitivity applies from there. **The\n * pointer and the value stay apart for the rest of the drag** — snapping\n * them back together on release would move the value nobody asked to move.\n *\n * @default { default: 1, shift: 0.1 }\n */\n dragSensitivity?: ModifierValue<number>\n\n /**\n * How much one arrow key press moves the value.\n *\n * `['raw', n]` moves the value by `n`, and `['normalized', n]` by `n` of the\n * range between `min` and `max`. The result is snapped to `step`, except for\n * an amount set on a modifier key, which is what lets shift move off the\n * grid. `null` turns the arrow keys off.\n *\n * The default moves by 1, and by 0.1 with shift. With a `step` above 1, raise\n * the amount to match: 1 would round straight back to where it started, and\n * a development build warns about it.\n *\n * @default { default: ['raw', 1], shift: ['raw', 0.1] }\n */\n keyboard?: ModifierValue<InputEventOption> | null\n\n /**\n * Make the slider unchangeable and remove it from the tab order.\n * The parts carry `data-disabled` while it is set.\n */\n disabled?: boolean\n /**\n * Make the value unchangeable.\n * The parts carry `data-readonly` while it is set.\n */\n readOnly?: boolean\n /** Called with the new value when a drag, the wheel or an arrow key moves it. */\n onChange?: (value: number) => void\n /**\n * Called when a change of the value starts — a press on the track, the first wheel notch\n * or arrow key — with the value\n * before it and what it is made with. A host recording automation can treat\n * the control as touched from here until `onChangeEnd`.\n */\n onChangeStart?: (value: number, source: ChangeSource) => void\n /**\n * Called when the change ends, with the value it ended on: on release, or\n * `changeEndDelay` after the last wheel notch or arrow key.\n */\n onChangeEnd?: (value: number, source: ChangeSource) => void\n /**\n * How long after the last wheel notch or arrow key the change counts as\n * over, in milliseconds. Neither has an event that says it is done.\n *\n * @default 500\n */\n changeEndDelay?: number\n /**\n * The slider renders exactly what you compose here; there is no default\n * markup to fall back to.\n *\n * @example\n * <Slider.Root value={value} min={0} max={100} onChange={setValue}>\n * <Slider.Track>\n * <Slider.Thumb aria-label=\"Level\" />\n * </Slider.Track>\n * </Slider.Root>\n */\n children: ReactNode\n\n /**\n * Receives `focus` and `blur`, which act on the range input inside the thumb —\n * the element that takes the focus — and do nothing while the slider is\n * disabled. `ref` reaches the root element itself.\n */\n actionsRef?: Ref<SliderMethods>\n}\n\nexport interface SliderMethods {\n focus: () => void\n blur: () => void\n}\n\ntype Props = SliderProps &\n Omit<ComponentPropsWithoutRef<'div'>, keyof SliderProps>\n\n/**\n * The wheel only acts while the focus is inside, so that scrolling a page past\n * the control does not change its value.\n */\nconst WHEEL_OPTIONS = { requireFocus: true }\n\nexport const Root = /* @__PURE__ */ forwardRef<HTMLDivElement, Props>(\n (\n {\n value,\n min,\n max,\n step = 1,\n scale = linearScale,\n orientation = 'horizontal',\n reverse = false,\n dragCursor = 'pointer',\n wheel = DEFAULT_WHEEL_OPTIONS,\n keyboard = DEFAULT_KEYBOARD_OPTIONS,\n dragSensitivity = DEFAULT_DRAG_SENSITIVITY,\n disabled = false,\n readOnly = false,\n onChange,\n onChangeStart,\n onChangeEnd,\n changeEndDelay,\n className,\n style,\n children,\n onFocus,\n onBlur,\n onPointerDown,\n onKeyDown,\n actionsRef,\n ...props\n }: Props,\n forwardedRef,\n ) => {\n // -- state and ref ---\n const trackRef = useRef<HTMLDivElement>(null)\n const thumbRef = useRef<SliderThumbMethods>(null)\n // --- interpret props ---\n const inactive = disabled || readOnly\n\n // Measured from the left or the top, as CSS places things:\n // normal -> normal (right)\n // vertical -> reversed (up)\n // reverse -> reversed (left)\n // vertical & reverse -> normal (down)\n const vertical = orientation === 'vertical'\n const displayReversed = vertical !== reverse\n const percent = valuePercent(value, { min, max, scale }, displayReversed)\n\n // --- internal functions ---\n // The pointer is normalized against the track on both axes; only the one\n // the slider runs along is read back. `AxisOptions` extends `ValueRange`,\n // so the same object also describes the scaling for `applyDelta`.\n const axis: AxisOptions = useMemo(\n () => ({ min, max, step, scale, reverse: displayReversed }),\n [min, max, step, scale, displayReversed],\n )\n\n useCheckSteps({ component: 'Slider', range: axis, keyboard, wheel })\n\n const gesture = useChangeGesture(\n value,\n { onChangeStart, onChangeEnd, changeEndDelay },\n inactive,\n )\n const change = useCallback(\n (next: number) => {\n gesture.changed(next)\n onChange?.(next)\n },\n [gesture, onChange],\n )\n\n const handleKeyDown = useCallback(\n (event: React.KeyboardEvent<HTMLDivElement>) => {\n const direction = arrowKeyDirection(event.key)\n if (direction === null) return\n event.preventDefault()\n if (!keyboard || !onChange || inactive) return\n gesture.pulse('keyboard')\n change(\n applyDelta(\n value,\n reverse ? -direction : direction,\n keyboard,\n axis,\n event,\n ),\n )\n },\n [keyboard, onChange, inactive, reverse, value, axis, gesture, change],\n )\n\n // --- hooks ---\n const valueOf = (v: XY<number>) => v[vertical ? 1 : 0]\n\n const { refCallback: dragRefCallback } = useDragValue<HTMLDivElement>({\n axis,\n baseElementRef: trackRef,\n cursor: inactive ? undefined : dragCursor,\n shouldStart: () => !inactive,\n sensitivity: (state) =>\n selectModifier(dragSensitivity, state.event).value,\n updateOnPointerDown: true,\n onChange: (v) => {\n if (inactive) return\n change(valueOf(v))\n },\n onDragStart: () => {\n if (inactive) return\n gesture.hold('pointer')\n thumbRef.current?.focus()\n },\n onDragEnd: () => gesture.end(),\n })\n\n const wheelRefCallback = useWheel<HTMLDivElement>((event) => {\n if (!wheel || !onChange || inactive) return\n // A notch the slider does not read — a sideways scroll on a vertical\n // slider — is left to the page rather than swallowed.\n const direction = wheelDirection(event, { horizontal: !vertical })\n if (direction === null) return\n event.preventDefault()\n gesture.pulse('wheel')\n change(\n applyDelta(value, reverse ? -direction : direction, wheel, axis, event),\n )\n }, WHEEL_OPTIONS)\n\n // Composed once, so React attaches the refs a single time instead of\n // detaching and re-attaching on every render.\n const rootRefCallback = useComposedRefs<HTMLDivElement>(\n forwardedRef,\n dragRefCallback,\n wheelRefCallback,\n )\n\n const context = useMemo(\n () => ({\n value,\n min,\n max,\n step,\n scale,\n orientation,\n reverse,\n disabled,\n readOnly,\n onChange,\n percent,\n trackRef,\n thumbRef,\n }),\n [\n value,\n min,\n max,\n step,\n scale,\n orientation,\n reverse,\n disabled,\n readOnly,\n onChange,\n percent,\n ],\n )\n\n useImperativeHandle(actionsRef, () => {\n return {\n focus() {\n if (!disabled) thumbRef.current?.focus()\n },\n blur() {\n thumbRef.current?.blur()\n },\n }\n }, [disabled])\n\n return (\n <SliderProvider value={context}>\n {/* oxlint-disable-next-line jsx-a11y/no-noninteractive-element-interactions -- the group is the pointer and keyboard event area, while the nested range input carries its control semantics */}\n <div\n className={className}\n ref={rootRefCallback}\n role=\"group\"\n // A press lands on the track or the thumb, neither of which can hold\n // focus, and the browser answers that by clearing the focus to the\n // body — undoing the focus the drag just gave the input. Taking the\n // focus here keeps it inside, and `onFocus` passes it to the input.\n tabIndex={-1}\n data-orientation={orientation}\n data-disabled={disabled ? '' : undefined}\n data-readonly={readOnly ? '' : undefined}\n style={style}\n onPointerDown={onPointerDown}\n onKeyDown={(event) => {\n handleKeyDown(event)\n onKeyDown?.(event)\n }}\n onFocus={(event) => {\n if (!disabled) thumbRef.current?.focus()\n onFocus?.(event)\n }}\n onBlur={(event) => {\n thumbRef.current?.blur()\n onBlur?.(event)\n }}\n {...props}\n >\n {children}\n </div>\n </SliderProvider>\n )\n },\n)\n\n/**\n * Customizable slider\n */\nexport const Slider = {\n Root,\n Thumb,\n Track,\n Marks,\n MarksOption,\n}\n\nexport { useSliderContext, type SliderContextValue } from './context'\nexport { type SliderThumbMethods, type SliderThumbProps } from './Thumb'\nexport { type SliderTrackProps } from './Track'\nexport { type SliderMarksProps } from './Marks'\nexport { type SliderMarksOptionProps } from './MarksOption'\n","import { createContext, RefObject, useContext } from 'react'\n\nimport { type XY } from '@tremolo-ui/dom'\nimport type { Scale } from '@tremolo-ui/functions'\n\nimport type { XYPadThumbMethods } from './Thumb'\n\nexport type XYPadContextValue = {\n value: XY<number>\n min: XY<number>\n max: XY<number>\n step: XY<number>\n /** How the value of each axis is distributed across the travel. */\n scale: XY<Scale>\n reverse: XY<boolean>\n disabled: boolean\n readOnly: boolean\n onChange?: (value: XY<number>) => void\n\n /**\n * Position of the thumb, 0-100 per axis, already accounting for `reverse`.\n */\n percent: XY<number>\n\n /** `Area` registers its element here; `Root` normalizes the pointer against it. */\n areaRef: RefObject<HTMLDivElement | null>\n /** `Thumb` registers itself here; `Root` focuses it when a drag starts. */\n thumbRef: RefObject<XYPadThumbMethods | null>\n}\n\nconst XYPadContext = /* @__PURE__ */ createContext<XYPadContextValue | null>(\n null,\n)\n\nexport const XYPadProvider = XYPadContext.Provider\n\n/**\n * Everything here is derived during render, so there is no state to keep in\n * sync: `value` comes from the props of `Root` and the rest follows from it.\n */\nexport function useXYPadContext(): XYPadContextValue\nexport function useXYPadContext<T>(selector: (state: XYPadContextValue) => T): T\nexport function useXYPadContext<T>(selector?: (state: XYPadContextValue) => T) {\n const context = useContext(XYPadContext)\n if (!context) throw new Error('Missing XYPadContext.Provider in the tree')\n return selector ? selector(context) : context\n}\n","import { ComponentPropsWithoutRef, CSSProperties, forwardRef } from 'react'\n\nimport { useComposedRefs } from '../../compose-refs'\nimport { Placement } from '../_util/Placement'\n\nimport { useXYPadContext } from './context'\n\nexport const Area = /* @__PURE__ */ forwardRef<\n HTMLDivElement,\n ComponentPropsWithoutRef<'div'>\n>(function Area({ children, className, style, ...props }, forwardedRef) {\n const { areaRef } = useXYPadContext()\n\n // The area is what the pointer position is normalized against, so the\n // context ref is composed with any ref the caller passed.\n const composedRef = useComposedRefs<HTMLDivElement>(forwardedRef, areaRef)\n\n return (\n <div\n ref={composedRef}\n className={className}\n style={\n {\n // The thumb inside is placed against this box.\n position: 'relative',\n ...style,\n } as CSSProperties\n }\n {...props}\n >\n <Placement name=\"XYPad.Area\">{children}</Placement>\n </div>\n )\n})\n","import {\n AriaAttributes,\n ComponentPropsWithoutRef,\n CSSProperties,\n forwardRef,\n Ref,\n useImperativeHandle,\n useRef,\n} from 'react'\n\nimport { type XYInput } from '@tremolo-ui/dom'\nimport { toXY } from '@tremolo-ui/dom/internal'\n\nimport { useCheckPlacement } from '../_util/Placement'\nimport { VisuallyHiddenRangeInput } from '../_util/VisuallyHiddenRangeInput'\n\nimport { useXYPadContext } from './context'\n\nimport type { CSSVariables } from '../../css-variables'\n\nexport interface XYPadThumbProps {\n /**\n * Sets `--color`, for the theme to colour the thumb with. The size is not\n * set here but by `--thumb-size` on `XYPad.Root`, so that the root can leave\n * the matching room around the area.\n */\n color?: string\n\n /**\n * The accessible name of each axis, as `[x, y]`. There are two range inputs\n * inside the thumb, so each takes a name of its own; a single string names\n * them both, which is rarely what you want.\n */\n 'aria-label'?: XYInput<AriaAttributes['aria-label']>\n /** The ids of what labels each axis, as `[x, y]` or one for both. */\n 'aria-labelledby'?: XYInput<AriaAttributes['aria-labelledby']>\n /** The ids of what describes each axis, as `[x, y]` or one for both. */\n 'aria-describedby'?: XYInput<AriaAttributes['aria-describedby']>\n /**\n * What the value of each axis means, when the number does not say it, as\n * `[x, y]` or one for both.\n */\n 'aria-valuetext'?: XYInput<AriaAttributes['aria-valuetext']>\n\n style?: CSSProperties & CSSVariables<'color' | 'translate'>\n\n /**\n * Receives `focus` and `blur` for the range inputs inside, which do nothing\n * while the pad is disabled. `ref` reaches the thumb element itself.\n */\n actionsRef?: Ref<XYPadThumbMethods>\n}\n\nexport interface XYPadThumbMethods {\n focus: () => void\n blur: () => void\n}\n\ntype Props = XYPadThumbProps &\n Omit<ComponentPropsWithoutRef<'div'>, keyof XYPadThumbProps>\n\nexport const Thumb = /* @__PURE__ */ forwardRef<HTMLDivElement, Props>(\n function Thumb(\n {\n color,\n children,\n className,\n style,\n 'aria-label': ariaLabel,\n 'aria-labelledby': ariaLabelledby,\n 'aria-describedby': ariaDescribedby,\n 'aria-valuetext': ariaValuetext,\n actionsRef,\n ...props\n },\n forwardedRef,\n ) {\n const xInputRef = useRef<HTMLInputElement>(null)\n const yInputRef = useRef<HTMLInputElement>(null)\n const {\n value,\n min,\n max,\n step,\n disabled,\n readOnly,\n onChange,\n percent,\n thumbRef,\n } = useXYPadContext()\n\n // The thumb is positioned against the area.\n useCheckPlacement('XYPad.Thumb', 'XYPad.Area')\n\n // Per axis, since the thumb holds one input for each.\n const labels = toXY(ariaLabel)\n const labelledby = toXY(ariaLabelledby)\n const describedby = toXY(ariaDescribedby)\n const valueText = toXY(ariaValuetext)\n\n const methods = () => ({\n focus() {\n if (!disabled) xInputRef.current?.focus()\n },\n blur() {\n xInputRef.current?.blur()\n yInputRef.current?.blur()\n },\n })\n\n useImperativeHandle(actionsRef, methods, [disabled])\n // Root focuses the thumb when a drag starts, wherever the user placed it.\n useImperativeHandle(thumbRef, methods, [disabled])\n\n return (\n <div\n ref={forwardedRef}\n className={className}\n data-disabled={disabled ? '' : undefined}\n data-readonly={readOnly ? '' : undefined}\n {...props}\n style={{\n ...{ '--color': color },\n // Not a style but the mechanics of the position below: the thumb is\n // placed by a percentage of the area, which needs it out of flow and\n // measured from its own centre.\n position: 'absolute',\n translate: 'var(--translate, -50% -50%)',\n zIndex: 100,\n ...style,\n // Where the thumb sits is the component's decision, not a style: a\n // `left` from the caller would take it off the area, so it is written\n // after theirs.\n left: `${percent[0]}%`,\n top: `${percent[1]}%`,\n }}\n >\n {([0, 1] as const).map((axis) => (\n <VisuallyHiddenRangeInput\n key={axis}\n ref={axis === 0 ? xInputRef : yInputRef}\n data-axis={axis === 0 ? 'x' : 'y'}\n value={value[axis]}\n min={min[axis]}\n max={max[axis]}\n step={step[axis]}\n disabled={disabled}\n aria-readonly={readOnly}\n aria-orientation={axis === 0 ? 'horizontal' : 'vertical'}\n aria-label={labels[axis] ?? (axis === 0 ? 'x' : 'y')}\n aria-labelledby={labelledby[axis]}\n aria-describedby={describedby[axis]}\n aria-valuetext={valueText[axis]}\n onChange={(event) => {\n if (readOnly) {\n event.currentTarget.value = String(value[axis])\n return\n }\n const next = [...value] as [number, number]\n next[axis] = event.currentTarget.valueAsNumber\n onChange?.(next)\n }}\n />\n ))}\n {children}\n </div>\n )\n },\n)\n","import {\n ComponentPropsWithoutRef,\n CSSProperties,\n forwardRef,\n ReactNode,\n Ref,\n useCallback,\n useImperativeHandle,\n useMemo,\n useRef,\n} from 'react'\n\nimport {\n type AxisOptions,\n type ChangeSource,\n DEFAULT_DRAG_SENSITIVITY,\n DEFAULT_KEYBOARD_OPTIONS,\n DEFAULT_WHEEL_OPTIONS,\n InputEventOption,\n ModifierState,\n type ModifierValue,\n valuePercent,\n type XY,\n type XYInput,\n} from '@tremolo-ui/dom'\nimport {\n applyDelta,\n arrowKeyMove,\n selectModifier,\n toXY,\n wheelMove,\n} from '@tremolo-ui/dom/internal'\nimport { linearScale, type Scale } from '@tremolo-ui/functions'\n\nimport { useComposedRefs } from '../../compose-refs'\nimport { useChangeGesture } from '../../hooks/_internal/useChangeGesture'\nimport { useCheckSteps } from '../../hooks/_internal/useCheckSteps'\nimport { useDragValue } from '../../hooks/useDragValue'\nimport { useWheel } from '../../hooks/useWheel'\n\nimport { Area } from './Area'\nimport { XYPadProvider } from './context'\nimport { Thumb, XYPadThumbMethods } from './Thumb'\n\n/**\n * Two-dimensional slider component.\n *\n * The per-axis settings mirror `Slider`, given as `[x, y]` tuples. A plain\n * value applies to both axes.\n */\nexport interface XYPadProps {\n /** The current value as `[x, y]`. The pad shows only this, so update it from `onChange`. */\n value: XY<number>\n /** The value at the start of each axis, as `[x, y]` or one number for both. */\n min: XYInput<number>\n /** The value at the end of each axis, as `[x, y]` or one number for both. */\n max: XYInput<number>\n\n /**\n * Granularity of each axis, as `[x, y]` or one number for both. A drag, the\n * wheel and the arrow keys snap the value to multiples of it.\n *\n * @default 1\n */\n step?: XYInput<number>\n /**\n * How the value of each axis is distributed across the travel, as `[x, y]`\n * or one scale for both.\n *\n * Pick one of the scales from `@tremolo-ui/functions`: `linearScale`,\n * `exponentialScale`, `curveScale(n)`, `symmetricSkewScale(n)`, or\n * `skewScale(n)` for a value that has to match a JUCE parameter.\n *\n * @default linearScale\n */\n scale?: XYInput<Scale>\n /**\n * Grow an axis the other way, as `[x, y]` or one for both. By default x grows\n * rightwards and y downwards. The arrow keys follow the direction on screen.\n *\n * @default false\n */\n reverse?: XYInput<boolean>\n\n /**\n * How much one notch of the wheel moves the value. It only acts while the\n * focus is inside, so that scrolling the page past the pad leaves it alone.\n * Scrolling sideways, or with shift held, moves x; otherwise it moves y.\n *\n * `['raw', n]` moves the value by `n`, and `['normalized', n]` by `n` of the\n * range of that axis. The result is snapped to `step`, except for an amount\n * set on a modifier key (`{ default: …, alt: … }`). `null` turns the wheel\n * off.\n *\n * @default ['raw', 1]\n */\n wheel?: ModifierValue<InputEventOption> | null\n /**\n * How much a drag moves the value, per modifier key.\n *\n * `1` is the pointer position itself, which is what a drag normally is here.\n * **Anything else turns the drag relative**: `0.1` makes the same movement\n * cover a tenth of the travel, so the value stops following the pointer and\n * starts moving a tenth as fast. Shift is bound to `0.1` by default, to\n * match what it does on the arrow keys.\n *\n * Pressing or releasing the key mid-drag does not disturb the value: the\n * travel so far is kept and the new sensitivity applies from there. **The\n * pointer and the value stay apart for the rest of the drag** — snapping\n * them back together on release would move the value nobody asked to move.\n *\n * @default { default: 1, shift: 0.1 }\n */\n dragSensitivity?: ModifierValue<number>\n\n /**\n * How much one arrow key press moves the value. Left and right move x, up\n * and down move y.\n *\n * `['raw', n]` moves the value by `n`, and `['normalized', n]` by `n` of the\n * range of that axis. The result is snapped to `step`, except for an amount\n * set on a modifier key, which is what lets shift move off the grid. `null`\n * turns the arrow keys off.\n *\n * The default moves by 1, and by 0.1 with shift. With a `step` above 1, raise\n * the amount to match: 1 would round straight back to where it started, and\n * a development build warns about it.\n *\n * @default { default: ['raw', 1], shift: ['raw', 0.1] }\n */\n keyboard?: ModifierValue<InputEventOption> | null\n\n /**\n * The cursor to show while dragging. It is set on the dragged element, so it\n * stays while the pointer is outside the pad.\n *\n * @default 'pointer'\n */\n dragCursor?: CSSProperties['cursor']\n\n /**\n * Make the pad unchangeable and remove its thumb from the tab order.\n * The parts carry `data-disabled` while it is set.\n */\n disabled?: boolean\n /**\n * Make the pad unchangeable while leaving its thumb focusable.\n * The parts carry `data-readonly` while it is set.\n */\n readOnly?: boolean\n\n /** Called with the new value when a drag, the wheel or an arrow key moves it. */\n onChange?: (value: XY<number>) => void\n /**\n * Called when a change of the value starts — a press on the area, the first wheel notch\n * or arrow key — with the value\n * before it and what it is made with. A host recording automation can treat\n * the control as touched from here until `onChangeEnd`.\n */\n onChangeStart?: (value: XY<number>, source: ChangeSource) => void\n /**\n * Called when the change ends, with the value it ended on: on release, or\n * `changeEndDelay` after the last wheel notch or arrow key.\n */\n onChangeEnd?: (value: XY<number>, source: ChangeSource) => void\n /**\n * How long after the last wheel notch or arrow key the change counts as\n * over, in milliseconds. Neither has an event that says it is done.\n *\n * @default 500\n */\n changeEndDelay?: number\n\n /**\n * The pad renders exactly what you compose here; there is no default\n * markup to fall back to.\n *\n * @example\n * <XYPad.Root value={[x, y]} min={0} max={100} onChange={setValue}>\n * <XYPad.Area>\n * <XYPad.Thumb />\n * </XYPad.Area>\n * </XYPad.Root>\n */\n children: ReactNode\n\n /**\n * Receives `focus` and `blur`, which act on the range inputs inside the thumb —\n * the elements that take the focus — and do nothing while\n * the pad is disabled. `ref` reaches the root element itself.\n */\n actionsRef?: Ref<XYPadMethods>\n}\n\nexport interface XYPadMethods {\n focus: () => void\n blur: () => void\n}\n\ntype Props = XYPadProps &\n Omit<ComponentPropsWithoutRef<'div'>, keyof XYPadProps>\n\n/**\n * The wheel only acts while the focus is inside, so that scrolling a page past\n * the control does not change its value.\n */\nconst WHEEL_OPTIONS = { requireFocus: true }\n\nexport const Root = /* @__PURE__ */ forwardRef<HTMLDivElement, Props>(\n (\n {\n value,\n min: _min,\n max: _max,\n step: _step = 1,\n scale: _scale = linearScale,\n reverse: _reverse = false,\n wheel = DEFAULT_WHEEL_OPTIONS,\n keyboard = DEFAULT_KEYBOARD_OPTIONS,\n dragSensitivity = DEFAULT_DRAG_SENSITIVITY,\n className,\n style,\n dragCursor = 'pointer',\n disabled = false,\n readOnly = false,\n onChange,\n onChangeStart,\n onChangeEnd,\n changeEndDelay,\n onPointerDown,\n onKeyDown,\n onFocus,\n onBlur,\n children,\n actionsRef,\n ...props\n }: Props,\n forwardedRef,\n ) => {\n // -- state and ref ---\n const rootRef = useRef<HTMLDivElement>(null)\n const areaRef = useRef<HTMLDivElement>(null)\n const thumbRef = useRef<XYPadThumbMethods>(null)\n\n // --- interpret props ---\n const inactive = disabled || readOnly\n\n const gesture = useChangeGesture(\n value,\n { onChangeStart, onChangeEnd, changeEndDelay },\n inactive,\n )\n const change = useCallback(\n (next: XY<number>) => {\n gesture.changed(next)\n onChange?.(next)\n },\n [gesture, onChange],\n )\n\n const min = useMemo(() => toXY(_min), [_min])\n const max = useMemo(() => toXY(_max), [_max])\n const step = useMemo(() => toXY(_step), [_step])\n const scale = useMemo(() => toXY(_scale), [_scale])\n const reverse = useMemo(() => toXY(_reverse), [_reverse])\n\n const percent = useMemo(\n (): XY<number> =>\n [0, 1].map((i) =>\n valuePercent(\n value[i],\n { min: min[i], max: max[i], scale: scale[i] },\n reverse[i],\n ),\n ) as XY<number>,\n [value, min, max, scale, reverse],\n )\n\n // --- internal functions ---\n // `AxisOptions` extends `ValueRange`, so the same pair also describes the\n // scaling for `applyDelta`. Its `reverse` only concerns the drag, where\n // positions follow the screen; the key and wheel handlers below flip the\n // direction themselves.\n const axis = useMemo(\n (): XY<AxisOptions> =>\n [0, 1].map((i) => ({\n min: min[i],\n max: max[i],\n step: step[i],\n scale: scale[i],\n reverse: reverse[i],\n })) as XY<AxisOptions>,\n [min, max, step, scale, reverse],\n )\n\n useCheckSteps({\n component: 'XYPad',\n axis: 'x',\n range: axis[0],\n keyboard,\n wheel,\n })\n useCheckSteps({\n component: 'XYPad',\n axis: 'y',\n range: axis[1],\n keyboard,\n wheel,\n })\n\n const withAxis = useCallback(\n (axis: 0 | 1, next: number): XY<number> =>\n axis === 0 ? [next, value[1]] : [value[0], next],\n [value],\n )\n\n /** @param i 0 = x, 1 = y */\n const nudge = useCallback(\n (\n i: 0 | 1,\n direction: number,\n option: ModifierValue<InputEventOption>,\n modifiers: ModifierState,\n ): XY<number> =>\n withAxis(\n i,\n applyDelta(value[i], direction, option, axis[i], modifiers),\n ),\n [value, axis, withAxis],\n )\n\n const handleKeyDown = useCallback(\n (event: React.KeyboardEvent<HTMLDivElement>) => {\n // The key picks the axis, whichever of the two inputs holds the\n // focus: the focus lands on the x input, so reading the axis off the\n // input would leave the y axis with no keys at all.\n const move = arrowKeyMove(event.key)\n if (!move) return\n event.preventDefault()\n if (!onChange || inactive || !keyboard) return\n const { axis: i, direction } = move\n gesture.pulse('keyboard')\n change(nudge(i, reverse[i] ? -direction : direction, keyboard, event))\n },\n [onChange, inactive, keyboard, reverse, nudge, gesture, change],\n )\n\n // --- hooks ---\n\n const { refCallback: dragRefCallback } = useDragValue<HTMLDivElement>({\n axis,\n baseElementRef: areaRef,\n sensitivity: (state) =>\n selectModifier(dragSensitivity, state.event).value,\n updateOnPointerDown: true,\n cursor: inactive ? undefined : dragCursor,\n shouldStart: () => !inactive,\n onChange: (v) => {\n if (inactive) return\n change(v)\n },\n onDragStart: () => {\n if (inactive) return\n gesture.hold('pointer')\n thumbRef.current?.focus()\n },\n onDragEnd: () => gesture.end(),\n })\n\n const wheelRefCallback = useWheel<HTMLDivElement>((event) => {\n if (!onChange || inactive || !wheel) return\n const move = wheelMove(event)\n if (!move) return\n event.preventDefault()\n const { axis: i, direction } = move\n gesture.pulse('wheel')\n change(nudge(i, reverse[i] ? -direction : direction, wheel, event))\n }, WHEEL_OPTIONS)\n\n // Composed once, so React attaches the refs a single time instead of\n // detaching and re-attaching on every render.\n const rootRefCallback = useComposedRefs<HTMLDivElement>(\n forwardedRef,\n rootRef,\n dragRefCallback,\n wheelRefCallback,\n )\n\n const context = useMemo(\n () => ({\n value,\n min,\n max,\n step,\n scale,\n reverse,\n disabled,\n readOnly,\n onChange,\n percent,\n areaRef,\n thumbRef,\n }),\n [\n value,\n min,\n max,\n step,\n scale,\n reverse,\n disabled,\n readOnly,\n onChange,\n percent,\n ],\n )\n\n useImperativeHandle(actionsRef, () => {\n return {\n focus() {\n if (!disabled) thumbRef.current?.focus()\n },\n blur() {\n thumbRef.current?.blur()\n },\n }\n }, [disabled])\n\n return (\n <XYPadProvider value={context}>\n {/* oxlint-disable-next-line jsx-a11y/no-noninteractive-element-interactions -- the group handles pointer and keyboard input shared by its two range controls */}\n <div\n className={className}\n ref={rootRefCallback}\n role=\"group\"\n tabIndex={-1}\n data-disabled={disabled ? '' : undefined}\n data-readonly={readOnly ? '' : undefined}\n style={style}\n onPointerDown={onPointerDown}\n onKeyDown={(event) => {\n handleKeyDown(event)\n onKeyDown?.(event)\n }}\n onFocus={(event) => {\n if (!disabled) thumbRef.current?.focus()\n onFocus?.(event)\n }}\n onBlur={(event) => {\n thumbRef.current?.blur()\n onBlur?.(event)\n }}\n {...props}\n >\n {children}\n </div>\n </XYPadProvider>\n )\n },\n)\n\n/**\n * Simple XYPad\n */\nexport const XYPad = {\n Root,\n Thumb,\n Area,\n}\n\nexport { type XYPadThumbProps, type XYPadThumbMethods } from './Thumb'\nexport { useXYPadContext, type XYPadContextValue } from './context'\n","import { useEffect } from 'react'\n\nimport { useCallbackRef } from './_internal/useCallbackRef'\n\nexport interface UseAnimationFrameOptions {\n /**\n * Stop the loop. Turning it back on starts a new one, whose first frame has\n * a `deltaTime` of `0`.\n * @default false\n */\n disabled?: boolean\n}\n\n/**\n * Call `callback` on every animation frame for as long as the component is\n * mounted.\n *\n * The callback is read on every frame, so it can be written inline and see\n * the latest render without restarting the loop.\n *\n * @param callback receives the frame's timestamp and the milliseconds since\n * the previous frame, which is `0` on the first one\n */\nexport function useAnimationFrame(\n callback: (timestamp: DOMHighResTimeStamp, deltaTime: number) => void,\n { disabled = false }: UseAnimationFrameOptions = {},\n) {\n // Read through a ref rather than depended on: `useAnimationFrame(() => ...)`\n // is a new function on every render, and a callback that renders would then\n // cancel and re-schedule its own loop on every frame.\n const runCallback = useCallbackRef(callback)\n\n useEffect(() => {\n if (disabled) return\n\n let frameId = 0\n let previous: DOMHighResTimeStamp | undefined\n\n // Scheduled before calling back, so that a slow callback does not delay\n // the next request.\n const loop = (timestamp: DOMHighResTimeStamp) => {\n frameId = requestAnimationFrame(loop)\n runCallback(timestamp, previous === undefined ? 0 : timestamp - previous)\n previous = timestamp\n }\n\n frameId = requestAnimationFrame(loop)\n return () => cancelAnimationFrame(frameId)\n }, [runCallback, disabled])\n}\n","import { useEffect, useState } from 'react'\n\nimport { createDrag, type DragState } from '@tremolo-ui/dom'\n\nimport { useCallbackRef } from './_internal/useCallbackRef'\n\nexport interface UseDragOptions {\n /**\n * Pixels the pointer has to travel before the drag counts as one.\n *\n * A click moves the pointer by a pixel or two, so without a threshold a\n * double click reports a drag between the two presses.\n *\n * @default 1\n */\n threshold?: number\n\n /** CSS cursor to show while dragging. Applied to the element itself. */\n cursor?: string\n\n /**\n * Hide the pointer and read its movement directly, instead of following it\n * around the screen.\n *\n * @see DragOptions.pointerLock\n * @default false\n */\n pointerLock?: boolean\n\n /**\n * Called on every move once the drag has started, with the pointer position\n * relative to the element and how far it moved since the last call.\n *\n * @param state the whole drag, for anything the four numbers leave out —\n * the pointer event and its modifier keys, most of all.\n */\n onDrag?: (\n x: number,\n y: number,\n deltaX: number,\n deltaY: number,\n state: DragState,\n ) => void\n /** Called once the pointer has moved past `threshold`, not on pointerdown. */\n onDragStart?: (state: DragState) => void\n /** Called when the pointer is released, only if the drag ever started. */\n onDragEnd?: (state: DragState) => void\n}\n\n/**\n * Track a pointer drag on an element.\n *\n * @returns a ref callback to attach to the element being dragged\n */\nexport function useDrag<T extends Element>({\n threshold = 1,\n cursor,\n pointerLock,\n onDrag,\n onDragStart,\n onDragEnd,\n}: UseDragOptions): (node: T | null) => void {\n const dragHandler = useCallbackRef(onDrag)\n const dragStartHandler = useCallbackRef(onDragStart)\n const dragEndHandler = useCallbackRef(onDragEnd)\n\n // The node is held in state rather than bound in the ref callback itself, so\n // that re-attaching the ref with the same node does not restart the drag.\n // React re-attaches on every render when the caller passes an inline ref.\n const [node, setNode] = useState<T | null>(null)\n\n useEffect(() => {\n if (!node) return\n\n const instance = createDrag(node, {\n threshold,\n cursor,\n pointerLock,\n onDragStart: (state) => dragStartHandler(state),\n onDrag: (state) =>\n dragHandler(state.x, state.y, state.deltaX, state.deltaY, state),\n onDragEnd: (state) => dragEndHandler(state),\n })\n\n return () => instance.destroy()\n }, [\n node,\n threshold,\n cursor,\n pointerLock,\n dragHandler,\n dragStartHandler,\n dragEndHandler,\n ])\n\n return setNode\n}\n","import { useCallback, useEffect, useRef } from 'react'\n\nimport { useCallbackRef } from './_internal/useCallbackRef'\n\n/**\n * What {@link useEventListener} listens on. A function is called after every\n * render, so it can return an element out of a ref; `null` listens on nothing.\n */\nexport type UseEventListenerTarget =\n | EventTarget\n | null\n | (() => EventTarget | null)\n\n/**\n * The options of `addEventListener`, as a boolean for `capture` or as an\n * object. `signal` is left out: the hook already owns when the listener goes.\n */\nexport type UseEventListenerOptions =\n | boolean\n | Pick<AddEventListenerOptions, 'capture' | 'once' | 'passive'>\n\ninterface Registration {\n node: EventTarget\n event: string\n capture: boolean\n once: boolean | undefined\n passive: boolean | undefined\n remove: VoidFunction\n}\n\n/**\n * Listen to an event on a target for as long as the component is mounted.\n *\n * The handler is read on every event, and the target and the options are\n * compared by what they resolve to, so all three can be written inline. The\n * listener is re-attached only when the element, the event or one of the\n * options actually changes.\n *\n * @returns a function that removes the listener. It stays removed until the\n * target, the event or the options change.\n */\nexport function useEventListener<K extends keyof DocumentEventMap>(\n target: UseEventListenerTarget,\n event: K,\n handler: (event: DocumentEventMap[K]) => void,\n options?: UseEventListenerOptions,\n): VoidFunction\nexport function useEventListener<K extends keyof WindowEventMap>(\n target: UseEventListenerTarget,\n event: K,\n handler: (event: WindowEventMap[K]) => void,\n options?: UseEventListenerOptions,\n): VoidFunction\nexport function useEventListener<K extends keyof GlobalEventHandlersEventMap>(\n target: UseEventListenerTarget,\n event: K,\n handler: (event: GlobalEventHandlersEventMap[K]) => void,\n options?: UseEventListenerOptions,\n): VoidFunction\nexport function useEventListener(\n target: UseEventListenerTarget,\n event: string,\n handler: (event: Event) => void,\n options?: UseEventListenerOptions,\n) {\n const listener = useCallbackRef(handler)\n const registrationRef = useRef<Registration | null>(null)\n\n const {\n capture = false,\n once,\n passive,\n } = typeof options === 'boolean' ? { capture: options } : (options ?? {})\n\n // No dependency list: an inline target or options object is a new value on\n // every render, so what decides a re-attach is the comparison below, not\n // their identity.\n useEffect(() => {\n const node = typeof target === 'function' ? target() : target\n const current = registrationRef.current\n\n if (\n current &&\n current.node === node &&\n current.event === event &&\n current.capture === capture &&\n current.once === once &&\n current.passive === passive\n ) {\n return\n }\n\n current?.remove()\n registrationRef.current = null\n if (!node) return\n\n const listenerOptions = { capture, once, passive }\n node.addEventListener(event, listener, listenerOptions)\n registrationRef.current = {\n node,\n event,\n capture,\n once,\n passive,\n remove: () => node.removeEventListener(event, listener, listenerOptions),\n }\n })\n\n useEffect(\n () => () => {\n registrationRef.current?.remove()\n registrationRef.current = null\n },\n [],\n )\n\n // Kept registered, so a render with the same target does not attach again.\n return useCallback(() => {\n const current = registrationRef.current\n if (!current) return\n current.remove()\n current.remove = () => {}\n }, [])\n}\n","import { useEffect, useMemo, useSyncExternalStore } from 'react'\n\nimport {\n createMIDIAccess,\n type MIDIAccessInstance,\n type MIDIAccessOptions,\n type MIDIAccessState,\n} from '@tremolo-ui/dom'\n\nconst INITIAL_STATE: MIDIAccessState = {\n midiAccess: null,\n error: null,\n inputs: [],\n}\n\nfunction createMIDIAccessStore() {\n let instance: MIDIAccessInstance | null = null\n let unsubscribe: VoidFunction | null = null\n let state = INITIAL_STATE\n const listeners = new Set<VoidFunction>()\n\n function emit() {\n for (const listener of listeners) listener()\n }\n\n function connect() {\n const nextInstance = createMIDIAccess()\n instance = nextInstance\n state = nextInstance.getState()\n unsubscribe = nextInstance.subscribe(() => {\n state = nextInstance.getState()\n emit()\n })\n emit()\n return nextInstance\n }\n\n function disconnect(currentInstance: MIDIAccessInstance) {\n if (instance !== currentInstance) return\n\n unsubscribe?.()\n unsubscribe = null\n instance = null\n state = INITIAL_STATE\n currentInstance.destroy()\n emit()\n }\n\n function request(options?: MIDIAccessOptions) {\n instance?.request(options)\n }\n\n return {\n connect,\n disconnect,\n request,\n getState: () => state,\n getServerState: () => INITIAL_STATE,\n subscribe: (listener: VoidFunction) => {\n listeners.add(listener)\n return () => listeners.delete(listener)\n },\n }\n}\n\n/**\n * Hooks for requesting MIDI access in the browser. The first argument allows you to choose whether to request access on mount.\n *\n * `inputs` follows the devices: it changes as one is plugged in or unplugged,\n * so a list of devices needs no `statechange` listener of its own.\n *\n * `request` takes `{ sysex: true }` when the app needs system exclusive\n * messages. Browsers treat that as a separate, more sensitive permission, so\n * ask for it only when it is actually used.\n */\nexport function useMIDIAccess(requestOnMount = true) {\n const store = useMemo(() => createMIDIAccessStore(), [])\n\n const { midiAccess, error, inputs } = useSyncExternalStore(\n store.subscribe,\n store.getState,\n store.getServerState,\n )\n\n useEffect(() => {\n const instance = store.connect()\n return () => store.disconnect(instance)\n }, [store])\n\n useEffect(() => {\n if (requestOnMount) store.request()\n }, [store, requestOnMount])\n\n return { request: store.request, midiAccess, error, inputs }\n}\n","import { useEffect, useRef } from 'react'\n\nimport {\n createMIDIInput,\n type MIDIInputHandlers,\n type MIDIInputInstance,\n} from '@tremolo-ui/dom'\n\n/**\n * Handle MIDI input events. To be used with {@link useMIDIAccess}.\n *\n * The handlers are read fresh on every event, so writing them inline is fine:\n * the listeners are attached once per `midiAccess` and stay attached. Devices\n * plugged in later are picked up without anything having to be rebuilt.\n *\n * @example\n * const { midiAccess } = useMIDIAccess()\n * useMIDIInput(midiAccess, {\n * onNoteOnEvent: (note, velocity) => play(note, velocity / 127),\n * onNoteOffEvent: (note) => stop(note),\n * onControlChangeEvent: (controller, value) => {\n * if (controller === 1) setModulation(value / 127)\n * },\n * })\n */\nexport function useMIDIInput(\n midiAccess: MIDIAccess | null,\n handlers: MIDIInputHandlers,\n) {\n // Read when the instance is created. The effect below keeps it current, and\n // runs right after, so a stale handler is replaced within the same commit.\n const latest = useRef(handlers)\n const instanceRef = useRef<MIDIInputInstance | null>(null)\n\n useEffect(() => {\n const instance = createMIDIInput(midiAccess, latest.current)\n instanceRef.current = instance\n\n return () => {\n instanceRef.current = null\n instance.destroy()\n }\n }, [midiAccess])\n\n // Runs after every render: handlers come from props and are cheap to push,\n // and updating in place leaves the listeners untouched.\n useEffect(() => {\n latest.current = handlers\n instanceRef.current?.update(handlers)\n })\n}\n","import { useEffect, useRef } from 'react'\n\nimport { createMIDIMessage, type MIDIMessageInstance } from '@tremolo-ui/dom'\n\n/**\n * Hooks for when you want to process MIDI events in more detail than useMIDIInput.\n *\n * The handler is read fresh on every event, so writing it inline is fine: the\n * listeners are attached once per `midiAccess` and stay attached, and devices\n * plugged in later are picked up.\n */\nexport function useMIDIMessage(\n midiAccess: MIDIAccess | null,\n onMIDIMessage: (event: MIDIMessageEvent) => void,\n) {\n const latest = useRef(onMIDIMessage)\n const instanceRef = useRef<MIDIMessageInstance | null>(null)\n\n useEffect(() => {\n const instance = createMIDIMessage(midiAccess, (event) =>\n latest.current(event),\n )\n instanceRef.current = instance\n\n return () => {\n instanceRef.current = null\n instance.destroy()\n }\n }, [midiAccess])\n\n useEffect(() => {\n latest.current = onMIDIMessage\n })\n}\n"],"mappings":"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AAI;GACb,IAAI,SAAS,OAAO;GACpB,QAAQ,QAAQ,IAAI;GACpB,WAAW,IAAI;EACjB,GACA;GAAC;GAAU;GAAO;GAAU;EAAO,CACrC;EAEA,MAAM,cAAc,kBAAkB;GACpC,IAAI,UAAU,QAAQ,UAAU;GAChC,MAAM,YAAY,sBAAsB,OAAO,OAAO;IACpD;IACA;IACA;GACF,CAAC;GAGD,IAAI,cAAc,MAAM;IACtB,SAAS,IAAI;IACb;GACF;GAEA,IAAI,cAAc,OAAO,QAAQ,MAAM,UAAU;GACjD,YAAY,SAAS;GACrB,QAAQ,IAAI;EACd,GAAG;GACD;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;EACF,CAAC;EAED,MAAM,QAAQ,aAEV,WACA,QACA,cACG;GACH,YACE,iBAAiB,OAAO,WAAW,QAAQ,QAAQ,SAAS,CAC9D;EACF,GACA;GAAC;GAAa;GAAO;EAAM,CAC7B;EAGA,MAAM,mBAAmB,UACtB,UAAU;GACT,MAAM,YAAY,eAAe,KAAK;GACtC,IAAI,CAAC,SAAS,YAAY,cAAc,MAAM;GAC9C,MAAM,eAAe;GACrB,QAAQ,MAAM,OAAO;GACrB,MAAM,WAAW,OAAO,KAAK;EAC/B,GACA,EAAE,cAAc,KAAK,CACvB;EAEA,MAAM,UAAU,eACP;GACL;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA,UAAU;GACV;GACA;GACA;GACA;EACF,IACA;GACE;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;EACF,CACF;EAIA,MAAM,kBAAkB,gBACtB,cACA,gBACF;EAEA,oBAAoB,kBAAkB;GACpC,OAAO;IACL,QAAQ;KACN,IAAI,CAAC,UAAU,SAAS,SAAS,MAAM;IACzC;IACA,OAAO;KACL,SAAS,SAAS,KAAK;IACzB;GACF;EA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"}
|