@seekalfred/primitives 0.6.0 → 0.7.1
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/README.md +19 -11
- package/dist/activity-heatmap/activity-heatmap.d.ts +92 -0
- package/dist/activity-heatmap/activity-heatmap.js +699 -0
- package/dist/activity-heatmap/activity-heatmap.js.map +1 -0
- package/dist/activity-heatmap/index.d.ts +2 -0
- package/dist/activity-heatmap/index.js +2 -0
- package/dist/alert/alert.d.ts +5 -1
- package/dist/alert/alert.js +68 -7
- package/dist/alert/alert.js.map +1 -1
- package/dist/alert-dialog/alert-dialog.d.ts +45 -18
- package/dist/alert-dialog/alert-dialog.js +128 -9
- package/dist/alert-dialog/alert-dialog.js.map +1 -1
- package/dist/alert-dialog/index.d.ts +2 -2
- package/dist/alert-dialog/index.js +2 -2
- package/dist/animated-counter/animated-counter.d.ts +38 -0
- package/dist/animated-counter/animated-counter.js +230 -0
- package/dist/animated-counter/animated-counter.js.map +1 -0
- package/dist/animated-counter/index.d.ts +2 -0
- package/dist/animated-counter/index.js +2 -0
- package/dist/announcement-bar/announcement-bar.d.ts +67 -0
- package/dist/announcement-bar/announcement-bar.js +364 -0
- package/dist/announcement-bar/announcement-bar.js.map +1 -0
- package/dist/announcement-bar/index.d.ts +2 -0
- package/dist/announcement-bar/index.js +2 -0
- package/dist/badge/badge.d.ts +1 -1
- package/dist/bar-chart/bar-chart.d.ts +51 -0
- package/dist/bar-chart/bar-chart.js +565 -0
- package/dist/bar-chart/bar-chart.js.map +1 -0
- package/dist/bar-chart/index.d.ts +2 -0
- package/dist/bar-chart/index.js +2 -0
- package/dist/breadcrumb/breadcrumb.d.ts +2 -2
- package/dist/breadcrumb/breadcrumb.js +12 -3
- package/dist/breadcrumb/breadcrumb.js.map +1 -1
- package/dist/brush-chart/brush-chart.d.ts +64 -0
- package/dist/brush-chart/brush-chart.js +1056 -0
- package/dist/brush-chart/brush-chart.js.map +1 -0
- package/dist/brush-chart/index.d.ts +2 -0
- package/dist/brush-chart/index.js +2 -0
- package/dist/button/button.d.ts +2 -2
- package/dist/button/button.js +31 -21
- package/dist/button/button.js.map +1 -1
- package/dist/button-group/button-group.js +3 -4
- package/dist/button-group/button-group.js.map +1 -1
- package/dist/card/card.js +1 -1
- package/dist/card/card.js.map +1 -1
- package/dist/card-stack/card-stack.d.ts +54 -0
- package/dist/card-stack/card-stack.js +492 -0
- package/dist/card-stack/card-stack.js.map +1 -0
- package/dist/card-stack/index.d.ts +2 -0
- package/dist/card-stack/index.js +2 -0
- package/dist/carousel/carousel.d.ts +56 -0
- package/dist/carousel/carousel.js +389 -0
- package/dist/carousel/carousel.js.map +1 -0
- package/dist/carousel/index.d.ts +2 -0
- package/dist/carousel/index.js +2 -0
- package/dist/checkbox/checkbox.js +4 -4
- package/dist/checkbox/checkbox.js.map +1 -1
- package/dist/chip-group/chip-group.d.ts +37 -0
- package/dist/chip-group/chip-group.js +148 -0
- package/dist/chip-group/chip-group.js.map +1 -0
- package/dist/chip-group/index.d.ts +2 -0
- package/dist/chip-group/index.js +2 -0
- package/dist/countdown/countdown.d.ts +45 -0
- package/dist/countdown/countdown.js +298 -0
- package/dist/countdown/countdown.js.map +1 -0
- package/dist/countdown/index.d.ts +2 -0
- package/dist/countdown/index.js +2 -0
- package/dist/date-picker/picker-parts.js +1 -1
- package/dist/date-picker/picker-parts.js.map +1 -1
- package/dist/dialog/dialog.js +1 -1
- package/dist/dialog/dialog.js.map +1 -1
- package/dist/donut-chart/donut-chart.d.ts +64 -0
- package/dist/donut-chart/donut-chart.js +576 -0
- package/dist/donut-chart/donut-chart.js.map +1 -0
- package/dist/donut-chart/index.d.ts +2 -0
- package/dist/donut-chart/index.js +2 -0
- package/dist/empty-state/empty-state.d.ts +21 -10
- package/dist/empty-state/empty-state.js +32 -26
- package/dist/empty-state/empty-state.js.map +1 -1
- package/dist/empty-state/index.d.ts +2 -2
- package/dist/empty-state/index.js +2 -2
- package/dist/file-drop-zone/file-drop-zone.js +1 -1
- package/dist/file-drop-zone/file-drop-zone.js.map +1 -1
- package/dist/filter-bar/filter-bar.d.ts +47 -26
- package/dist/filter-bar/filter-bar.js +272 -156
- package/dist/filter-bar/filter-bar.js.map +1 -1
- package/dist/gauge/gauge.d.ts +45 -0
- package/dist/gauge/gauge.js +219 -0
- package/dist/gauge/gauge.js.map +1 -0
- package/dist/gauge/index.d.ts +2 -0
- package/dist/gauge/index.js +2 -0
- package/dist/in-view-title/in-view-title.d.ts +33 -0
- package/dist/in-view-title/in-view-title.js +119 -0
- package/dist/in-view-title/in-view-title.js.map +1 -0
- package/dist/in-view-title/index.d.ts +2 -0
- package/dist/in-view-title/index.js +2 -0
- package/dist/index.d.ts +49 -6
- package/dist/index.js +48 -8
- package/dist/lib/cn.js +11 -1
- package/dist/lib/cn.js.map +1 -1
- package/dist/lib/field-message.js +28 -10
- package/dist/lib/field-message.js.map +1 -1
- package/dist/lib/field-surface.js +9 -8
- package/dist/lib/field-surface.js.map +1 -1
- package/dist/lib/flag-emoji.js +50 -0
- package/dist/lib/flag-emoji.js.map +1 -0
- package/dist/lib/flip.js +11 -3
- package/dist/lib/flip.js.map +1 -1
- package/dist/lib/menu-highlight.js +64 -0
- package/dist/lib/menu-highlight.js.map +1 -0
- package/dist/lib/motion.js +9 -2
- package/dist/lib/motion.js.map +1 -1
- package/dist/lib/rolling-value.js +1 -1
- package/dist/lib/rolling-value.js.map +1 -1
- package/dist/lib/spring-tween.js +500 -0
- package/dist/lib/spring-tween.js.map +1 -0
- package/dist/lib/swap-text.js +3 -1
- package/dist/lib/swap-text.js.map +1 -1
- package/dist/line-chart/index.d.ts +2 -0
- package/dist/line-chart/index.js +2 -0
- package/dist/line-chart/line-chart.d.ts +76 -0
- package/dist/line-chart/line-chart.js +611 -0
- package/dist/line-chart/line-chart.js.map +1 -0
- package/dist/mention-input/index.d.ts +2 -0
- package/dist/mention-input/index.js +2 -0
- package/dist/mention-input/mention-input.d.ts +98 -0
- package/dist/mention-input/mention-input.js +576 -0
- package/dist/mention-input/mention-input.js.map +1 -0
- package/dist/menu/menu.d.ts +2 -0
- package/dist/menu/menu.js +23 -12
- package/dist/menu/menu.js.map +1 -1
- package/dist/money-input/index.d.ts +2 -0
- package/dist/money-input/index.js +2 -0
- package/dist/money-input/money-input.d.ts +65 -0
- package/dist/money-input/money-input.js +607 -0
- package/dist/money-input/money-input.js.map +1 -0
- package/dist/morph-select/index.d.ts +2 -0
- package/dist/morph-select/index.js +2 -0
- package/dist/morph-select/morph-select.d.ts +71 -0
- package/dist/morph-select/morph-select.js +693 -0
- package/dist/morph-select/morph-select.js.map +1 -0
- package/dist/multi-select/index.d.ts +2 -0
- package/dist/multi-select/index.js +2 -0
- package/dist/multi-select/multi-select.d.ts +36 -0
- package/dist/multi-select/multi-select.js +152 -0
- package/dist/multi-select/multi-select.js.map +1 -0
- package/dist/number-field/index.d.ts +2 -0
- package/dist/number-field/index.js +2 -0
- package/dist/number-field/number-field.d.ts +42 -0
- package/dist/number-field/number-field.js +199 -0
- package/dist/number-field/number-field.js.map +1 -0
- package/dist/onboarding-checklist/index.d.ts +2 -0
- package/dist/onboarding-checklist/index.js +2 -0
- package/dist/onboarding-checklist/onboarding-checklist.d.ts +55 -0
- package/dist/onboarding-checklist/onboarding-checklist.js +577 -0
- package/dist/onboarding-checklist/onboarding-checklist.js.map +1 -0
- package/dist/password-field/password-field.js +2 -2
- package/dist/password-field/password-field.js.map +1 -1
- package/dist/phone-input/index.d.ts +3 -0
- package/dist/phone-input/index.js +3 -0
- package/dist/phone-input/phone-countries.d.ts +29 -0
- package/dist/phone-input/phone-countries.js +170 -0
- package/dist/phone-input/phone-countries.js.map +1 -0
- package/dist/phone-input/phone-input.d.ts +66 -0
- package/dist/phone-input/phone-input.js +440 -0
- package/dist/phone-input/phone-input.js.map +1 -0
- package/dist/popover/popover.d.ts +12 -0
- package/dist/popover/popover.js +31 -7
- package/dist/popover/popover.js.map +1 -1
- package/dist/progress/progress.js +1 -1
- package/dist/radio-group/radio-group.js +1 -1
- package/dist/radio-group/radio-group.js.map +1 -1
- package/dist/reactions/index.d.ts +2 -0
- package/dist/reactions/index.js +2 -0
- package/dist/reactions/reactions.d.ts +50 -0
- package/dist/reactions/reactions.js +315 -0
- package/dist/reactions/reactions.js.map +1 -0
- package/dist/rich-text-editor/index.d.ts +3 -0
- package/dist/rich-text-editor/index.js +2 -0
- package/dist/rich-text-editor/rich-text-editor.d.ts +70 -0
- package/dist/rich-text-editor/rich-text-editor.js +909 -0
- package/dist/rich-text-editor/rich-text-editor.js.map +1 -0
- package/dist/rich-text-editor/rich-text-engine.d.ts +16 -0
- package/dist/rich-text-editor/rich-text-engine.js +366 -0
- package/dist/rich-text-editor/rich-text-engine.js.map +1 -0
- package/dist/rich-text-editor/rich-text-html.js +174 -0
- package/dist/rich-text-editor/rich-text-html.js.map +1 -0
- package/dist/rich-text-editor/rich-text-icons.js +43 -0
- package/dist/rich-text-editor/rich-text-icons.js.map +1 -0
- package/dist/ridgeline/index.d.ts +2 -0
- package/dist/ridgeline/index.js +2 -0
- package/dist/ridgeline/ridgeline.d.ts +55 -0
- package/dist/ridgeline/ridgeline.js +701 -0
- package/dist/ridgeline/ridgeline.js.map +1 -0
- package/dist/scroll-area/index.d.ts +2 -2
- package/dist/scroll-area/index.js +2 -2
- package/dist/scroll-area/scroll-area.d.ts +68 -31
- package/dist/scroll-area/scroll-area.js +208 -59
- package/dist/scroll-area/scroll-area.js.map +1 -1
- package/dist/scrub-input/evaluate.d.ts +14 -0
- package/dist/scrub-input/evaluate.js +72 -0
- package/dist/scrub-input/evaluate.js.map +1 -0
- package/dist/scrub-input/index.d.ts +3 -0
- package/dist/scrub-input/index.js +3 -0
- package/dist/scrub-input/scrub-input.d.ts +74 -0
- package/dist/scrub-input/scrub-input.js +486 -0
- package/dist/scrub-input/scrub-input.js.map +1 -0
- package/dist/search-field/search-field.d.ts +2 -1
- package/dist/search-field/search-field.js +4 -4
- package/dist/search-field/search-field.js.map +1 -1
- package/dist/select/select.d.ts +3 -1
- package/dist/select/select.js +20 -8
- package/dist/select/select.js.map +1 -1
- package/dist/separator/separator.d.ts +1 -1
- package/dist/sheet/sheet.js +2 -1
- package/dist/sheet/sheet.js.map +1 -1
- package/dist/shortcut-recorder/index.d.ts +3 -0
- package/dist/shortcut-recorder/index.js +3 -0
- package/dist/shortcut-recorder/shortcut-model.d.ts +27 -0
- package/dist/shortcut-recorder/shortcut-model.js +206 -0
- package/dist/shortcut-recorder/shortcut-model.js.map +1 -0
- package/dist/shortcut-recorder/shortcut-recorder.d.ts +93 -0
- package/dist/shortcut-recorder/shortcut-recorder.js +489 -0
- package/dist/shortcut-recorder/shortcut-recorder.js.map +1 -0
- package/dist/slider/index.d.ts +2 -2
- package/dist/slider/slider.d.ts +46 -14
- package/dist/slider/slider.js +151 -67
- package/dist/slider/slider.js.map +1 -1
- package/dist/slope-chart/index.d.ts +2 -0
- package/dist/slope-chart/index.js +2 -0
- package/dist/slope-chart/slope-chart.d.ts +54 -0
- package/dist/slope-chart/slope-chart.js +568 -0
- package/dist/slope-chart/slope-chart.js.map +1 -0
- package/dist/sparkline/index.d.ts +2 -0
- package/dist/sparkline/index.js +2 -0
- package/dist/sparkline/sparkline.d.ts +46 -0
- package/dist/sparkline/sparkline.js +390 -0
- package/dist/sparkline/sparkline.js.map +1 -0
- package/dist/split-bar/index.d.ts +2 -0
- package/dist/split-bar/index.js +2 -0
- package/dist/split-bar/split-bar.d.ts +62 -0
- package/dist/split-bar/split-bar.js +513 -0
- package/dist/split-bar/split-bar.js.map +1 -0
- package/dist/split-button/split-button.d.ts +1 -1
- package/dist/split-button/split-button.js +12 -11
- package/dist/split-button/split-button.js.map +1 -1
- package/dist/stat-card/index.d.ts +2 -2
- package/dist/stat-card/index.js +2 -2
- package/dist/stat-card/stat-card.d.ts +24 -4
- package/dist/stat-card/stat-card.js +114 -17
- package/dist/stat-card/stat-card.js.map +1 -1
- package/dist/stepper/index.d.ts +2 -0
- package/dist/stepper/index.js +2 -0
- package/dist/stepper/stepper.d.ts +51 -0
- package/dist/stepper/stepper.js +215 -0
- package/dist/stepper/stepper.js.map +1 -0
- package/dist/streamgraph/index.d.ts +2 -0
- package/dist/streamgraph/index.js +2 -0
- package/dist/streamgraph/streamgraph.d.ts +70 -0
- package/dist/streamgraph/streamgraph.js +613 -0
- package/dist/streamgraph/streamgraph.js.map +1 -0
- package/dist/styles.css +1 -1
- package/dist/swipe-actions/index.d.ts +2 -0
- package/dist/swipe-actions/index.js +2 -0
- package/dist/swipe-actions/swipe-actions.d.ts +67 -0
- package/dist/swipe-actions/swipe-actions.js +552 -0
- package/dist/swipe-actions/swipe-actions.js.map +1 -0
- package/dist/switch/switch.js +2 -2
- package/dist/switch/switch.js.map +1 -1
- package/dist/table/index.d.ts +2 -2
- package/dist/table/index.js +2 -2
- package/dist/table/table.d.ts +56 -4
- package/dist/table/table.js +340 -165
- package/dist/table/table.js.map +1 -1
- package/dist/tabs/tabs.js +2 -2
- package/dist/tabs/tabs.js.map +1 -1
- package/dist/tag-input/index.d.ts +2 -0
- package/dist/tag-input/index.js +2 -0
- package/dist/tag-input/tag-input.d.ts +40 -0
- package/dist/tag-input/tag-input.js +269 -0
- package/dist/tag-input/tag-input.js.map +1 -0
- package/dist/text-morph/index.d.ts +2 -0
- package/dist/text-morph/index.js +2 -0
- package/dist/text-morph/text-morph.d.ts +23 -0
- package/dist/text-morph/text-morph.js +21 -0
- package/dist/text-morph/text-morph.js.map +1 -0
- package/dist/text-reveal/index.d.ts +2 -0
- package/dist/text-reveal/index.js +2 -0
- package/dist/text-reveal/text-reveal.d.ts +27 -0
- package/dist/text-reveal/text-reveal.js +51 -0
- package/dist/text-reveal/text-reveal.js.map +1 -0
- package/dist/text-shimmer/index.d.ts +2 -0
- package/dist/text-shimmer/index.js +2 -0
- package/dist/text-shimmer/text-shimmer.d.ts +29 -0
- package/dist/text-shimmer/text-shimmer.js +29 -0
- package/dist/text-shimmer/text-shimmer.js.map +1 -0
- package/dist/time-picker/time-picker.js +2 -2
- package/dist/time-picker/time-picker.js.map +1 -1
- package/dist/toast/index.d.ts +2 -2
- package/dist/toast/index.js +2 -2
- package/dist/toast/toast.d.ts +6 -0
- package/dist/toast/toast.js +82 -40
- package/dist/toast/toast.js.map +1 -1
- package/dist/tooltip/tooltip.d.ts +9 -2
- package/dist/tooltip/tooltip.js +7 -7
- package/dist/tooltip/tooltip.js.map +1 -1
- package/dist/treemap/index.d.ts +2 -0
- package/dist/treemap/index.js +2 -0
- package/dist/treemap/treemap.d.ts +53 -0
- package/dist/treemap/treemap.js +699 -0
- package/dist/treemap/treemap.js.map +1 -0
- package/dist/usage-meter/index.d.ts +2 -0
- package/dist/usage-meter/index.js +2 -0
- package/dist/usage-meter/usage-meter.d.ts +50 -0
- package/dist/usage-meter/usage-meter.js +440 -0
- package/dist/usage-meter/usage-meter.js.map +1 -0
- package/dist/waffle-chart/index.d.ts +2 -0
- package/dist/waffle-chart/index.js +2 -0
- package/dist/waffle-chart/waffle-chart.d.ts +57 -0
- package/dist/waffle-chart/waffle-chart.js +426 -0
- package/dist/waffle-chart/waffle-chart.js.map +1 -0
- package/package.json +165 -3
|
@@ -0,0 +1,486 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { cn, cnAll } from "../lib/cn.js";
|
|
3
|
+
import { prefersReducedMotion } from "../lib/motion.js";
|
|
4
|
+
import { SwapText } from "../lib/swap-text.js";
|
|
5
|
+
import { evaluateScrubExpression } from "./evaluate.js";
|
|
6
|
+
import { forwardRef, useCallback, useEffect, useId, useImperativeHandle, useRef, useState } from "react";
|
|
7
|
+
import { Button } from "react-aria-components";
|
|
8
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
9
|
+
import { createPortal } from "react-dom";
|
|
10
|
+
//#region src/scrub-input/scrub-input.tsx
|
|
11
|
+
/** Movement before a press on the label becomes a scrub, and the most pixels the field strains past a limit. */
|
|
12
|
+
const SLOP = 3;
|
|
13
|
+
const STRAIN = 12;
|
|
14
|
+
/** The most overshoot kept, so reversing starts moving the value again after a short relax. */
|
|
15
|
+
const OVERSHOOT_CAP = 90;
|
|
16
|
+
/** How far a refused step or an invalid entry kicks the number (px). */
|
|
17
|
+
const KICK = 6;
|
|
18
|
+
/** The longest the spring home takes (the smooth spring, 720ms) plus a margin. */
|
|
19
|
+
const RELEASE_MS = 900;
|
|
20
|
+
/** How long an invalid entry is flagged. */
|
|
21
|
+
const INVALID_MS = 900;
|
|
22
|
+
const decimalsOf = (value) => {
|
|
23
|
+
const text = String(value);
|
|
24
|
+
if (text.includes("e-")) return Number(text.split("e-")[1]);
|
|
25
|
+
return (text.split(".")[1] ?? "").length;
|
|
26
|
+
};
|
|
27
|
+
const roundTo = (value, places) => {
|
|
28
|
+
const factor = 10 ** places;
|
|
29
|
+
return Math.round(value * factor) / factor;
|
|
30
|
+
};
|
|
31
|
+
/** Rubber-band resistance: each extra pixel gives less, and the strain never exceeds `limit`. */
|
|
32
|
+
const rubber = (distance, limit = STRAIN) => Math.sign(distance) * (1 - 1 / (Math.abs(distance) * .55 / limit + 1)) * limit;
|
|
33
|
+
const format = (value, places) => String(roundTo(value, places)).replace(/^-0$/, "0");
|
|
34
|
+
const sizeClass = {
|
|
35
|
+
md: "h-(--density-control-h-sm) rounded-md text-sm",
|
|
36
|
+
sm: "h-7.5 rounded-sm text-xs"
|
|
37
|
+
};
|
|
38
|
+
/**
|
|
39
|
+
* A compact number field for inspectors and property panels. Drag the label sideways to
|
|
40
|
+
* scrub the value (the mouse pointer is locked when the browser allows it, so a long drag
|
|
41
|
+
* never runs out of screen), or click in and type an exact value. A typed entry takes
|
|
42
|
+
* arithmetic such as `12*2` or `(100-24)/2`, and a unit from `units` such as `50%`.
|
|
43
|
+
*
|
|
44
|
+
* Keyboard: the arrow keys step (Shift ten times coarser, Alt ten times finer), Enter
|
|
45
|
+
* commits typing, Escape restores the last value (or cancels a scrub back to where it
|
|
46
|
+
* began). A double click on the label restores `resetValue`. The input is a spinbutton, and
|
|
47
|
+
* its value text spells out percent and degrees.
|
|
48
|
+
*
|
|
49
|
+
* At a limit the field strains: dragged past `min` or `max` it stretches against
|
|
50
|
+
* rubber-band resistance and springs back on release, and a refused step or an invalid
|
|
51
|
+
* entry kicks the number. Reduced motion removes the strain, the kick and the fill's glide.
|
|
52
|
+
*
|
|
53
|
+
* @example
|
|
54
|
+
* <ScrubInput label="W" value={width} onValueChange={setWidth} min={0} units={["px", "%"]} />
|
|
55
|
+
*/
|
|
56
|
+
const ScrubInput = forwardRef(function ScrubInput({ label, value, defaultValue = 0, onValueChange, onValueCommit, min = -Infinity, max = Infinity, step = 1, fineStep, coarseStep, precision, pixelsPerStep = 2, wrap = false, unit: unitProp, defaultUnit, units, onUnitChange, resetValue, track, size = "md", pointerLock = true, className, disabled, id: idProp, onFocus, onBlur, onKeyDown, "aria-label": ariaLabel, ...rest }, forwarded) {
|
|
57
|
+
const uid = useId();
|
|
58
|
+
const id = idProp ?? `scrub-${uid.replace(/[^a-zA-Z0-9-]/g, "")}`;
|
|
59
|
+
const inputRef = useRef(null);
|
|
60
|
+
useImperativeHandle(forwarded, () => inputRef.current);
|
|
61
|
+
const shellRef = useRef(null);
|
|
62
|
+
const bodyRef = useRef(null);
|
|
63
|
+
const handleRef = useRef(null);
|
|
64
|
+
const cursorRef = useRef(null);
|
|
65
|
+
const fine = fineStep ?? step / 10;
|
|
66
|
+
const coarse = coarseStep ?? step * 10;
|
|
67
|
+
const places = precision ?? Math.max(decimalsOf(step), decimalsOf(fine));
|
|
68
|
+
const canWrap = wrap && Number.isFinite(min) && Number.isFinite(max) && max > min;
|
|
69
|
+
const showTrack = (track ?? true) && Number.isFinite(min) && Number.isFinite(max) && max > min;
|
|
70
|
+
const [inner, setInner] = useState(defaultValue);
|
|
71
|
+
const current = value ?? inner;
|
|
72
|
+
const [innerUnit, setInnerUnit] = useState(defaultUnit ?? unitProp ?? units?.[0] ?? "");
|
|
73
|
+
const unit = unitProp ?? innerUnit;
|
|
74
|
+
const unitList = units ?? (unit ? [unit] : []);
|
|
75
|
+
const [draft, setDraft] = useState(null);
|
|
76
|
+
const [scrubbing, setScrubbing] = useState(null);
|
|
77
|
+
const [modifier, setModifier] = useState(null);
|
|
78
|
+
const [invalid, setInvalid] = useState(false);
|
|
79
|
+
const [releasing, setReleasing] = useState(false);
|
|
80
|
+
const latest = useRef({
|
|
81
|
+
current,
|
|
82
|
+
unit
|
|
83
|
+
});
|
|
84
|
+
useEffect(() => {
|
|
85
|
+
latest.current = {
|
|
86
|
+
current,
|
|
87
|
+
unit
|
|
88
|
+
};
|
|
89
|
+
}, [current, unit]);
|
|
90
|
+
const drag = useRef(null);
|
|
91
|
+
const suppressClick = useRef(false);
|
|
92
|
+
const timers = useRef({
|
|
93
|
+
invalid: 0,
|
|
94
|
+
release: 0
|
|
95
|
+
});
|
|
96
|
+
const cursor = useRef({
|
|
97
|
+
x: 0,
|
|
98
|
+
y: 0
|
|
99
|
+
});
|
|
100
|
+
useEffect(() => {
|
|
101
|
+
const pending = timers.current;
|
|
102
|
+
return () => {
|
|
103
|
+
window.clearTimeout(pending.invalid);
|
|
104
|
+
window.clearTimeout(pending.release);
|
|
105
|
+
document.documentElement.style.removeProperty("cursor");
|
|
106
|
+
};
|
|
107
|
+
}, []);
|
|
108
|
+
const fraction = showTrack ? Math.min(1, Math.max(0, (current - min) / (max - min))) : 0;
|
|
109
|
+
/** The strain past a limit in px (positive past max): the shell stretches from its far edge and the number leans with it. */
|
|
110
|
+
function paintStrain(amount) {
|
|
111
|
+
const shell = shellRef.current;
|
|
112
|
+
const body = bodyRef.current;
|
|
113
|
+
if (shell) {
|
|
114
|
+
const width = Math.max(1, shell.offsetWidth);
|
|
115
|
+
shell.style.transform = amount === 0 ? "" : `scaleX(${1 + Math.abs(amount) / width})`;
|
|
116
|
+
shell.style.transformOrigin = amount >= 0 ? "left" : "right";
|
|
117
|
+
}
|
|
118
|
+
if (body) body.style.transform = amount === 0 ? "" : `translateX(${amount * .5}px)`;
|
|
119
|
+
}
|
|
120
|
+
/** Lets the shell and the number spring home: a transition switched on for the moment the strain is cleared. */
|
|
121
|
+
function springHome() {
|
|
122
|
+
paintStrain(0);
|
|
123
|
+
if (prefersReducedMotion()) return;
|
|
124
|
+
setReleasing(true);
|
|
125
|
+
window.clearTimeout(timers.current.release);
|
|
126
|
+
timers.current.release = window.setTimeout(() => setReleasing(false), RELEASE_MS);
|
|
127
|
+
}
|
|
128
|
+
/** A short kick of the number toward `direction`: a step refused at a limit, or an entry that was not understood. */
|
|
129
|
+
function kick(direction) {
|
|
130
|
+
const node = bodyRef.current;
|
|
131
|
+
if (!node || prefersReducedMotion()) return;
|
|
132
|
+
node.style.setProperty("--strain-x", `${direction * KICK}px`);
|
|
133
|
+
node.removeAttribute("data-strain");
|
|
134
|
+
node.offsetWidth;
|
|
135
|
+
node.setAttribute("data-strain", "");
|
|
136
|
+
}
|
|
137
|
+
const emit = useCallback((next, source, commit, nextUnit = latest.current.unit) => {
|
|
138
|
+
if (next !== latest.current.current) {
|
|
139
|
+
latest.current = {
|
|
140
|
+
current: next,
|
|
141
|
+
unit: nextUnit
|
|
142
|
+
};
|
|
143
|
+
if (value === void 0) setInner(next);
|
|
144
|
+
onValueChange?.(next, {
|
|
145
|
+
source,
|
|
146
|
+
unit: nextUnit
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
if (commit) onValueCommit?.(next, {
|
|
150
|
+
source,
|
|
151
|
+
unit: nextUnit
|
|
152
|
+
});
|
|
153
|
+
}, [
|
|
154
|
+
onValueChange,
|
|
155
|
+
onValueCommit,
|
|
156
|
+
value
|
|
157
|
+
]);
|
|
158
|
+
const constrain = (next) => {
|
|
159
|
+
const rounded = roundTo(next, places);
|
|
160
|
+
if (canWrap) {
|
|
161
|
+
const span = max - min;
|
|
162
|
+
return roundTo(((rounded - min) % span + span) % span + min, places);
|
|
163
|
+
}
|
|
164
|
+
return Math.min(max, Math.max(min, rounded));
|
|
165
|
+
};
|
|
166
|
+
function flagInvalid() {
|
|
167
|
+
setInvalid(true);
|
|
168
|
+
window.clearTimeout(timers.current.invalid);
|
|
169
|
+
timers.current.invalid = window.setTimeout(() => setInvalid(false), INVALID_MS);
|
|
170
|
+
kick(-1);
|
|
171
|
+
}
|
|
172
|
+
function commitDraft(text = draft) {
|
|
173
|
+
setDraft(null);
|
|
174
|
+
if (text === null) return true;
|
|
175
|
+
if (text.trim() === format(current, places)) return true;
|
|
176
|
+
const parsed = evaluateScrubExpression(text, unitList);
|
|
177
|
+
if (!parsed) {
|
|
178
|
+
flagInvalid();
|
|
179
|
+
return false;
|
|
180
|
+
}
|
|
181
|
+
let nextUnit = latest.current.unit;
|
|
182
|
+
if (parsed.unit && parsed.unit !== nextUnit) {
|
|
183
|
+
nextUnit = parsed.unit;
|
|
184
|
+
if (unitProp === void 0) setInnerUnit(nextUnit);
|
|
185
|
+
onUnitChange?.(nextUnit);
|
|
186
|
+
}
|
|
187
|
+
if (!canWrap && (parsed.value > max || parsed.value < min)) kick(parsed.value > max ? 1 : -1);
|
|
188
|
+
emit(constrain(parsed.value), "type", true, nextUnit);
|
|
189
|
+
return true;
|
|
190
|
+
}
|
|
191
|
+
function stepBy(direction, event) {
|
|
192
|
+
const amount = event.shiftKey ? coarse : event.altKey ? fine : step;
|
|
193
|
+
const base = latest.current.current;
|
|
194
|
+
if (!canWrap && (direction > 0 && base >= max || direction < 0 && base <= min)) {
|
|
195
|
+
kick(direction);
|
|
196
|
+
return;
|
|
197
|
+
}
|
|
198
|
+
const snapped = event.shiftKey ? (direction > 0 ? Math.floor(base / amount + 1e-9) + 1 : Math.ceil(base / amount - 1e-9) - 1) * amount : base + direction * amount;
|
|
199
|
+
emit(constrain(snapped), "key", true);
|
|
200
|
+
}
|
|
201
|
+
const endScrub = useCallback((cancel) => {
|
|
202
|
+
const state = drag.current;
|
|
203
|
+
drag.current = null;
|
|
204
|
+
if (!state) return;
|
|
205
|
+
document.documentElement.style.removeProperty("cursor");
|
|
206
|
+
if (document.pointerLockElement === handleRef.current) document.exitPointerLock();
|
|
207
|
+
setScrubbing(null);
|
|
208
|
+
setModifier(null);
|
|
209
|
+
if (!state.moved) return;
|
|
210
|
+
suppressClick.current = true;
|
|
211
|
+
if (cancel) emit(state.start, "scrub", true);
|
|
212
|
+
else onValueCommit?.(latest.current.current, {
|
|
213
|
+
source: "scrub",
|
|
214
|
+
unit: latest.current.unit
|
|
215
|
+
});
|
|
216
|
+
springHome();
|
|
217
|
+
}, [emit, onValueCommit]);
|
|
218
|
+
useEffect(() => {
|
|
219
|
+
if (!scrubbing) return;
|
|
220
|
+
const onKey = (event) => {
|
|
221
|
+
if (event.key === "Escape") {
|
|
222
|
+
event.preventDefault();
|
|
223
|
+
endScrub(true);
|
|
224
|
+
return;
|
|
225
|
+
}
|
|
226
|
+
if (event.key === "Shift" || event.key === "Alt") setModifier(event.shiftKey ? "coarse" : event.altKey ? "fine" : null);
|
|
227
|
+
};
|
|
228
|
+
const onLock = () => {
|
|
229
|
+
if (drag.current?.locked && document.pointerLockElement !== handleRef.current) endScrub(false);
|
|
230
|
+
};
|
|
231
|
+
window.addEventListener("keydown", onKey);
|
|
232
|
+
window.addEventListener("keyup", onKey);
|
|
233
|
+
document.addEventListener("pointerlockchange", onLock);
|
|
234
|
+
return () => {
|
|
235
|
+
window.removeEventListener("keydown", onKey);
|
|
236
|
+
window.removeEventListener("keyup", onKey);
|
|
237
|
+
document.removeEventListener("pointerlockchange", onLock);
|
|
238
|
+
};
|
|
239
|
+
}, [endScrub, scrubbing]);
|
|
240
|
+
function placeCursor() {
|
|
241
|
+
const node = cursorRef.current;
|
|
242
|
+
if (node) node.style.transform = `translate3d(${cursor.current.x}px, ${cursor.current.y}px, 0)`;
|
|
243
|
+
}
|
|
244
|
+
function onHandleDown(event) {
|
|
245
|
+
if (disabled || drag.current || event.pointerType === "mouse" && event.button !== 0) return;
|
|
246
|
+
event.preventDefault();
|
|
247
|
+
if (draft !== null) commitDraft();
|
|
248
|
+
try {
|
|
249
|
+
event.currentTarget.setPointerCapture(event.pointerId);
|
|
250
|
+
} catch {}
|
|
251
|
+
drag.current = {
|
|
252
|
+
id: event.pointerId,
|
|
253
|
+
lastX: event.clientX,
|
|
254
|
+
travel: 0,
|
|
255
|
+
raw: latest.current.current,
|
|
256
|
+
start: latest.current.current,
|
|
257
|
+
moved: false,
|
|
258
|
+
locked: false,
|
|
259
|
+
mouse: event.pointerType === "mouse"
|
|
260
|
+
};
|
|
261
|
+
cursor.current = {
|
|
262
|
+
x: event.clientX,
|
|
263
|
+
y: event.clientY
|
|
264
|
+
};
|
|
265
|
+
}
|
|
266
|
+
function onHandleMove(event) {
|
|
267
|
+
const state = drag.current;
|
|
268
|
+
if (!state || event.pointerId !== state.id) return;
|
|
269
|
+
const locked = document.pointerLockElement === handleRef.current;
|
|
270
|
+
const dx = locked ? event.movementX : event.clientX - state.lastX;
|
|
271
|
+
state.lastX = event.clientX;
|
|
272
|
+
state.travel += dx;
|
|
273
|
+
if (!state.moved) {
|
|
274
|
+
if (Math.abs(state.travel) < SLOP) return;
|
|
275
|
+
state.moved = true;
|
|
276
|
+
inputRef.current?.blur();
|
|
277
|
+
document.documentElement.style.cursor = "ew-resize";
|
|
278
|
+
if (pointerLock && state.mouse && handleRef.current?.requestPointerLock) try {
|
|
279
|
+
const request = handleRef.current.requestPointerLock();
|
|
280
|
+
if (request instanceof Promise) request.catch(() => {});
|
|
281
|
+
} catch {}
|
|
282
|
+
setScrubbing({ locked: false });
|
|
283
|
+
}
|
|
284
|
+
if (locked && !state.locked) {
|
|
285
|
+
state.locked = true;
|
|
286
|
+
setScrubbing({ locked: true });
|
|
287
|
+
}
|
|
288
|
+
if (locked) {
|
|
289
|
+
const width = window.innerWidth;
|
|
290
|
+
cursor.current.x = ((cursor.current.x + dx) % width + width) % width;
|
|
291
|
+
} else cursor.current = {
|
|
292
|
+
x: event.clientX,
|
|
293
|
+
y: event.clientY
|
|
294
|
+
};
|
|
295
|
+
placeCursor();
|
|
296
|
+
const amount = event.shiftKey ? coarse : event.altKey ? fine : step;
|
|
297
|
+
const mode = event.shiftKey ? "coarse" : event.altKey ? "fine" : null;
|
|
298
|
+
if (mode !== modifier) setModifier(mode);
|
|
299
|
+
state.raw += dx / pixelsPerStep * amount;
|
|
300
|
+
if (!canWrap) {
|
|
301
|
+
const perPx = amount / pixelsPerStep;
|
|
302
|
+
const over = state.raw > max ? (state.raw - max) / perPx : state.raw < min ? (state.raw - min) / perPx : 0;
|
|
303
|
+
if (Math.abs(over) > OVERSHOOT_CAP) state.raw = (over > 0 ? max : min) + Math.sign(over) * OVERSHOOT_CAP * perPx;
|
|
304
|
+
paintStrain(prefersReducedMotion() ? 0 : rubber(Math.max(-90, Math.min(OVERSHOOT_CAP, over))));
|
|
305
|
+
}
|
|
306
|
+
const next = constrain(Math.round(state.raw / amount) * amount);
|
|
307
|
+
if (next !== latest.current.current) emit(next, "scrub", false);
|
|
308
|
+
}
|
|
309
|
+
function onHandleUp(event) {
|
|
310
|
+
const state = drag.current;
|
|
311
|
+
if (!state || event.pointerId !== state.id) return;
|
|
312
|
+
endScrub(false);
|
|
313
|
+
}
|
|
314
|
+
function onHandleClick(event) {
|
|
315
|
+
if (suppressClick.current) {
|
|
316
|
+
suppressClick.current = false;
|
|
317
|
+
event.preventDefault();
|
|
318
|
+
return;
|
|
319
|
+
}
|
|
320
|
+
if (disabled) return;
|
|
321
|
+
event.preventDefault();
|
|
322
|
+
inputRef.current?.focus();
|
|
323
|
+
inputRef.current?.select();
|
|
324
|
+
}
|
|
325
|
+
function onHandleDoubleClick() {
|
|
326
|
+
if (disabled || resetValue === void 0) return;
|
|
327
|
+
emit(constrain(resetValue), "reset", true);
|
|
328
|
+
}
|
|
329
|
+
function onInputKeyDown(event) {
|
|
330
|
+
onKeyDown?.(event);
|
|
331
|
+
if (event.defaultPrevented) return;
|
|
332
|
+
if (event.key === "ArrowUp" || event.key === "ArrowDown") {
|
|
333
|
+
event.preventDefault();
|
|
334
|
+
if (draft !== null && !commitDraft()) return;
|
|
335
|
+
stepBy(event.key === "ArrowUp" ? 1 : -1, event);
|
|
336
|
+
requestAnimationFrame(() => inputRef.current?.select());
|
|
337
|
+
return;
|
|
338
|
+
}
|
|
339
|
+
if (event.key === "Enter") {
|
|
340
|
+
event.preventDefault();
|
|
341
|
+
commitDraft();
|
|
342
|
+
requestAnimationFrame(() => inputRef.current?.select());
|
|
343
|
+
return;
|
|
344
|
+
}
|
|
345
|
+
if (event.key === "Escape" && draft !== null) {
|
|
346
|
+
event.preventDefault();
|
|
347
|
+
event.stopPropagation();
|
|
348
|
+
setDraft(null);
|
|
349
|
+
requestAnimationFrame(() => inputRef.current?.select());
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
function onInputFocus(event) {
|
|
353
|
+
onFocus?.(event);
|
|
354
|
+
event.currentTarget.select();
|
|
355
|
+
}
|
|
356
|
+
function onInputBlur(event) {
|
|
357
|
+
onBlur?.(event);
|
|
358
|
+
commitDraft();
|
|
359
|
+
}
|
|
360
|
+
function cycleUnit() {
|
|
361
|
+
if (unitList.length < 2) return;
|
|
362
|
+
const next = unitList[(unitList.indexOf(unit) + 1) % unitList.length] ?? unit;
|
|
363
|
+
if (unitProp === void 0) setInnerUnit(next);
|
|
364
|
+
onUnitChange?.(next);
|
|
365
|
+
latest.current = {
|
|
366
|
+
...latest.current,
|
|
367
|
+
unit: next
|
|
368
|
+
};
|
|
369
|
+
}
|
|
370
|
+
const text = draft ?? format(current, places);
|
|
371
|
+
const spokenUnit = unit === "%" ? "percent" : unit === "deg" || unit === "°" ? "degrees" : unit;
|
|
372
|
+
const valueText = `${format(current, places)}${unit ? ` ${spokenUnit}` : ""}`;
|
|
373
|
+
const multiplier = modifier === "coarse" ? `×${format(coarse / step, 2)}` : modifier === "fine" ? `×${format(fine / step, 3)}` : null;
|
|
374
|
+
const labelIsText = typeof label === "string" || typeof label === "number";
|
|
375
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
376
|
+
"data-slot": "scrub-input",
|
|
377
|
+
"data-size": size,
|
|
378
|
+
"data-disabled": disabled ? "" : void 0,
|
|
379
|
+
"data-scrubbing": scrubbing ? "" : void 0,
|
|
380
|
+
"data-invalid": invalid ? "" : void 0,
|
|
381
|
+
"data-releasing": releasing ? "" : void 0,
|
|
382
|
+
className: cn(cnAll("group/scrub relative isolate grid w-full min-w-0 grid-cols-[auto_minmax(0,1fr)] items-stretch font-sans text-foreground", sizeClass[size], "data-[disabled]:opacity-(--opacity-disabled)"), className),
|
|
383
|
+
children: [
|
|
384
|
+
/* @__PURE__ */ jsx("div", {
|
|
385
|
+
ref: shellRef,
|
|
386
|
+
"aria-hidden": "true",
|
|
387
|
+
"data-slot": "scrub-input-shell",
|
|
388
|
+
className: cnAll("absolute inset-0 -z-10 overflow-hidden border-(length:--border-width) border-transparent bg-surface-muted will-change-transform", size === "sm" ? "rounded-sm" : "rounded-md", "motion-safe:transition-[background-color,border-color] motion-safe:duration-(--duration-quick) motion-safe:ease-settle", "group-data-[releasing]/scrub:motion-safe:transition-[transform,background-color,border-color] group-data-[releasing]/scrub:motion-safe:duration-(--duration-smooth) group-data-[releasing]/scrub:motion-safe:ease-smooth", "group-hover/scrub:border-border", "group-focus-within/scrub:border-foreground group-focus-within/scrub:bg-surface", "forced-colors:group-focus-within/scrub:outline-solid forced-colors:group-focus-within/scrub:outline-(length:--focus-ring-width) forced-colors:group-focus-within/scrub:outline-offset-(--focus-ring-offset)", "group-data-[scrubbing]/scrub:border-border-strong", "group-data-[invalid]/scrub:border-destructive-strong"),
|
|
389
|
+
children: showTrack ? /* @__PURE__ */ jsx("span", {
|
|
390
|
+
"data-slot": "scrub-input-fill",
|
|
391
|
+
style: { transform: `scaleX(${fraction})` },
|
|
392
|
+
className: "pointer-events-none absolute inset-0 origin-left bg-foreground/6 group-data-[scrubbing]/scrub:bg-foreground/10 motion-safe:transition-transform motion-safe:duration-(--duration-smooth) motion-safe:ease-smooth group-data-[scrubbing]/scrub:motion-safe:transition-none"
|
|
393
|
+
}) : null
|
|
394
|
+
}),
|
|
395
|
+
/* @__PURE__ */ jsx("label", {
|
|
396
|
+
ref: handleRef,
|
|
397
|
+
htmlFor: id,
|
|
398
|
+
"data-slot": "scrub-input-handle",
|
|
399
|
+
"data-text": labelIsText ? "" : void 0,
|
|
400
|
+
onPointerDown: onHandleDown,
|
|
401
|
+
onPointerMove: onHandleMove,
|
|
402
|
+
onPointerUp: onHandleUp,
|
|
403
|
+
onPointerCancel: onHandleUp,
|
|
404
|
+
onClick: onHandleClick,
|
|
405
|
+
onDoubleClick: onHandleDoubleClick,
|
|
406
|
+
className: "flex min-w-7.5 cursor-ew-resize touch-pan-y items-center justify-center ps-2.5 pe-1 text-text-secondary select-none motion-safe:transition-colors motion-safe:duration-(--duration-quick) data-[text]:font-medium hover:text-foreground group-data-[scrubbing]/scrub:text-foreground group-data-[disabled]/scrub:cursor-not-allowed [&_svg]:size-3.5 [&_svg]:flex-none",
|
|
407
|
+
children: label
|
|
408
|
+
}),
|
|
409
|
+
/* @__PURE__ */ jsxs("div", {
|
|
410
|
+
ref: bodyRef,
|
|
411
|
+
"data-slot": "scrub-input-body",
|
|
412
|
+
className: cnAll("flex min-w-0 items-center gap-0.5 pe-2.5 data-[strain]:motion-safe:animate-field-strain", "group-data-[releasing]/scrub:motion-safe:transition-transform group-data-[releasing]/scrub:motion-safe:duration-(--duration-smooth) group-data-[releasing]/scrub:motion-safe:ease-smooth"),
|
|
413
|
+
children: [/* @__PURE__ */ jsx("input", {
|
|
414
|
+
...rest,
|
|
415
|
+
ref: inputRef,
|
|
416
|
+
id,
|
|
417
|
+
type: "text",
|
|
418
|
+
inputMode: "decimal",
|
|
419
|
+
autoComplete: "off",
|
|
420
|
+
spellCheck: false,
|
|
421
|
+
role: "spinbutton",
|
|
422
|
+
"aria-label": ariaLabel,
|
|
423
|
+
"aria-valuenow": current,
|
|
424
|
+
"aria-valuemin": Number.isFinite(min) ? min : void 0,
|
|
425
|
+
"aria-valuemax": Number.isFinite(max) ? max : void 0,
|
|
426
|
+
"aria-valuetext": valueText,
|
|
427
|
+
"aria-invalid": invalid || void 0,
|
|
428
|
+
disabled,
|
|
429
|
+
value: text,
|
|
430
|
+
onChange: (event) => setDraft(event.target.value),
|
|
431
|
+
onFocus: onInputFocus,
|
|
432
|
+
onBlur: onInputBlur,
|
|
433
|
+
onKeyDown: onInputKeyDown,
|
|
434
|
+
className: cnAll("m-0 h-full w-full min-w-0 flex-1 border-0 bg-transparent p-0 ps-1 font-sans text-foreground tabular-nums outline-none selection:bg-primary selection:text-primary-foreground disabled:cursor-not-allowed", size === "sm" ? "text-xs" : "text-sm")
|
|
435
|
+
}), unit ? unitList.length > 1 ? /* @__PURE__ */ jsx(Button, {
|
|
436
|
+
"data-slot": "scrub-input-unit",
|
|
437
|
+
isDisabled: disabled,
|
|
438
|
+
onPress: cycleUnit,
|
|
439
|
+
"aria-label": `Unit: ${unit}. Change unit`,
|
|
440
|
+
className: "relative inline-flex h-6 min-w-6 flex-none cursor-pointer items-center justify-center rounded-xs border-0 bg-transparent px-1 text-xs text-text-secondary outline-none motion-safe:transition-[background-color,color,scale] motion-safe:duration-(--duration-quick) motion-safe:ease-settle data-[hovered]:bg-foreground/7 data-[hovered]:text-foreground data-[pressed]:scale-95 data-[focus-visible]:outline-solid data-[focus-visible]:outline-(length:--focus-ring-width) data-[focus-visible]:outline-(--color-ring) data-[disabled]:cursor-not-allowed",
|
|
441
|
+
children: /* @__PURE__ */ jsx(SwapText, {
|
|
442
|
+
swapKey: unit,
|
|
443
|
+
motion: "rise",
|
|
444
|
+
children: unit
|
|
445
|
+
})
|
|
446
|
+
}) : /* @__PURE__ */ jsx("span", {
|
|
447
|
+
"aria-hidden": "true",
|
|
448
|
+
"data-slot": "scrub-input-unit",
|
|
449
|
+
className: "inline-flex h-6 min-w-4.5 flex-none items-center justify-center px-1 text-xs text-text-secondary",
|
|
450
|
+
children: unit
|
|
451
|
+
}) : null]
|
|
452
|
+
}),
|
|
453
|
+
multiplier && !scrubbing?.locked ? /* @__PURE__ */ jsx("span", {
|
|
454
|
+
"aria-hidden": "true",
|
|
455
|
+
"data-slot": "scrub-input-multiplier",
|
|
456
|
+
className: "pointer-events-none absolute bottom-full mb-1.5 start-1.5 z-10 rounded-pill bg-foreground px-1.75 py-0.5 text-xs leading-4 text-background tabular-nums motion-safe:animate-field-clear-in motion-reduce:animate-fade-in",
|
|
457
|
+
children: multiplier
|
|
458
|
+
}) : null,
|
|
459
|
+
scrubbing?.locked ? createPortal(/* @__PURE__ */ jsxs("div", {
|
|
460
|
+
ref: (node) => {
|
|
461
|
+
cursorRef.current = node;
|
|
462
|
+
placeCursor();
|
|
463
|
+
},
|
|
464
|
+
"aria-hidden": "true",
|
|
465
|
+
"data-slot": "scrub-input-cursor",
|
|
466
|
+
className: "pointer-events-none fixed top-0 left-0 z-50 -mt-2 -ms-3.25 flex items-center gap-1",
|
|
467
|
+
children: [/* @__PURE__ */ jsx("svg", {
|
|
468
|
+
width: "26",
|
|
469
|
+
height: "16",
|
|
470
|
+
viewBox: "0 0 26 16",
|
|
471
|
+
className: "block fill-foreground stroke-background drop-shadow-sm",
|
|
472
|
+
strokeWidth: "1.4",
|
|
473
|
+
strokeLinejoin: "round",
|
|
474
|
+
children: /* @__PURE__ */ jsx("path", { d: "M1.5 8 L7.5 2.5 V6 H18.5 V2.5 L24.5 8 L18.5 13.5 V10 H7.5 V13.5 Z" })
|
|
475
|
+
}), multiplier ? /* @__PURE__ */ jsx("span", {
|
|
476
|
+
className: "rounded-pill bg-foreground px-1.5 py-px font-sans text-xs leading-4 text-background tabular-nums motion-safe:animate-field-clear-in motion-reduce:animate-fade-in",
|
|
477
|
+
children: multiplier
|
|
478
|
+
}) : null]
|
|
479
|
+
}), document.body) : null
|
|
480
|
+
]
|
|
481
|
+
});
|
|
482
|
+
});
|
|
483
|
+
//#endregion
|
|
484
|
+
export { ScrubInput };
|
|
485
|
+
|
|
486
|
+
//# sourceMappingURL=scrub-input.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scrub-input.js","names":["AriaButton"],"sources":["../../src/scrub-input/scrub-input.tsx"],"sourcesContent":["\"use client\";\n\nimport {\n forwardRef,\n useCallback,\n useEffect,\n useId,\n useImperativeHandle,\n useRef,\n useState,\n type FocusEvent,\n type InputHTMLAttributes,\n type KeyboardEvent as ReactKeyboardEvent,\n type MouseEvent as ReactMouseEvent,\n type PointerEvent as ReactPointerEvent,\n type ReactNode,\n} from \"react\";\nimport { createPortal } from \"react-dom\";\nimport { Button as AriaButton } from \"react-aria-components\";\n\nimport { cn, cnAll } from \"../lib/cn.js\";\nimport { prefersReducedMotion } from \"../lib/motion.js\";\nimport { SwapText } from \"../lib/swap-text.js\";\nimport { evaluateScrubExpression } from \"./evaluate.js\";\n\nexport type ScrubInputSource = \"scrub\" | \"type\" | \"key\" | \"reset\";\n\nexport interface ScrubInputChangeDetails {\n /** What produced the value: a label drag, a typed entry, an arrow key, or a double click reset. */\n source: ScrubInputSource;\n /** The unit in effect after the change. */\n unit: string;\n}\n\nexport interface ScrubInputProps extends Omit<\n InputHTMLAttributes<HTMLInputElement>,\n | \"value\"\n | \"defaultValue\"\n | \"onChange\"\n | \"type\"\n | \"size\"\n | \"min\"\n | \"max\"\n | \"step\"\n | \"prefix\"\n | \"className\"\n> {\n /** Text or a 16px icon. It is the scrub handle. An icon label needs `aria-label` so the field keeps an accessible name. */\n label: ReactNode;\n /** The value. Controlled when given. */\n value?: number;\n /** @default 0 */\n defaultValue?: number;\n /** Fires for every change, including each step of a scrub. */\n onValueChange?: (value: number, details: ScrubInputChangeDetails) => void;\n /** Fires once when a change settles: a scrub ends, Enter or blur commits typing, or a key step lands. Save on this. */\n onValueCommit?: (value: number, details: ScrubInputChangeDetails) => void;\n /** @default -Infinity */\n min?: number;\n /** @default Infinity */\n max?: number;\n /** One step for the arrow keys and for scrubbing. @default 1 */\n step?: number;\n /** The step with Alt held. @default step / 10 */\n fineStep?: number;\n /** The step with Shift held; coarse steps land on the coarse grid. @default step * 10 */\n coarseStep?: number;\n /** Decimal places kept. Defaults to the precision of `step` or `fineStep`. */\n precision?: number;\n /** Pointer travel in px for one step while scrubbing. @default 2 */\n pixelsPerStep?: number;\n /** Wraps from `max` back to `min`, as a rotation does. Needs both limits. @default false */\n wrap?: boolean;\n /** The unit shown after the number. Controlled when given. With more than one entry in `units` it is a button that cycles them. */\n unit?: string;\n defaultUnit?: string;\n /** The units a typed value may carry, such as `[\"px\", \"%\"]`. Defaults to the current unit. */\n units?: readonly string[];\n onUnitChange?: (unit: string) => void;\n /** Double click the label to return here. */\n resetValue?: number;\n /** Shows where the value sits between `min` and `max` as a quiet fill. On by default when both limits are set. */\n track?: boolean;\n /** `md` is the compact control height, `sm` is 30px. @default \"md\" */\n size?: \"sm\" | \"md\";\n /** Locks the mouse pointer while it scrubs, when the browser allows it, so a long drag never runs out of screen. @default true */\n pointerLock?: boolean;\n /** Extra classes for the field. Merged last, so `w-40` sizes it. */\n className?: string;\n}\n\n/** Movement before a press on the label becomes a scrub, and the most pixels the field strains past a limit. */\nconst SLOP = 3;\nconst STRAIN = 12;\n/** The most overshoot kept, so reversing starts moving the value again after a short relax. */\nconst OVERSHOOT_CAP = 90;\n/** How far a refused step or an invalid entry kicks the number (px). */\nconst KICK = 6;\n/** The longest the spring home takes (the smooth spring, 720ms) plus a margin. */\nconst RELEASE_MS = 900;\n/** How long an invalid entry is flagged. */\nconst INVALID_MS = 900;\n\nconst decimalsOf = (value: number) => {\n const text = String(value);\n if (text.includes(\"e-\")) return Number(text.split(\"e-\")[1]);\n return (text.split(\".\")[1] ?? \"\").length;\n};\nconst roundTo = (value: number, places: number) => {\n const factor = 10 ** places;\n return Math.round(value * factor) / factor;\n};\n/** Rubber-band resistance: each extra pixel gives less, and the strain never exceeds `limit`. */\nconst rubber = (distance: number, limit = STRAIN) =>\n Math.sign(distance) * (1 - 1 / ((Math.abs(distance) * 0.55) / limit + 1)) * limit;\nconst format = (value: number, places: number) =>\n String(roundTo(value, places)).replace(/^-0$/, \"0\");\n\ninterface Drag {\n id: number;\n lastX: number;\n travel: number;\n raw: number;\n start: number;\n moved: boolean;\n locked: boolean;\n mouse: boolean;\n}\n\nconst sizeClass = {\n md: \"h-(--density-control-h-sm) rounded-md text-sm\",\n sm: \"h-7.5 rounded-sm text-xs\",\n} as const;\n\n/**\n * A compact number field for inspectors and property panels. Drag the label sideways to\n * scrub the value (the mouse pointer is locked when the browser allows it, so a long drag\n * never runs out of screen), or click in and type an exact value. A typed entry takes\n * arithmetic such as `12*2` or `(100-24)/2`, and a unit from `units` such as `50%`.\n *\n * Keyboard: the arrow keys step (Shift ten times coarser, Alt ten times finer), Enter\n * commits typing, Escape restores the last value (or cancels a scrub back to where it\n * began). A double click on the label restores `resetValue`. The input is a spinbutton, and\n * its value text spells out percent and degrees.\n *\n * At a limit the field strains: dragged past `min` or `max` it stretches against\n * rubber-band resistance and springs back on release, and a refused step or an invalid\n * entry kicks the number. Reduced motion removes the strain, the kick and the fill's glide.\n *\n * @example\n * <ScrubInput label=\"W\" value={width} onValueChange={setWidth} min={0} units={[\"px\", \"%\"]} />\n */\nexport const ScrubInput = forwardRef<HTMLInputElement, ScrubInputProps>(function ScrubInput(\n {\n label,\n value,\n defaultValue = 0,\n onValueChange,\n onValueCommit,\n min = -Infinity,\n max = Infinity,\n step = 1,\n fineStep,\n coarseStep,\n precision,\n pixelsPerStep = 2,\n wrap = false,\n unit: unitProp,\n defaultUnit,\n units,\n onUnitChange,\n resetValue,\n track,\n size = \"md\",\n pointerLock = true,\n className,\n disabled,\n id: idProp,\n onFocus,\n onBlur,\n onKeyDown,\n \"aria-label\": ariaLabel,\n ...rest\n },\n forwarded,\n) {\n const uid = useId();\n const id = idProp ?? `scrub-${uid.replace(/[^a-zA-Z0-9-]/g, \"\")}`;\n const inputRef = useRef<HTMLInputElement>(null);\n useImperativeHandle(forwarded, () => inputRef.current as HTMLInputElement);\n const shellRef = useRef<HTMLDivElement>(null);\n const bodyRef = useRef<HTMLDivElement>(null);\n const handleRef = useRef<HTMLLabelElement>(null);\n const cursorRef = useRef<HTMLDivElement>(null);\n\n const fine = fineStep ?? step / 10;\n const coarse = coarseStep ?? step * 10;\n const places = precision ?? Math.max(decimalsOf(step), decimalsOf(fine));\n const canWrap = wrap && Number.isFinite(min) && Number.isFinite(max) && max > min;\n const showTrack = (track ?? true) && Number.isFinite(min) && Number.isFinite(max) && max > min;\n\n const [inner, setInner] = useState(defaultValue);\n const current = value ?? inner;\n const [innerUnit, setInnerUnit] = useState(defaultUnit ?? unitProp ?? units?.[0] ?? \"\");\n const unit = unitProp ?? innerUnit;\n const unitList = units ?? (unit ? [unit] : []);\n const [draft, setDraft] = useState<string | null>(null);\n const [scrubbing, setScrubbing] = useState<null | { locked: boolean }>(null);\n const [modifier, setModifier] = useState<\"fine\" | \"coarse\" | null>(null);\n const [invalid, setInvalid] = useState(false);\n const [releasing, setReleasing] = useState(false);\n\n // What the handlers read, so a scrub in flight never sees a stale render.\n const latest = useRef({ current, unit });\n useEffect(() => {\n latest.current = { current, unit };\n }, [current, unit]);\n const drag = useRef<Drag | null>(null);\n const suppressClick = useRef(false);\n const timers = useRef({ invalid: 0, release: 0 });\n const cursor = useRef({ x: 0, y: 0 });\n useEffect(() => {\n const pending = timers.current;\n return () => {\n window.clearTimeout(pending.invalid);\n window.clearTimeout(pending.release);\n document.documentElement.style.removeProperty(\"cursor\");\n };\n }, []);\n\n const fraction = showTrack ? Math.min(1, Math.max(0, (current - min) / (max - min))) : 0;\n\n /** The strain past a limit in px (positive past max): the shell stretches from its far edge and the number leans with it. */\n function paintStrain(amount: number) {\n const shell = shellRef.current;\n const body = bodyRef.current;\n if (shell) {\n const width = Math.max(1, shell.offsetWidth);\n shell.style.transform = amount === 0 ? \"\" : `scaleX(${1 + Math.abs(amount) / width})`;\n shell.style.transformOrigin = amount >= 0 ? \"left\" : \"right\";\n }\n if (body) body.style.transform = amount === 0 ? \"\" : `translateX(${amount * 0.5}px)`;\n }\n\n /** Lets the shell and the number spring home: a transition switched on for the moment the strain is cleared. */\n function springHome() {\n paintStrain(0);\n if (prefersReducedMotion()) return;\n setReleasing(true);\n window.clearTimeout(timers.current.release);\n timers.current.release = window.setTimeout(() => setReleasing(false), RELEASE_MS);\n }\n\n /** A short kick of the number toward `direction`: a step refused at a limit, or an entry that was not understood. */\n function kick(direction: number) {\n const node = bodyRef.current;\n if (!node || prefersReducedMotion()) return;\n node.style.setProperty(\"--strain-x\", `${direction * KICK}px`);\n // Restart the animation: the attribute has to leave and come back across a style recalculation.\n node.removeAttribute(\"data-strain\");\n void node.offsetWidth;\n node.setAttribute(\"data-strain\", \"\");\n }\n\n const emit = useCallback(\n (next: number, source: ScrubInputSource, commit: boolean, nextUnit = latest.current.unit) => {\n if (next !== latest.current.current) {\n latest.current = { current: next, unit: nextUnit };\n if (value === undefined) setInner(next);\n onValueChange?.(next, { source, unit: nextUnit });\n }\n if (commit) onValueCommit?.(next, { source, unit: nextUnit });\n },\n [onValueChange, onValueCommit, value],\n );\n\n const constrain = (next: number) => {\n const rounded = roundTo(next, places);\n if (canWrap) {\n const span = max - min;\n return roundTo(((((rounded - min) % span) + span) % span) + min, places);\n }\n return Math.min(max, Math.max(min, rounded));\n };\n\n function flagInvalid() {\n setInvalid(true);\n window.clearTimeout(timers.current.invalid);\n timers.current.invalid = window.setTimeout(() => setInvalid(false), INVALID_MS);\n kick(-1);\n }\n\n function commitDraft(text: string | null = draft) {\n setDraft(null);\n if (text === null) return true;\n if (text.trim() === format(current, places)) return true;\n const parsed = evaluateScrubExpression(text, unitList);\n if (!parsed) {\n flagInvalid();\n return false;\n }\n let nextUnit = latest.current.unit;\n if (parsed.unit && parsed.unit !== nextUnit) {\n nextUnit = parsed.unit;\n if (unitProp === undefined) setInnerUnit(nextUnit);\n onUnitChange?.(nextUnit);\n }\n if (!canWrap && (parsed.value > max || parsed.value < min)) kick(parsed.value > max ? 1 : -1);\n emit(constrain(parsed.value), \"type\", true, nextUnit);\n return true;\n }\n\n function stepBy(direction: 1 | -1, event: { shiftKey: boolean; altKey: boolean }) {\n const amount = event.shiftKey ? coarse : event.altKey ? fine : step;\n const base = latest.current.current;\n if (!canWrap && ((direction > 0 && base >= max) || (direction < 0 && base <= min))) {\n kick(direction);\n return;\n }\n // Coarse steps land on the coarse grid, so 13 with Shift goes to 20, not 23.\n const snapped = event.shiftKey\n ? (direction > 0\n ? Math.floor(base / amount + 1e-9) + 1\n : Math.ceil(base / amount - 1e-9) - 1) * amount\n : base + direction * amount;\n emit(constrain(snapped), \"key\", true);\n }\n\n // ---- Scrubbing ----\n\n const endScrub = useCallback(\n (cancel: boolean) => {\n const state = drag.current;\n drag.current = null;\n if (!state) return;\n document.documentElement.style.removeProperty(\"cursor\");\n if (document.pointerLockElement === handleRef.current) document.exitPointerLock();\n setScrubbing(null);\n setModifier(null);\n if (!state.moved) return;\n suppressClick.current = true;\n if (cancel) emit(state.start, \"scrub\", true);\n else onValueCommit?.(latest.current.current, { source: \"scrub\", unit: latest.current.unit });\n springHome();\n },\n // springHome only reads refs and sets state.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [emit, onValueCommit],\n );\n\n useEffect(() => {\n if (!scrubbing) return;\n const onKey = (event: KeyboardEvent) => {\n if (event.key === \"Escape\") {\n event.preventDefault();\n endScrub(true);\n return;\n }\n if (event.key === \"Shift\" || event.key === \"Alt\")\n setModifier(event.shiftKey ? \"coarse\" : event.altKey ? \"fine\" : null);\n };\n const onLock = () => {\n if (drag.current?.locked && document.pointerLockElement !== handleRef.current)\n endScrub(false);\n };\n window.addEventListener(\"keydown\", onKey);\n window.addEventListener(\"keyup\", onKey);\n document.addEventListener(\"pointerlockchange\", onLock);\n return () => {\n window.removeEventListener(\"keydown\", onKey);\n window.removeEventListener(\"keyup\", onKey);\n document.removeEventListener(\"pointerlockchange\", onLock);\n };\n }, [endScrub, scrubbing]);\n\n function placeCursor() {\n const node = cursorRef.current;\n if (node) node.style.transform = `translate3d(${cursor.current.x}px, ${cursor.current.y}px, 0)`;\n }\n\n function onHandleDown(event: ReactPointerEvent<HTMLLabelElement>) {\n if (disabled || drag.current || (event.pointerType === \"mouse\" && event.button !== 0)) return;\n event.preventDefault();\n if (draft !== null) commitDraft();\n try {\n event.currentTarget.setPointerCapture(event.pointerId);\n } catch {\n // A pointer the browser is not tracking (a synthetic one) cannot be captured; the drag follows it all the same.\n }\n drag.current = {\n id: event.pointerId,\n lastX: event.clientX,\n travel: 0,\n raw: latest.current.current,\n start: latest.current.current,\n moved: false,\n locked: false,\n mouse: event.pointerType === \"mouse\",\n };\n cursor.current = { x: event.clientX, y: event.clientY };\n }\n\n function onHandleMove(event: ReactPointerEvent<HTMLLabelElement>) {\n const state = drag.current;\n if (!state || event.pointerId !== state.id) return;\n const locked = document.pointerLockElement === handleRef.current;\n const dx = locked ? event.movementX : event.clientX - state.lastX;\n state.lastX = event.clientX;\n state.travel += dx;\n if (!state.moved) {\n if (Math.abs(state.travel) < SLOP) return;\n state.moved = true;\n inputRef.current?.blur();\n document.documentElement.style.cursor = \"ew-resize\";\n if (pointerLock && state.mouse && handleRef.current?.requestPointerLock) {\n try {\n const request = handleRef.current.requestPointerLock() as unknown;\n if (request instanceof Promise) request.catch(() => {});\n } catch {\n // Pointer lock is optional.\n }\n }\n setScrubbing({ locked: false });\n }\n if (locked && !state.locked) {\n state.locked = true;\n setScrubbing({ locked: true });\n }\n\n // A stand-in cursor replaces the locked pointer and wraps at the window edges, the way long scrubs do in design tools.\n if (locked) {\n const width = window.innerWidth;\n cursor.current.x = (((cursor.current.x + dx) % width) + width) % width;\n } else cursor.current = { x: event.clientX, y: event.clientY };\n placeCursor();\n\n const amount = event.shiftKey ? coarse : event.altKey ? fine : step;\n const mode = event.shiftKey ? \"coarse\" : event.altKey ? \"fine\" : null;\n if (mode !== modifier) setModifier(mode);\n state.raw += (dx / pixelsPerStep) * amount;\n\n if (!canWrap) {\n const perPx = amount / pixelsPerStep;\n const over =\n state.raw > max\n ? (state.raw - max) / perPx\n : state.raw < min\n ? (state.raw - min) / perPx\n : 0;\n // Cap the stored overshoot so reversing starts moving the value again after a short relax, not a long walk back.\n if (Math.abs(over) > OVERSHOOT_CAP)\n state.raw = (over > 0 ? max : min) + Math.sign(over) * OVERSHOOT_CAP * perPx;\n paintStrain(\n prefersReducedMotion()\n ? 0\n : rubber(Math.max(-OVERSHOOT_CAP, Math.min(OVERSHOOT_CAP, over))),\n );\n }\n const next = constrain(Math.round(state.raw / amount) * amount);\n if (next !== latest.current.current) emit(next, \"scrub\", false);\n }\n\n function onHandleUp(event: ReactPointerEvent<HTMLLabelElement>) {\n const state = drag.current;\n if (!state || event.pointerId !== state.id) return;\n endScrub(false);\n }\n\n function onHandleClick(event: ReactMouseEvent<HTMLLabelElement>) {\n if (suppressClick.current) {\n suppressClick.current = false;\n event.preventDefault();\n return;\n }\n if (disabled) return;\n event.preventDefault();\n inputRef.current?.focus();\n inputRef.current?.select();\n }\n\n function onHandleDoubleClick() {\n if (disabled || resetValue === undefined) return;\n emit(constrain(resetValue), \"reset\", true);\n }\n\n // ---- Typing ----\n\n function onInputKeyDown(event: ReactKeyboardEvent<HTMLInputElement>) {\n onKeyDown?.(event);\n if (event.defaultPrevented) return;\n if (event.key === \"ArrowUp\" || event.key === \"ArrowDown\") {\n event.preventDefault();\n if (draft !== null && !commitDraft()) return;\n stepBy(event.key === \"ArrowUp\" ? 1 : -1, event);\n requestAnimationFrame(() => inputRef.current?.select());\n return;\n }\n if (event.key === \"Enter\") {\n event.preventDefault();\n commitDraft();\n requestAnimationFrame(() => inputRef.current?.select());\n return;\n }\n if (event.key === \"Escape\" && draft !== null) {\n event.preventDefault();\n event.stopPropagation();\n setDraft(null);\n requestAnimationFrame(() => inputRef.current?.select());\n }\n }\n\n function onInputFocus(event: FocusEvent<HTMLInputElement>) {\n onFocus?.(event);\n event.currentTarget.select();\n }\n\n function onInputBlur(event: FocusEvent<HTMLInputElement>) {\n onBlur?.(event);\n commitDraft();\n }\n\n function cycleUnit() {\n if (unitList.length < 2) return;\n const next = unitList[(unitList.indexOf(unit) + 1) % unitList.length] ?? unit;\n if (unitProp === undefined) setInnerUnit(next);\n onUnitChange?.(next);\n latest.current = { ...latest.current, unit: next };\n }\n\n const text = draft ?? format(current, places);\n const spokenUnit = unit === \"%\" ? \"percent\" : unit === \"deg\" || unit === \"°\" ? \"degrees\" : unit;\n const valueText = `${format(current, places)}${unit ? ` ${spokenUnit}` : \"\"}`;\n const multiplier =\n modifier === \"coarse\"\n ? `×${format(coarse / step, 2)}`\n : modifier === \"fine\"\n ? `×${format(fine / step, 3)}`\n : null;\n const labelIsText = typeof label === \"string\" || typeof label === \"number\";\n\n return (\n <div\n data-slot=\"scrub-input\"\n data-size={size}\n data-disabled={disabled ? \"\" : undefined}\n data-scrubbing={scrubbing ? \"\" : undefined}\n data-invalid={invalid ? \"\" : undefined}\n data-releasing={releasing ? \"\" : undefined}\n className={cn(\n cnAll(\n \"group/scrub relative isolate grid w-full min-w-0 grid-cols-[auto_minmax(0,1fr)] items-stretch font-sans text-foreground\",\n sizeClass[size],\n \"data-[disabled]:opacity-(--opacity-disabled)\",\n ),\n className,\n )}\n >\n <div\n ref={shellRef}\n aria-hidden=\"true\"\n data-slot=\"scrub-input-shell\"\n className={cnAll(\n \"absolute inset-0 -z-10 overflow-hidden border-(length:--border-width) border-transparent bg-surface-muted will-change-transform\",\n size === \"sm\" ? \"rounded-sm\" : \"rounded-md\",\n \"motion-safe:transition-[background-color,border-color] motion-safe:duration-(--duration-quick) motion-safe:ease-settle\",\n // The shell springs home after a strain: its transform transitions only while `releasing`.\n \"group-data-[releasing]/scrub:motion-safe:transition-[transform,background-color,border-color] group-data-[releasing]/scrub:motion-safe:duration-(--duration-smooth) group-data-[releasing]/scrub:motion-safe:ease-smooth\",\n \"group-hover/scrub:border-border\",\n \"group-focus-within/scrub:border-foreground group-focus-within/scrub:bg-surface\",\n \"forced-colors:group-focus-within/scrub:outline-solid forced-colors:group-focus-within/scrub:outline-(length:--focus-ring-width) forced-colors:group-focus-within/scrub:outline-offset-(--focus-ring-offset)\",\n \"group-data-[scrubbing]/scrub:border-border-strong\",\n \"group-data-[invalid]/scrub:border-destructive-strong\",\n )}\n >\n {showTrack ? (\n <span\n data-slot=\"scrub-input-fill\"\n style={{ transform: `scaleX(${fraction})` }}\n className=\"pointer-events-none absolute inset-0 origin-left bg-foreground/6 group-data-[scrubbing]/scrub:bg-foreground/10 motion-safe:transition-transform motion-safe:duration-(--duration-smooth) motion-safe:ease-smooth group-data-[scrubbing]/scrub:motion-safe:transition-none\"\n />\n ) : null}\n </div>\n <label\n ref={handleRef}\n htmlFor={id}\n data-slot=\"scrub-input-handle\"\n data-text={labelIsText ? \"\" : undefined}\n onPointerDown={onHandleDown}\n onPointerMove={onHandleMove}\n onPointerUp={onHandleUp}\n onPointerCancel={onHandleUp}\n onClick={onHandleClick}\n onDoubleClick={onHandleDoubleClick}\n className=\"flex min-w-7.5 cursor-ew-resize touch-pan-y items-center justify-center ps-2.5 pe-1 text-text-secondary select-none motion-safe:transition-colors motion-safe:duration-(--duration-quick) data-[text]:font-medium hover:text-foreground group-data-[scrubbing]/scrub:text-foreground group-data-[disabled]/scrub:cursor-not-allowed [&_svg]:size-3.5 [&_svg]:flex-none\"\n >\n {label}\n </label>\n <div\n ref={bodyRef}\n data-slot=\"scrub-input-body\"\n className={cnAll(\n \"flex min-w-0 items-center gap-0.5 pe-2.5 data-[strain]:motion-safe:animate-field-strain\",\n \"group-data-[releasing]/scrub:motion-safe:transition-transform group-data-[releasing]/scrub:motion-safe:duration-(--duration-smooth) group-data-[releasing]/scrub:motion-safe:ease-smooth\",\n )}\n >\n <input\n {...rest}\n ref={inputRef}\n id={id}\n type=\"text\"\n inputMode=\"decimal\"\n autoComplete=\"off\"\n spellCheck={false}\n role=\"spinbutton\"\n aria-label={ariaLabel}\n aria-valuenow={current}\n aria-valuemin={Number.isFinite(min) ? min : undefined}\n aria-valuemax={Number.isFinite(max) ? max : undefined}\n aria-valuetext={valueText}\n aria-invalid={invalid || undefined}\n disabled={disabled}\n value={text}\n onChange={(event) => setDraft(event.target.value)}\n onFocus={onInputFocus}\n onBlur={onInputBlur}\n onKeyDown={onInputKeyDown}\n className={cnAll(\n \"m-0 h-full w-full min-w-0 flex-1 border-0 bg-transparent p-0 ps-1 font-sans text-foreground tabular-nums outline-none selection:bg-primary selection:text-primary-foreground disabled:cursor-not-allowed\",\n size === \"sm\" ? \"text-xs\" : \"text-sm\",\n )}\n />\n {unit ? (\n unitList.length > 1 ? (\n <AriaButton\n data-slot=\"scrub-input-unit\"\n isDisabled={disabled}\n onPress={cycleUnit}\n aria-label={`Unit: ${unit}. Change unit`}\n className=\"relative inline-flex h-6 min-w-6 flex-none cursor-pointer items-center justify-center rounded-xs border-0 bg-transparent px-1 text-xs text-text-secondary outline-none motion-safe:transition-[background-color,color,scale] motion-safe:duration-(--duration-quick) motion-safe:ease-settle data-[hovered]:bg-foreground/7 data-[hovered]:text-foreground data-[pressed]:scale-95 data-[focus-visible]:outline-solid data-[focus-visible]:outline-(length:--focus-ring-width) data-[focus-visible]:outline-(--color-ring) data-[disabled]:cursor-not-allowed\"\n >\n <SwapText swapKey={unit} motion=\"rise\">\n {unit}\n </SwapText>\n </AriaButton>\n ) : (\n <span\n aria-hidden=\"true\"\n data-slot=\"scrub-input-unit\"\n className=\"inline-flex h-6 min-w-4.5 flex-none items-center justify-center px-1 text-xs text-text-secondary\"\n >\n {unit}\n </span>\n )\n ) : null}\n </div>\n {multiplier && !scrubbing?.locked ? (\n <span\n aria-hidden=\"true\"\n data-slot=\"scrub-input-multiplier\"\n className=\"pointer-events-none absolute bottom-full mb-1.5 start-1.5 z-10 rounded-pill bg-foreground px-1.75 py-0.5 text-xs leading-4 text-background tabular-nums motion-safe:animate-field-clear-in motion-reduce:animate-fade-in\"\n >\n {multiplier}\n </span>\n ) : null}\n {scrubbing?.locked\n ? createPortal(\n <div\n ref={(node) => {\n cursorRef.current = node;\n placeCursor();\n }}\n aria-hidden=\"true\"\n data-slot=\"scrub-input-cursor\"\n className=\"pointer-events-none fixed top-0 left-0 z-50 -mt-2 -ms-3.25 flex items-center gap-1\"\n >\n <svg\n width=\"26\"\n height=\"16\"\n viewBox=\"0 0 26 16\"\n className=\"block fill-foreground stroke-background drop-shadow-sm\"\n strokeWidth=\"1.4\"\n strokeLinejoin=\"round\"\n >\n <path d=\"M1.5 8 L7.5 2.5 V6 H18.5 V2.5 L24.5 8 L18.5 13.5 V10 H7.5 V13.5 Z\" />\n </svg>\n {multiplier ? (\n <span className=\"rounded-pill bg-foreground px-1.5 py-px font-sans text-xs leading-4 text-background tabular-nums motion-safe:animate-field-clear-in motion-reduce:animate-fade-in\">\n {multiplier}\n </span>\n ) : null}\n </div>,\n document.body,\n )\n : null}\n </div>\n );\n});\n"],"mappings":"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"}
|
|
@@ -21,6 +21,7 @@ export interface SearchFieldProps extends Omit<InputProps, "className" | "style"
|
|
|
21
21
|
* @example
|
|
22
22
|
* <SearchField label="Search contacts" value={query} onValueChange={setQuery} />
|
|
23
23
|
*/
|
|
24
|
-
|
|
24
|
+
declare const SearchField$1: import("react").ForwardRefExoticComponent<SearchFieldProps & import("react").RefAttributes<HTMLInputElement>>;
|
|
25
25
|
//#endregion
|
|
26
|
+
export { SearchField$1 as SearchField };
|
|
26
27
|
//# sourceMappingURL=search-field.d.ts.map
|
|
@@ -13,10 +13,10 @@ import { jsx, jsxs } from "react/jsx-runtime";
|
|
|
13
13
|
* PasswordField for why a Group, and why `data-focus-within` darkens the border
|
|
14
14
|
* as well). `group/shell` lets the glass read the box's focus and filled state.
|
|
15
15
|
*/
|
|
16
|
-
const shellClass = cn(fieldSurfaceVariants(), "group/shell flex items-center gap-(--density-control-gap) h-(--density-control-h-md) px-(--density-control-pad-md) [&:where([data-focus-within])]:border-
|
|
16
|
+
const shellClass = cn(fieldSurfaceVariants(), "group/shell flex items-center gap-(--density-control-gap) h-(--density-control-h-md) px-(--density-control-pad-md) [&:where([data-focus-within])]:border-border-strong");
|
|
17
17
|
/** Borderless, fill-less input; the browser's own clear button and search decoration are hidden because the field has its own. */
|
|
18
18
|
const inputClass = "min-w-0 flex-1 appearance-none border-0 bg-transparent p-0 font-sans text-base text-foreground outline-none placeholder:text-muted-foreground selection:bg-primary selection:text-primary-foreground data-[disabled]:cursor-not-allowed [&::-webkit-search-cancel-button]:hidden [&::-webkit-search-decoration]:hidden";
|
|
19
|
-
const clearClass = "motion-safe:button-motion grid size-6 cursor-pointer place-items-center rounded-
|
|
19
|
+
const clearClass = "motion-safe:button-motion grid size-6 cursor-pointer place-items-center rounded-control border-0 bg-transparent p-0 text-muted-foreground outline-none data-[hovered]:bg-muted data-[hovered]:text-foreground motion-safe:data-[pressed]:scale-96 data-[focus-visible]:outline-solid data-[focus-visible]:outline-(length:--focus-ring-width) data-[focus-visible]:outline-offset-(--focus-ring-offset) data-[focus-visible]:outline-(--color-ring) data-[disabled]:cursor-not-allowed";
|
|
20
20
|
/** The glass wakes up with the field: muted at rest, full contrast while the field has focus or a value. */
|
|
21
21
|
function SearchGlyph() {
|
|
22
22
|
return /* @__PURE__ */ jsxs("svg", {
|
|
@@ -94,7 +94,7 @@ function ClearButton({ show, label, disabled, onClear }) {
|
|
|
94
94
|
* @example
|
|
95
95
|
* <SearchField label="Search contacts" value={query} onValueChange={setQuery} />
|
|
96
96
|
*/
|
|
97
|
-
const SearchField = forwardRef(function SearchField({ label, value, onValueChange, clearLabel = "Clear search", className, ...props }, ref) {
|
|
97
|
+
const SearchField$1 = forwardRef(function SearchField({ label, value, onValueChange, clearLabel = "Clear search", className, ...props }, ref) {
|
|
98
98
|
const inputRef = useRef(null);
|
|
99
99
|
const refs = useMemo(() => mergeRefs(ref, inputRef), [ref]);
|
|
100
100
|
const field = useFieldWiring({
|
|
@@ -150,6 +150,6 @@ const SearchField = forwardRef(function SearchField({ label, value, onValueChang
|
|
|
150
150
|
});
|
|
151
151
|
});
|
|
152
152
|
//#endregion
|
|
153
|
-
export { SearchField };
|
|
153
|
+
export { SearchField$1 as SearchField };
|
|
154
154
|
|
|
155
155
|
//# sourceMappingURL=search-field.js.map
|