@passui/react 0.1.44 → 0.1.46

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.
Files changed (140) hide show
  1. package/README.md +57 -46
  2. package/dist/types/src/components/accordion/accordion.d.ts +1 -1
  3. package/dist/types/src/components/breadcrumb/breadcrumb.d.ts +1 -1
  4. package/dist/types/src/components/inline-disclosure/inline-disclosure.d.ts +1 -1
  5. package/dist/types/src/components/popover/anchored-surface.d.ts +5 -1
  6. package/dist/types/src/components/popover/index.d.ts +41 -0
  7. package/dist/types/src/components/popover/popover-content.d.ts +32 -0
  8. package/dist/types/src/components/popover/popover-root.d.ts +22 -0
  9. package/dist/types/src/components/popover/popover-trigger.d.ts +23 -0
  10. package/dist/types/src/components/stepper/rolling-digit.d.ts +1 -1
  11. package/dist/types/src/components/table/table.d.ts +1 -1
  12. package/dist/types/src/components/view-controller/view-controller.d.ts +2 -2
  13. package/dist/types/src/index.d.ts +1 -1
  14. package/dist/types/src/machines/popover.machine.d.ts +30 -0
  15. package/package.json +307 -307
  16. package/src/appearance/index.ts +2 -2
  17. package/src/appearance/platform.tsx +76 -60
  18. package/src/appearance/theme.tsx +409 -377
  19. package/src/components/accordion/accordion.tsx +58 -49
  20. package/src/components/ambient/ambient.tsx +25 -25
  21. package/src/components/ambient/index.ts +1 -1
  22. package/src/components/aspect-ratio/aspect-ratio.tsx +21 -16
  23. package/src/components/breadcrumb/breadcrumb.tsx +63 -57
  24. package/src/components/button/button-styles.ts +39 -32
  25. package/src/components/button/button.tsx +91 -84
  26. package/src/components/button/icon-button.tsx +27 -25
  27. package/src/components/button/pending-spinner.tsx +8 -8
  28. package/src/components/button/use-pending-indicator.ts +11 -11
  29. package/src/components/chip/chip.tsx +35 -30
  30. package/src/components/code-block/code-block.tsx +40 -39
  31. package/src/components/combo-box/combo-box.tsx +5 -5
  32. package/src/components/command/command.tsx +124 -94
  33. package/src/components/compact-date-picker/calendar-month.ts +75 -53
  34. package/src/components/compact-date-picker/compact-date-picker.tsx +308 -289
  35. package/src/components/compact-time-picker/compact-time-picker.tsx +288 -281
  36. package/src/components/compact-time-picker/time-value.ts +32 -29
  37. package/src/components/content-unavailable-view/content-unavailable-view.tsx +64 -48
  38. package/src/components/content-view/content-view.tsx +168 -159
  39. package/src/components/content-view/index.ts +1 -1
  40. package/src/components/context-menu/conceal-menu-source.ts +25 -25
  41. package/src/components/context-menu/context-menu-host.tsx +439 -398
  42. package/src/components/context-menu/context-menu-store.ts +125 -123
  43. package/src/components/context-menu/menu-navigation.ts +25 -18
  44. package/src/components/context-menu/menu-position.ts +33 -29
  45. package/src/components/context-menu/menu-reveal.ts +32 -32
  46. package/src/components/context-menu/use-context-menu.ts +63 -56
  47. package/src/components/conversation/conversation.tsx +265 -263
  48. package/src/components/conversation/index.ts +1 -1
  49. package/src/components/date-picker/date-picker.tsx +95 -94
  50. package/src/components/date-picker/date-value.ts +20 -10
  51. package/src/components/dialog/dialog-host.tsx +105 -103
  52. package/src/components/dialog/dialog-store.ts +37 -36
  53. package/src/components/dialog/dialog.tsx +192 -179
  54. package/src/components/dialog/index.ts +1 -1
  55. package/src/components/disclosure-group/disclosure-group.tsx +93 -86
  56. package/src/components/divider/divider.tsx +24 -22
  57. package/src/components/inline-disclosure/inline-disclosure.tsx +40 -36
  58. package/src/components/keyboard-shortcut/keyboard-shortcut.tsx +23 -19
  59. package/src/components/labeled-content/labeled-content.tsx +68 -68
  60. package/src/components/locale-switcher/index.ts +1 -1
  61. package/src/components/locale-switcher/locale-switcher.tsx +279 -251
  62. package/src/components/menu/menu.tsx +75 -58
  63. package/src/components/morph/index.ts +2 -2
  64. package/src/components/morph/morph.tsx +227 -201
  65. package/src/components/morph/use-morph-disclosure.ts +19 -19
  66. package/src/components/one-time-code/one-time-code.tsx +216 -200
  67. package/src/components/picker/picker-trigger.ts +16 -13
  68. package/src/components/picker/picker.tsx +371 -345
  69. package/src/components/popover/anchored-surface.tsx +253 -229
  70. package/src/components/popover/index.ts +50 -0
  71. package/src/components/popover/popover-content.tsx +334 -0
  72. package/src/components/popover/popover-fit.ts +58 -53
  73. package/src/components/popover/popover-root.tsx +97 -0
  74. package/src/components/popover/popover-trigger.tsx +71 -0
  75. package/src/components/profile-image/profile-image.tsx +80 -64
  76. package/src/components/progress-view/progress-view.tsx +61 -59
  77. package/src/components/progress-view/use-liquid-progress.ts +38 -38
  78. package/src/components/radio-group/radio-group.tsx +71 -68
  79. package/src/components/search-field/expanding-search-field.tsx +191 -177
  80. package/src/components/search-field/search-field.tsx +59 -50
  81. package/src/components/search-list/search-list.tsx +246 -236
  82. package/src/components/section/section.tsx +58 -58
  83. package/src/components/secure-field/secure-field.tsx +3 -3
  84. package/src/components/segmented-control/segmented-control.tsx +136 -136
  85. package/src/components/sheet/modal-environment.ts +71 -64
  86. package/src/components/sheet/sheet-portal.tsx +58 -53
  87. package/src/components/sheet/sheet.tsx +146 -141
  88. package/src/components/sheet/use-sheet-focus.ts +97 -82
  89. package/src/components/sidebar/sidebar.tsx +78 -78
  90. package/src/components/skeleton/skeleton.tsx +30 -21
  91. package/src/components/slider/slider.tsx +113 -104
  92. package/src/components/slosh-slider/liquid-step.ts +32 -31
  93. package/src/components/slosh-slider/slosh-slider.tsx +265 -228
  94. package/src/components/stat-tile/index.ts +1 -1
  95. package/src/components/stat-tile/stat-tile.tsx +79 -68
  96. package/src/components/status-indicator/status-indicator.tsx +40 -37
  97. package/src/components/stepper/rolling-digit.tsx +82 -68
  98. package/src/components/stepper/stepper.tsx +122 -113
  99. package/src/components/swatch-picker/swatch-picker.tsx +120 -120
  100. package/src/components/tab-bar/tab-bar.tsx +149 -148
  101. package/src/components/tab-view/tab-view.tsx +140 -137
  102. package/src/components/table/table.tsx +117 -98
  103. package/src/components/text-editor/text-editor.tsx +34 -29
  104. package/src/components/text-field/text-field.tsx +182 -180
  105. package/src/components/toast/toast-store.ts +127 -115
  106. package/src/components/toast/toaster.tsx +361 -347
  107. package/src/components/toggle/toggle.tsx +297 -282
  108. package/src/components/tooltip/tooltip-host.tsx +157 -150
  109. package/src/components/view-controller/index.ts +1 -1
  110. package/src/components/view-controller/view-controller.tsx +64 -53
  111. package/src/components/wake-slider/wake-slider.tsx +220 -205
  112. package/src/hooks/scroll/minimize-decision.ts +11 -7
  113. package/src/hooks/use-anchored-disclosure.ts +19 -19
  114. package/src/hooks/use-anchored-placement.ts +29 -25
  115. package/src/hooks/use-dismiss.ts +33 -29
  116. package/src/hooks/use-floating-dismiss.ts +24 -22
  117. package/src/hooks/use-media-query.ts +67 -60
  118. package/src/hooks/use-minimize-on-scroll.ts +69 -60
  119. package/src/hooks/use-visual-viewport.ts +63 -63
  120. package/src/hooks/viewport/types.ts +16 -16
  121. package/src/hooks/viewport/viewport-metrics.ts +16 -10
  122. package/src/icons/icons.tsx +440 -328
  123. package/src/index.ts +91 -91
  124. package/src/lib/anchored-placement.ts +70 -65
  125. package/src/lib/class-names.ts +14 -13
  126. package/src/lib/clipboard.ts +8 -7
  127. package/src/lib/date.ts +61 -60
  128. package/src/lib/floating-layers.ts +39 -24
  129. package/src/lib/haptics.ts +10 -10
  130. package/src/lib/language.ts +9 -9
  131. package/src/lib/range-value.ts +27 -21
  132. package/src/lib/screen-edges.ts +33 -30
  133. package/src/lib/shapes.ts +38 -34
  134. package/src/machines/popover.machine.ts +51 -0
  135. package/src/motion/transitions.ts +46 -43
  136. package/src/styles/code-block.css +69 -43
  137. package/src/styles/motion-controls.css +393 -191
  138. package/src/tokens.ts +1 -1
  139. package/dist/types/src/components/popover/popover.d.ts +0 -21
  140. package/src/components/popover/popover.tsx +0 -322
@@ -1,30 +1,30 @@
1
1
  // Adapted from Bencho. See THIRD_PARTY_NOTICES.md for the MIT notice.
2
- import { type CSSProperties, useEffect, useRef } from "react"
3
- import { useMediaQuery } from "../../hooks/use-media-query"
4
- import { cn } from "../../lib/class-names"
5
- import { clampRange as clamp } from "../../lib/range-value"
6
- import { stepLiquid } from "./liquid-step"
2
+ import { type CSSProperties, useEffect, useRef } from "react";
3
+ import { useMediaQuery } from "../../hooks/use-media-query";
4
+ import { cn } from "../../lib/class-names";
5
+ import { clampRange as clamp } from "../../lib/range-value";
6
+ import { stepLiquid } from "./liquid-step";
7
7
 
8
- export type SloshSliderSize = "sm" | "md" | "lg"
8
+ export type SloshSliderSize = "sm" | "md" | "lg";
9
9
 
10
10
  export interface SloshSliderProps {
11
- label: string
12
- value?: number
13
- defaultValue?: number
14
- onChange?: (value: number) => void
15
- /** 0 is rigid; 100 is loose liquid. @default 15 */
16
- viscosity?: number
17
- /** How far the handle coasts after release, 0–100. @default 55 */
18
- momentum?: number
19
- /** Lean of the liquid's leading edge, 0–100. @default 45 */
20
- tilt?: number
21
- /** Track corner, from square to a pill, 0–20. @default 20 */
22
- corner?: number
23
- /** Track height and knob scale. @default "md" */
24
- size?: SloshSliderSize
25
- disabled?: boolean
26
- className?: string
27
- formatValue?: (value: number) => string
11
+ label: string;
12
+ value?: number;
13
+ defaultValue?: number;
14
+ onChange?: (value: number) => void;
15
+ /** 0 is rigid; 100 is loose liquid. @default 15 */
16
+ viscosity?: number;
17
+ /** How far the handle coasts after release, 0–100. @default 55 */
18
+ momentum?: number;
19
+ /** Lean of the liquid's leading edge, 0–100. @default 45 */
20
+ tilt?: number;
21
+ /** Track corner, from square to a pill, 0–20. @default 20 */
22
+ corner?: number;
23
+ /** Track height and knob scale. @default "md" */
24
+ size?: SloshSliderSize;
25
+ disabled?: boolean;
26
+ className?: string;
27
+ formatValue?: (value: number) => string;
28
28
  }
29
29
 
30
30
  /* A slider whose value has mass.
@@ -40,218 +40,255 @@ export interface SloshSliderProps {
40
40
  The loop writes geometry directly and parks as soon as it rests.
41
41
  React receives only changed integer values for the application's state. */
42
42
  export function SloshSlider({
43
- label,
44
- value,
45
- defaultValue = 62,
46
- onChange,
47
- viscosity = 15,
48
- momentum = 55,
49
- tilt = 45,
50
- corner = 20,
51
- size = "md",
52
- disabled = false,
53
- className,
54
- formatValue,
43
+ label,
44
+ value,
45
+ defaultValue = 62,
46
+ onChange,
47
+ viscosity = 15,
48
+ momentum = 55,
49
+ tilt = 45,
50
+ corner = 20,
51
+ size = "md",
52
+ disabled = false,
53
+ className,
54
+ formatValue,
55
55
  }: SloshSliderProps) {
56
- const reduced = useMediaQuery("(prefers-reduced-motion: reduce)")
57
- const track = useRef<HTMLDivElement>(null)
58
- const fillEl = useRef<HTMLDivElement>(null)
59
- const knobEl = useRef<HTMLDivElement>(null)
60
- const initial = clamp(value ?? defaultValue, 0, 100)
61
- const state = useRef({
62
- value: initial,
63
- handleVelocity: 0,
64
- fill: initial,
65
- velocity: 0,
66
- held: false,
67
- raf: 0,
68
- previous: 0,
69
- last: { value: initial, time: 0 },
70
- notified: Math.round(initial),
71
- })
72
- const settings = useRef({ viscosity, momentum, tilt, reduced, onChange, formatValue })
73
- settings.current = { viscosity, momentum, tilt, reduced, onChange, formatValue }
74
- const run = useRef<() => void>(() => {})
56
+ const reduced = useMediaQuery("(prefers-reduced-motion: reduce)");
57
+ const track = useRef<HTMLDivElement>(null);
58
+ const fillEl = useRef<HTMLDivElement>(null);
59
+ const knobEl = useRef<HTMLDivElement>(null);
60
+ const initial = clamp(value ?? defaultValue, 0, 100);
61
+ const state = useRef({
62
+ value: initial,
63
+ handleVelocity: 0,
64
+ fill: initial,
65
+ velocity: 0,
66
+ held: false,
67
+ raf: 0,
68
+ previous: 0,
69
+ last: { value: initial, time: 0 },
70
+ notified: Math.round(initial),
71
+ });
72
+ const settings = useRef({
73
+ viscosity,
74
+ momentum,
75
+ tilt,
76
+ reduced,
77
+ onChange,
78
+ formatValue,
79
+ });
80
+ settings.current = {
81
+ viscosity,
82
+ momentum,
83
+ tilt,
84
+ reduced,
85
+ onChange,
86
+ formatValue,
87
+ };
88
+ const run = useRef<() => void>(() => {});
75
89
 
76
- useEffect(() => {
77
- const s = state.current
78
- const paint = () => {
79
- if (!track.current || !fillEl.current || !knobEl.current) return
80
- fillEl.current.style.width = `${s.fill.toFixed(2)}%`
81
- knobEl.current.style.insetInlineStart = `${s.value.toFixed(2)}%`
82
- /* Proportional to how fast the fill is actually moving: upright
90
+ useEffect(() => {
91
+ const s = state.current;
92
+ const paint = () => {
93
+ if (!track.current || !fillEl.current || !knobEl.current) return;
94
+ fillEl.current.style.width = `${s.fill.toFixed(2)}%`;
95
+ knobEl.current.style.insetInlineStart = `${s.value.toFixed(2)}%`;
96
+ /* Proportional to how fast the fill is actually moving: upright
83
97
  at rest, so it cannot be decoration when nothing travels. */
84
- const lean = settings.current.reduced
85
- ? 0
86
- : clamp((clamp(settings.current.tilt, 0, 100) / 100) * s.velocity * 3, -20, 20)
87
- fillEl.current.style.setProperty("--slosh-lean", `${lean.toFixed(2)}px`)
88
- const next = Math.round(s.value)
89
- track.current.setAttribute("aria-valuenow", String(next))
90
- const spoken = settings.current.formatValue?.(next)
91
- if (spoken) track.current.setAttribute("aria-valuetext", spoken)
92
- else track.current.removeAttribute("aria-valuetext")
93
- if (s.notified !== next) {
94
- s.notified = next
95
- settings.current.onChange?.(next)
96
- }
97
- }
98
- const tick = (time: number) => {
99
- const dt = s.previous ? clamp((time - s.previous) / 16.67, 0, 2.5) : 1
100
- s.previous = time
101
- const knobs = settings.current
102
- /* Friction is per frame, raised to dt: a dropped frame must
98
+ const lean = settings.current.reduced
99
+ ? 0
100
+ : clamp(
101
+ (clamp(settings.current.tilt, 0, 100) / 100) * s.velocity * 3,
102
+ -20,
103
+ 20,
104
+ );
105
+ fillEl.current.style.setProperty("--slosh-lean", `${lean.toFixed(2)}px`);
106
+ const next = Math.round(s.value);
107
+ track.current.setAttribute("aria-valuenow", String(next));
108
+ const spoken = settings.current.formatValue?.(next);
109
+ if (spoken) track.current.setAttribute("aria-valuetext", spoken);
110
+ else track.current.removeAttribute("aria-valuetext");
111
+ if (s.notified !== next) {
112
+ s.notified = next;
113
+ settings.current.onChange?.(next);
114
+ }
115
+ };
116
+ const tick = (time: number) => {
117
+ const dt = s.previous ? clamp((time - s.previous) / 16.67, 0, 2.5) : 1;
118
+ s.previous = time;
119
+ const knobs = settings.current;
120
+ /* Friction is per frame, raised to dt: a dropped frame must
103
121
  not double the deceleration. The release is a throw, not a stop. */
104
- if (!s.held && s.handleVelocity) {
105
- s.value += s.handleVelocity * dt
106
- s.handleVelocity *= (0.86 + (clamp(knobs.momentum, 0, 100) / 100) * 0.115) ** dt
107
- if (s.value <= 0 || s.value >= 100) {
108
- s.value = clamp(s.value, 0, 100)
109
- s.handleVelocity = 0
110
- }
111
- if (Math.abs(s.handleVelocity) < 0.01) s.handleVelocity = 0
112
- }
113
- const next = stepLiquid(s, s.value, knobs.reduced ? 0 : knobs.viscosity, dt)
114
- s.fill = next.fill
115
- s.velocity = next.velocity
116
- paint()
117
- if (!s.handleVelocity && !s.velocity && Math.abs(s.fill - s.value) < 0.02) {
118
- s.raf = 0
119
- s.previous = 0
120
- return
121
- }
122
- s.raf = requestAnimationFrame(tick)
123
- }
124
- run.current = () => {
125
- // The handle paints on the input event; it never waits for the fill.
126
- paint()
127
- if (!s.raf) s.raf = requestAnimationFrame(tick)
128
- }
129
- paint()
130
- return () => {
131
- cancelAnimationFrame(s.raf)
132
- s.raf = 0
133
- s.previous = 0
134
- }
135
- }, [])
122
+ if (!s.held && s.handleVelocity) {
123
+ s.value += s.handleVelocity * dt;
124
+ s.handleVelocity *=
125
+ (0.86 + (clamp(knobs.momentum, 0, 100) / 100) * 0.115) ** dt;
126
+ if (s.value <= 0 || s.value >= 100) {
127
+ s.value = clamp(s.value, 0, 100);
128
+ s.handleVelocity = 0;
129
+ }
130
+ if (Math.abs(s.handleVelocity) < 0.01) s.handleVelocity = 0;
131
+ }
132
+ const next = stepLiquid(
133
+ s,
134
+ s.value,
135
+ knobs.reduced ? 0 : knobs.viscosity,
136
+ dt,
137
+ );
138
+ s.fill = next.fill;
139
+ s.velocity = next.velocity;
140
+ paint();
141
+ if (
142
+ !s.handleVelocity &&
143
+ !s.velocity &&
144
+ Math.abs(s.fill - s.value) < 0.02
145
+ ) {
146
+ s.raf = 0;
147
+ s.previous = 0;
148
+ return;
149
+ }
150
+ s.raf = requestAnimationFrame(tick);
151
+ };
152
+ run.current = () => {
153
+ // The handle paints on the input event; it never waits for the fill.
154
+ paint();
155
+ if (!s.raf) s.raf = requestAnimationFrame(tick);
156
+ };
157
+ paint();
158
+ return () => {
159
+ cancelAnimationFrame(s.raf);
160
+ s.raf = 0;
161
+ s.previous = 0;
162
+ };
163
+ }, []);
136
164
 
137
- // biome-ignore lint/correctness/useExhaustiveDependencies: Motion settings can wake a parked loop without changing its value.
138
- useEffect(() => {
139
- const s = state.current
140
- if (value !== undefined && Math.round(clamp(value, 0, 100)) !== s.notified) {
141
- s.value = clamp(value, 0, 100)
142
- s.notified = Math.round(s.value)
143
- s.handleVelocity = 0
144
- }
145
- if (reduced || disabled) {
146
- s.handleVelocity = 0
147
- s.held = false
148
- }
149
- run.current()
150
- }, [value, reduced, disabled, viscosity, tilt, formatValue])
165
+ // biome-ignore lint/correctness/useExhaustiveDependencies: Motion settings can wake a parked loop without changing its value.
166
+ useEffect(() => {
167
+ const s = state.current;
168
+ if (
169
+ value !== undefined &&
170
+ Math.round(clamp(value, 0, 100)) !== s.notified
171
+ ) {
172
+ s.value = clamp(value, 0, 100);
173
+ s.notified = Math.round(s.value);
174
+ s.handleVelocity = 0;
175
+ }
176
+ if (reduced || disabled) {
177
+ s.handleVelocity = 0;
178
+ s.held = false;
179
+ }
180
+ run.current();
181
+ }, [value, reduced, disabled, viscosity, tilt, formatValue]);
151
182
 
152
- const readAt = (clientX: number) => {
153
- const t = track.current
154
- if (!t) return 0
155
- const box = t.getBoundingClientRect()
156
- const fraction = box.width ? (clientX - box.left) / box.width : 0
157
- return clamp(
158
- (getComputedStyle(t).direction === "rtl" ? 1 - fraction : fraction) * 100,
159
- 0,
160
- 100
161
- )
162
- }
163
- const release = (cancelled = false) => {
164
- const s = state.current
165
- if (!s.held) return
166
- s.held = false
167
- /* Leaves along its measured velocity, capped so a full-track flick
183
+ const readAt = (clientX: number) => {
184
+ const t = track.current;
185
+ if (!t) return 0;
186
+ const box = t.getBoundingClientRect();
187
+ const fraction = box.width ? (clientX - box.left) / box.width : 0;
188
+ return clamp(
189
+ (getComputedStyle(t).direction === "rtl" ? 1 - fraction : fraction) * 100,
190
+ 0,
191
+ 100,
192
+ );
193
+ };
194
+ const release = (cancelled = false) => {
195
+ const s = state.current;
196
+ if (!s.held) return;
197
+ s.held = false;
198
+ /* Leaves along its measured velocity, capped so a full-track flick
168
199
  does not simply pin it. A cancelled gesture never throws. */
169
- s.handleVelocity = cancelled || reduced ? 0 : clamp(s.handleVelocity, -6, 6)
170
- if (performance.now() - s.last.time > 100) s.handleVelocity = 0
171
- run.current()
172
- }
173
- return (
174
- <div
175
- className={cn("passui-slosh-slider not-typeset", disabled && "opacity-40", className)}
176
- style={{ "--slosh-radius": `${clamp(corner, 0, 20)}px` } as CSSProperties}
177
- data-size={size}
178
- >
179
- {/* Nothing but the track: the liquid is the value's motion, not a
200
+ s.handleVelocity =
201
+ cancelled || reduced ? 0 : clamp(s.handleVelocity, -6, 6);
202
+ if (performance.now() - s.last.time > 100) s.handleVelocity = 0;
203
+ run.current();
204
+ };
205
+ return (
206
+ <div
207
+ className={cn(
208
+ "passui-slosh-slider not-typeset",
209
+ disabled && "opacity-40",
210
+ className,
211
+ )}
212
+ style={{ "--slosh-radius": `${clamp(corner, 0, 20)}px` } as CSSProperties}
213
+ data-size={size}
214
+ >
215
+ {/* Nothing but the track: the liquid is the value's motion, not a
180
216
  second numeric readout that lags and overshoots beside it. */}
181
- <div
182
- ref={track}
183
- className="passui-slosh-track"
184
- role="slider"
185
- tabIndex={disabled ? -1 : 0}
186
- aria-label={label}
187
- aria-disabled={disabled || undefined}
188
- aria-valuemin={0}
189
- aria-valuemax={100}
190
- aria-valuenow={Math.round(initial)}
191
- aria-valuetext={formatValue?.(Math.round(initial))}
192
- onPointerDown={(event) => {
193
- if (disabled || event.button !== 0) return
194
- event.currentTarget.focus()
195
- event.currentTarget.setPointerCapture(event.pointerId)
196
- const s = state.current
197
- s.held = true
198
- s.handleVelocity = 0
199
- s.value = readAt(event.clientX)
200
- s.last = { value: s.value, time: performance.now() }
201
- run.current()
202
- }}
203
- onPointerMove={(event) => {
204
- const s = state.current
205
- if (!s.held || disabled) return
206
- const next = readAt(event.clientX),
207
- now = performance.now()
208
- /* Per frame, not per ms: the loop's release velocity must
217
+ <div
218
+ ref={track}
219
+ className="passui-slosh-track"
220
+ role="slider"
221
+ tabIndex={disabled ? -1 : 0}
222
+ aria-label={label}
223
+ aria-disabled={disabled || undefined}
224
+ aria-valuemin={0}
225
+ aria-valuemax={100}
226
+ aria-valuenow={Math.round(initial)}
227
+ aria-valuetext={formatValue?.(Math.round(initial))}
228
+ onPointerDown={(event) => {
229
+ if (disabled || event.button !== 0) return;
230
+ event.currentTarget.focus();
231
+ event.currentTarget.setPointerCapture(event.pointerId);
232
+ const s = state.current;
233
+ s.held = true;
234
+ s.handleVelocity = 0;
235
+ s.value = readAt(event.clientX);
236
+ s.last = { value: s.value, time: performance.now() };
237
+ run.current();
238
+ }}
239
+ onPointerMove={(event) => {
240
+ const s = state.current;
241
+ if (!s.held || disabled) return;
242
+ const next = readAt(event.clientX);
243
+ const now = performance.now();
244
+ /* Per frame, not per ms: the loop's release velocity must
209
245
  use the same unit as the spring. */
210
- s.handleVelocity =
211
- ((next - s.last.value) / Math.max(1, now - s.last.time)) * 16.67
212
- s.last = { value: next, time: now }
213
- s.value = next
214
- run.current()
215
- }}
216
- onPointerUp={() => release()}
217
- onPointerCancel={() => release(true)}
218
- onLostPointerCapture={() => release(true)}
219
- onKeyDown={(event) => {
220
- if (disabled) return
221
- const s = state.current,
222
- step = event.shiftKey ? 10 : 2
223
- const rtl = getComputedStyle(event.currentTarget).direction === "rtl"
224
- const next =
225
- event.key === "ArrowUp" || event.key === (rtl ? "ArrowLeft" : "ArrowRight")
226
- ? s.value + step
227
- : event.key === "ArrowDown" ||
228
- event.key === (rtl ? "ArrowRight" : "ArrowLeft")
229
- ? s.value - step
230
- : event.key === "Home"
231
- ? 0
232
- : event.key === "End"
233
- ? 100
234
- : null
235
- if (next === null) return
236
- event.preventDefault()
237
- s.handleVelocity = 0
238
- s.value = clamp(next, 0, 100)
239
- run.current()
240
- }}
241
- >
242
- <div
243
- aria-hidden="true"
244
- ref={fillEl}
245
- className="passui-slosh-fill"
246
- style={{ width: `${initial}%` }}
247
- />
248
- <div
249
- aria-hidden="true"
250
- ref={knobEl}
251
- className="passui-slosh-knob"
252
- style={{ insetInlineStart: `${initial}%` }}
253
- />
254
- </div>
255
- </div>
256
- )
246
+ s.handleVelocity =
247
+ ((next - s.last.value) / Math.max(1, now - s.last.time)) * 16.67;
248
+ s.last = { value: next, time: now };
249
+ s.value = next;
250
+ run.current();
251
+ }}
252
+ onPointerUp={() => release()}
253
+ onPointerCancel={() => release(true)}
254
+ onLostPointerCapture={() => release(true)}
255
+ onKeyDown={(event) => {
256
+ if (disabled) return;
257
+ const s = state.current;
258
+ const step = event.shiftKey ? 10 : 2;
259
+ const rtl = getComputedStyle(event.currentTarget).direction === "rtl";
260
+ const next =
261
+ event.key === "ArrowUp" ||
262
+ event.key === (rtl ? "ArrowLeft" : "ArrowRight")
263
+ ? s.value + step
264
+ : event.key === "ArrowDown" ||
265
+ event.key === (rtl ? "ArrowRight" : "ArrowLeft")
266
+ ? s.value - step
267
+ : event.key === "Home"
268
+ ? 0
269
+ : event.key === "End"
270
+ ? 100
271
+ : null;
272
+ if (next === null) return;
273
+ event.preventDefault();
274
+ s.handleVelocity = 0;
275
+ s.value = clamp(next, 0, 100);
276
+ run.current();
277
+ }}
278
+ >
279
+ <div
280
+ aria-hidden="true"
281
+ ref={fillEl}
282
+ className="passui-slosh-fill"
283
+ style={{ width: `${initial}%` }}
284
+ />
285
+ <div
286
+ aria-hidden="true"
287
+ ref={knobEl}
288
+ className="passui-slosh-knob"
289
+ style={{ insetInlineStart: `${initial}%` }}
290
+ />
291
+ </div>
292
+ </div>
293
+ );
257
294
  }
@@ -1 +1 @@
1
- export * from "./stat-tile"
1
+ export * from "./stat-tile";