@astryxdesign/core 0.7.6 → 0.7.7-canary.1d04d40
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/AppShell/AppShell.d.ts.map +1 -1
- package/dist/AppShell/AppShell.js +23 -3
- package/dist/BottomSheet/BottomSheetPanel.d.ts.map +1 -1
- package/dist/BottomSheet/BottomSheetPanel.js +16 -9
- package/dist/Chat/ChatComposerInput.d.ts +9 -0
- package/dist/Chat/ChatComposerInput.d.ts.map +1 -1
- package/dist/Chat/ChatComposerInput.js +2 -0
- package/dist/Chat/ChatMessage.js +2 -2
- package/dist/Chat/chatMessage.vars.stylex.d.ts +8 -8
- package/dist/Chat/chatMessage.vars.stylex.js +8 -8
- package/dist/CommandPalette/CommandPaletteInput.d.ts.map +1 -1
- package/dist/CommandPalette/CommandPaletteInput.js +8 -1
- package/dist/DateInput/MonthScroller.d.ts.map +1 -1
- package/dist/DateInput/MonthScroller.js +72 -2
- package/dist/Drawer/Drawer.d.ts.map +1 -1
- package/dist/Drawer/Drawer.js +6 -0
- package/dist/DropdownMenu/useMenuOverflow.d.ts +13 -3
- package/dist/DropdownMenu/useMenuOverflow.d.ts.map +1 -1
- package/dist/DropdownMenu/useMenuOverflow.js +31 -8
- package/dist/Field/PanelSearchInput.d.ts.map +1 -1
- package/dist/Field/PanelSearchInput.js +8 -1
- package/dist/Field/textEntryTraits.d.ts +6 -6
- package/dist/Field/textEntryTraits.js +6 -6
- package/dist/Layer/layerClickBoundary.d.ts +7 -1
- package/dist/Layer/layerClickBoundary.d.ts.map +1 -1
- package/dist/Layer/layerClickBoundary.js +2 -2
- package/dist/Layer/usePressPin.d.ts +95 -0
- package/dist/Layer/usePressPin.d.ts.map +1 -0
- package/dist/Layer/usePressPin.js +335 -0
- package/dist/Layer/useTouchTrigger.d.ts +23 -1
- package/dist/Layer/useTouchTrigger.d.ts.map +1 -1
- package/dist/Layer/useTouchTrigger.js +29 -2
- package/dist/Link/Link.d.ts.map +1 -1
- package/dist/Link/Link.js +1 -0
- package/dist/ProgressBar/ProgressBar.d.ts.map +1 -1
- package/dist/ProgressBar/ProgressBar.js +20 -9
- package/dist/ProgressBar/ProgressBarTouch.d.ts +29 -0
- package/dist/ProgressBar/ProgressBarTouch.d.ts.map +1 -0
- package/dist/ProgressBar/ProgressBarTouch.js +91 -0
- package/dist/Selector/Selector.d.ts.map +1 -1
- package/dist/Selector/Selector.js +25 -13
- package/dist/Slider/Slider.d.ts.map +1 -1
- package/dist/Slider/Slider.js +404 -131
- package/dist/Slider/sliderTouchController.d.ts +189 -0
- package/dist/Slider/sliderTouchController.d.ts.map +1 -0
- package/dist/Slider/sliderTouchController.js +1017 -0
- package/dist/Slider/useSliderTouch.d.ts +22 -0
- package/dist/Slider/useSliderTouch.d.ts.map +1 -0
- package/dist/Slider/useSliderTouch.js +56 -0
- package/dist/Tooltip/Tooltip.d.ts +30 -6
- package/dist/Tooltip/Tooltip.d.ts.map +1 -1
- package/dist/Tooltip/Tooltip.js +5 -0
- package/dist/Tooltip/index.d.ts +1 -1
- package/dist/Tooltip/index.d.ts.map +1 -1
- package/dist/Tooltip/useTooltip.d.ts +31 -5
- package/dist/Tooltip/useTooltip.d.ts.map +1 -1
- package/dist/Tooltip/useTooltip.js +59 -18
- package/dist/Typeahead/BaseTypeahead.d.ts.map +1 -1
- package/dist/Typeahead/BaseTypeahead.js +19 -9
- package/dist/astryx.css +40 -6
- package/dist/gestures/GestureArena.d.ts +21 -2
- package/dist/gestures/GestureArena.d.ts.map +1 -1
- package/dist/gestures/GestureArena.js +23 -3
- package/dist/gestures/GestureHost.d.ts +123 -3
- package/dist/gestures/GestureHost.d.ts.map +1 -1
- package/dist/gestures/GestureHost.js +791 -12
- package/dist/gestures/GestureRecognizer.d.ts +15 -1
- package/dist/gestures/GestureRecognizer.d.ts.map +1 -1
- package/dist/gestures/GestureRecognizer.js +12 -0
- package/dist/gestures/PanGestureRecognizer.d.ts +8 -0
- package/dist/gestures/PanGestureRecognizer.d.ts.map +1 -1
- package/dist/gestures/PinchGestureRecognizer.d.ts +5 -1
- package/dist/gestures/PinchGestureRecognizer.d.ts.map +1 -1
- package/dist/gestures/PinchGestureRecognizer.js +10 -0
- package/dist/gestures/ScreenEdgePanGestureRecognizer.d.ts +3 -3
- package/dist/gestures/ScreenEdgePanGestureRecognizer.d.ts.map +1 -1
- package/dist/gestures/ScreenEdgePanGestureRecognizer.js +7 -5
- package/dist/gestures/nativeScroll.d.ts +16 -2
- package/dist/gestures/nativeScroll.d.ts.map +1 -1
- package/dist/gestures/nativeScroll.js +39 -8
- package/dist/gestures/recognizerInternals.d.ts +4 -2
- package/dist/gestures/recognizerInternals.d.ts.map +1 -1
- package/dist/gestures/types.d.ts +11 -0
- package/dist/gestures/types.d.ts.map +1 -1
- package/dist/gestures/types.js +7 -0
- package/dist/gestures/wheelSegmenter.d.ts +45 -0
- package/dist/gestures/wheelSegmenter.d.ts.map +1 -0
- package/dist/gestures/wheelSegmenter.js +98 -0
- package/dist/motion/animator.d.ts.map +1 -1
- package/dist/motion/animator.js +140 -19
- package/dist/motion/paging.d.ts +58 -0
- package/dist/motion/paging.d.ts.map +1 -0
- package/dist/motion/paging.js +128 -0
- package/dist/motion/uikit/index.d.ts +5 -3
- package/dist/motion/uikit/index.d.ts.map +1 -1
- package/dist/motion/uikit/index.js +5 -3
- package/dist/motion/uikit/paging.d.ts +160 -0
- package/dist/motion/uikit/paging.d.ts.map +1 -0
- package/dist/motion/uikit/paging.js +162 -0
- package/dist/motion/uikit/progressBar.d.ts +59 -0
- package/dist/motion/uikit/progressBar.d.ts.map +1 -0
- package/dist/motion/uikit/progressBar.js +82 -0
- package/dist/motion/uikit/scroll.d.ts +0 -19
- package/dist/motion/uikit/scroll.d.ts.map +1 -1
- package/dist/motion/uikit/scroll.js +2 -21
- package/dist/motion/uikit/slider.d.ts +97 -0
- package/dist/motion/uikit/slider.d.ts.map +1 -0
- package/dist/motion/uikit/slider.js +111 -0
- package/dist/motion/uikit/springs.d.ts +46 -1
- package/dist/motion/uikit/springs.d.ts.map +1 -1
- package/dist/motion/uikit/springs.js +48 -4
- package/dist/utils/selectionLock.d.ts +43 -0
- package/dist/utils/selectionLock.d.ts.map +1 -1
- package/dist/utils/selectionLock.js +394 -6
- package/package.json +5 -5
- package/src/AppShell/AppShell.doc.mjs +10 -0
- package/src/AppShell/AppShell.tsx +48 -2
- package/src/BottomSheet/BottomSheetPanel.tsx +16 -9
- package/src/Chat/Chat.doc.mjs +1 -0
- package/src/Chat/ChatComposerInput.doc.mjs +11 -0
- package/src/Chat/ChatComposerInput.tsx +11 -0
- package/src/Chat/ChatMessage.tsx +2 -2
- package/src/Chat/chatMessage.vars.stylex.ts +8 -8
- package/src/CommandPalette/CommandPaletteInput.tsx +6 -0
- package/src/DateInput/MonthScroller.tsx +63 -5
- package/src/Drawer/Drawer.tsx +6 -0
- package/src/DropdownMenu/useMenuOverflow.ts +44 -8
- package/src/Field/PanelSearchInput.tsx +6 -0
- package/src/Field/textEntryTraits.ts +6 -6
- package/src/Layer/layerClickBoundary.ts +2 -2
- package/src/Layer/usePressPin.ts +481 -0
- package/src/Layer/useTouchTrigger.ts +37 -1
- package/src/Link/Link.tsx +3 -0
- package/src/ProgressBar/ProgressBar.doc.mjs +3 -3
- package/src/ProgressBar/ProgressBar.tsx +82 -11
- package/src/ProgressBar/ProgressBarTouch.ts +109 -0
- package/src/Selector/Selector.tsx +29 -2
- package/src/Slider/Slider.doc.mjs +11 -7
- package/src/Slider/Slider.tsx +539 -132
- package/src/Slider/sliderTouchController.ts +1209 -0
- package/src/Slider/useSliderTouch.ts +78 -0
- package/src/Tooltip/Tooltip.doc.mjs +21 -6
- package/src/Tooltip/Tooltip.tsx +42 -5
- package/src/Tooltip/index.ts +1 -0
- package/src/Tooltip/useTooltip.doc.mjs +4 -2
- package/src/Tooltip/useTooltip.tsx +84 -21
- package/src/Typeahead/BaseTypeahead.tsx +16 -8
- package/src/gestures/GestureArena.ts +45 -4
- package/src/gestures/GestureHost.ts +949 -12
- package/src/gestures/GestureRecognizer.ts +28 -0
- package/src/gestures/PanGestureRecognizer.ts +8 -0
- package/src/gestures/PinchGestureRecognizer.ts +16 -1
- package/src/gestures/README.md +82 -3
- package/src/gestures/ScreenEdgePanGestureRecognizer.ts +7 -5
- package/src/gestures/nativeScroll.ts +49 -9
- package/src/gestures/recognizerInternals.ts +9 -2
- package/src/gestures/types.ts +12 -0
- package/src/gestures/wheelSegmenter.ts +114 -0
- package/src/motion/MEASUREMENTS.md +8 -1
- package/src/motion/README.md +6 -3
- package/src/motion/animator.ts +193 -25
- package/src/motion/paging.ts +138 -0
- package/src/motion/uikit/index.ts +5 -3
- package/src/motion/uikit/paging.ts +162 -0
- package/src/motion/uikit/progressBar.ts +82 -0
- package/src/motion/uikit/scroll.ts +2 -18
- package/src/motion/uikit/slider.ts +105 -0
- package/src/motion/uikit/springs.ts +37 -2
- package/src/utils/selectionLock.ts +501 -6
- package/dist/motion/selectPage.d.ts +0 -26
- package/dist/motion/selectPage.d.ts.map +0 -1
- package/dist/motion/selectPage.js +0 -55
- package/src/motion/selectPage.ts +0 -55
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* @file ProgressBar.tsx
|
|
7
|
-
* @input Uses React, useId, stylex,
|
|
7
|
+
* @input Uses React, useId, stylex, tokens and ProgressBarTouch
|
|
8
8
|
* @output Exports ProgressBar component, ProgressBarProps, ProgressBarVariant types
|
|
9
9
|
* @position Core implementation; consumed by index.ts
|
|
10
10
|
*
|
|
@@ -35,6 +35,7 @@ import type {BaseProps} from '../BaseProps';
|
|
|
35
35
|
import {themeProps} from '../utils/themeProps';
|
|
36
36
|
import {VisuallyHidden} from '../VisuallyHidden';
|
|
37
37
|
import type {ProgressBarVariantMap} from './index';
|
|
38
|
+
import {useProgressBarTouch} from './ProgressBarTouch';
|
|
38
39
|
|
|
39
40
|
// Every mark is wrapped in a Tooltip (marks always carry a label), so load it
|
|
40
41
|
// lazily: a ProgressBar with no marks never bundles the Tooltip chunk. While it
|
|
@@ -249,10 +250,26 @@ const styles = stylex.create({
|
|
|
249
250
|
// no transform), as before: the track's clip would cut the CanvasText
|
|
250
251
|
// outline's start cap off a slid fill, and that outline is what keeps the
|
|
251
252
|
// fill's shape visible there (architecture:theme-tokens INV11).
|
|
253
|
+
//
|
|
254
|
+
// Under a finger (TOUCH_QUERY, on every touch device), the fill moves as
|
|
255
|
+
// UIProgressView's setProgress(_:animated: true) does. It moves linearly
|
|
256
|
+
// from wherever it is drawn. Its duration is the `progress.fillTrackMs` role
|
|
257
|
+
// times the share of the track the value moved (`--_progress-bar-move`).
|
|
258
|
+
// Its curve is the `progress.curves.fill` role (`--_progress-bar-curve`).
|
|
259
|
+
// The touch layer (ProgressBarTouch) writes both, and this rule scales the
|
|
260
|
+
// duration by the theme's tempo. Above zero, the fill never draws shorter
|
|
261
|
+
// than the touch minimum (`--_progress-bar-min`, 16px on the 8px track). At
|
|
262
|
+
// zero it paints nothing (`fillEmpty`). Under reduced motion it lands at
|
|
263
|
+
// once, as everywhere (below). A mouse or trackpad keeps the theme's timing.
|
|
252
264
|
fill: {
|
|
253
265
|
width: {
|
|
254
266
|
default: '100%',
|
|
255
|
-
'@media (forced-colors: active)':
|
|
267
|
+
'@media (forced-colors: active)': {
|
|
268
|
+
default: 'var(--_progress-bar-fill, 0%)',
|
|
269
|
+
// Under a finger: the touch minimum and the flip offset (below).
|
|
270
|
+
'@media (pointer: coarse)':
|
|
271
|
+
'calc(max(var(--_progress-bar-fill, 0%), var(--_progress-bar-min, 0px)) + var(--_progress-bar-flip, 0) * 0.01px)',
|
|
272
|
+
},
|
|
256
273
|
},
|
|
257
274
|
height: '100%',
|
|
258
275
|
// Forced colors replace status fills with Canvas; this boundary keeps the
|
|
@@ -271,6 +288,19 @@ const styles = stylex.create({
|
|
|
271
288
|
default: 'translateX(calc(100% - var(--_progress-bar-fill, 0%)))',
|
|
272
289
|
'@media (forced-colors: active)': 'none',
|
|
273
290
|
},
|
|
291
|
+
'@media (pointer: coarse)': {
|
|
292
|
+
// `--_progress-bar-flip` steps 0, 1, 2, 0… on every change. It moves
|
|
293
|
+
// the fill by that many hundredths of a pixel. So a change back to the
|
|
294
|
+
// value it just left is never the running transition's own start
|
|
295
|
+
// value. CSS shortens a transition that reverses to its start, which
|
|
296
|
+
// would halve a return mid-flight (10 → 90 → 10). It takes three
|
|
297
|
+
// values because two would repeat on that return.
|
|
298
|
+
default:
|
|
299
|
+
'translateX(calc(max(var(--_progress-bar-fill, 0%), var(--_progress-bar-min, 0px)) - 100% + var(--_progress-bar-flip, 0) * 0.01px))',
|
|
300
|
+
':dir(rtl)':
|
|
301
|
+
'translateX(calc(min(calc(100% - var(--_progress-bar-fill, 0%)), calc(100% - var(--_progress-bar-min, 0px))) - var(--_progress-bar-flip, 0) * 0.01px))',
|
|
302
|
+
},
|
|
303
|
+
// After the touch key, so StyleX negates that key under forced colors.
|
|
274
304
|
'@media (forced-colors: active)': 'none',
|
|
275
305
|
},
|
|
276
306
|
willChange: {
|
|
@@ -281,17 +311,36 @@ const styles = stylex.create({
|
|
|
281
311
|
default: 'transform',
|
|
282
312
|
'@media (forced-colors: active)': 'width',
|
|
283
313
|
},
|
|
284
|
-
// Under reduced motion a new value lands at once,
|
|
285
|
-
// fill never travels
|
|
286
|
-
//
|
|
287
|
-
//
|
|
314
|
+
// Under reduced motion a new value lands at once, with a mouse as with a
|
|
315
|
+
// finger: the fill never travels. The keys are the shared reduced-motion
|
|
316
|
+
// queries, as the other transitions spell them. The coarse key comes after
|
|
317
|
+
// the touch key, so it wins there.
|
|
288
318
|
transitionDuration: {
|
|
289
319
|
default: durationVars['--duration-medium'],
|
|
320
|
+
// The touch layer's move (the `progress.fillTrackMs` role times the
|
|
321
|
+
// share moved), scaled by the theme's tempo.
|
|
322
|
+
'@media (pointer: coarse)': {
|
|
323
|
+
default:
|
|
324
|
+
'calc(var(--_progress-bar-move, 0ms) * var(--_astryx-motion-tempo, 1))',
|
|
325
|
+
// Under forced colors the fill is sized by `width` instead. Touch
|
|
326
|
+
// motion never animates a layout property, so with a finger the new
|
|
327
|
+
// size lands at once there.
|
|
328
|
+
'@media (forced-colors: active)': '0s',
|
|
329
|
+
},
|
|
290
330
|
'@media (prefers-reduced-motion: reduce) and (not (pointer: coarse))':
|
|
291
331
|
'0s',
|
|
292
332
|
'@media (prefers-reduced-motion: reduce) and (pointer: coarse)': '0s',
|
|
293
333
|
},
|
|
294
|
-
transitionTimingFunction:
|
|
334
|
+
transitionTimingFunction: {
|
|
335
|
+
default: easeVars['--ease-standard'],
|
|
336
|
+
'@media (pointer: coarse)': 'var(--_progress-bar-curve, linear)',
|
|
337
|
+
},
|
|
338
|
+
},
|
|
339
|
+
fillEmpty: {
|
|
340
|
+
opacity: {
|
|
341
|
+
default: null,
|
|
342
|
+
'@media (pointer: coarse)': 0,
|
|
343
|
+
},
|
|
295
344
|
},
|
|
296
345
|
indeterminateFill: {
|
|
297
346
|
height: '100%',
|
|
@@ -428,7 +477,15 @@ const markOnTrackStyles = stylex.create({
|
|
|
428
477
|
// `CSSProperties` has no index signature for custom properties, so the fill's
|
|
429
478
|
// inline style — which publishes the filled share as the private
|
|
430
479
|
// `--_progress-bar-fill` — needs this augmentation to typecheck.
|
|
431
|
-
type FillStyle = CSSProperties &
|
|
480
|
+
type FillStyle = CSSProperties &
|
|
481
|
+
Record<
|
|
482
|
+
| '--_progress-bar-fill'
|
|
483
|
+
| '--_progress-bar-move'
|
|
484
|
+
| '--_progress-bar-curve'
|
|
485
|
+
| '--_progress-bar-flip'
|
|
486
|
+
| '--_progress-bar-min',
|
|
487
|
+
string
|
|
488
|
+
>;
|
|
432
489
|
|
|
433
490
|
function defaultFormatValueLabel(value: number, max: number): string {
|
|
434
491
|
const pct = max > 0 ? Math.round((value / max) * 100) : 0;
|
|
@@ -509,7 +566,16 @@ export function ProgressBar({
|
|
|
509
566
|
const showValueLabel = hasValueLabel && !isIndeterminate;
|
|
510
567
|
|
|
511
568
|
const fillVariant = isDisabled ? 'disabled' : variant;
|
|
512
|
-
|
|
569
|
+
// Under a finger the fill moves as UIProgressView's does and draws at least
|
|
570
|
+
// 16px; what that needs from script is the touch layer's (ProgressBarTouch).
|
|
571
|
+
const touch = useProgressBarTouch(
|
|
572
|
+
percentage,
|
|
573
|
+
!isIndeterminate && (marks?.length ?? 0) > 0,
|
|
574
|
+
);
|
|
575
|
+
const fillStyle: FillStyle = {
|
|
576
|
+
'--_progress-bar-fill': `${percentage}%`,
|
|
577
|
+
...touch.fillVars,
|
|
578
|
+
};
|
|
513
579
|
|
|
514
580
|
// Marks make no sense without a determinate value, so they are only drawn
|
|
515
581
|
// in determinate mode. Non-finite mark values are dropped; the rest are
|
|
@@ -532,7 +598,7 @@ export function ProgressBar({
|
|
|
532
598
|
value: mark.value,
|
|
533
599
|
label: mark.label,
|
|
534
600
|
pct,
|
|
535
|
-
isOnFill: percentage > 0 && pct <=
|
|
601
|
+
isOnFill: percentage > 0 && pct <= touch.drawnPercentage,
|
|
536
602
|
};
|
|
537
603
|
})
|
|
538
604
|
: [];
|
|
@@ -582,6 +648,7 @@ export function ProgressBar({
|
|
|
582
648
|
aria-valuemax={isIndeterminate ? undefined : safeMax}
|
|
583
649
|
aria-labelledby={labelId}
|
|
584
650
|
aria-valuetext={isIndeterminate ? undefined : valueText}
|
|
651
|
+
ref={touch.trackRef}
|
|
585
652
|
{...mergeProps(
|
|
586
653
|
themeProps('progress-bar-track'),
|
|
587
654
|
stylex.props(styles.track),
|
|
@@ -600,7 +667,11 @@ export function ProgressBar({
|
|
|
600
667
|
<div
|
|
601
668
|
{...mergeProps(
|
|
602
669
|
themeProps('progress-bar-fill', {variant: fillVariant}),
|
|
603
|
-
stylex.props(
|
|
670
|
+
stylex.props(
|
|
671
|
+
styles.fill,
|
|
672
|
+
percentage === 0 && styles.fillEmpty,
|
|
673
|
+
variantStyles[fillVariant],
|
|
674
|
+
),
|
|
604
675
|
)}
|
|
605
676
|
style={fillStyle}
|
|
606
677
|
/>
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
|
|
3
|
+
'use client';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* @file ProgressBarTouch.ts
|
|
7
|
+
* @input Uses React, the touch presentation resolver, the shared resize
|
|
8
|
+
* observer, and the progress table's roles (through resolveMotion)
|
|
9
|
+
* @output useProgressBarTouch: the fill's touch variables (move length,
|
|
10
|
+
* curve, return offset, minimum length) and, under a finger with marks,
|
|
11
|
+
* the share of the track the fill draws
|
|
12
|
+
* @position ProgressBar's touch layer, imported only by ProgressBar.tsx
|
|
13
|
+
*
|
|
14
|
+
* SYNC: When modified, update /packages/core/src/ProgressBar/ProgressBar.tsx
|
|
15
|
+
* (its touch styles read these variables and the 16px minimum) and
|
|
16
|
+
* /packages/core/src/motion/uikit/progressBar.ts.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
import {useEffect, useRef, useState, type RefObject} from 'react';
|
|
20
|
+
import {formatMotionRoleValue} from '../motion/roles';
|
|
21
|
+
import {resolveMotion} from '../motion/resolveMotion';
|
|
22
|
+
import {progressBar} from '../motion/uikit/progressBar';
|
|
23
|
+
import {observeResize} from '../utils/sharedResizeObserver';
|
|
24
|
+
import {useTouchPresentation} from '../utils/touchPresentation';
|
|
25
|
+
|
|
26
|
+
/** The track's height. */
|
|
27
|
+
const TRACK_HEIGHT_PX = 8;
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Under a finger the fill never draws shorter than this above zero:
|
|
31
|
+
* UIProgressView's minimum, two track heights, on ProgressBar's 8px track.
|
|
32
|
+
*/
|
|
33
|
+
export const TOUCH_MIN_FILL_PX =
|
|
34
|
+
progressBar.progressView.minimumFillHeights * TRACK_HEIGHT_PX;
|
|
35
|
+
|
|
36
|
+
export interface ProgressBarTouch {
|
|
37
|
+
/** The fill's touch timing variables, set inline on the fill. */
|
|
38
|
+
fillVars: Record<
|
|
39
|
+
| '--_progress-bar-move'
|
|
40
|
+
| '--_progress-bar-curve'
|
|
41
|
+
| '--_progress-bar-flip'
|
|
42
|
+
| '--_progress-bar-min',
|
|
43
|
+
string
|
|
44
|
+
>;
|
|
45
|
+
/** The track, measured under a finger when the bar has marks. */
|
|
46
|
+
trackRef: RefObject<HTMLDivElement | null>;
|
|
47
|
+
/** The share of the track the fill draws (0–100), for placing marks. */
|
|
48
|
+
drawnPercentage: number;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export function useProgressBarTouch(
|
|
52
|
+
percentage: number,
|
|
53
|
+
hasMarks: boolean,
|
|
54
|
+
): ProgressBarTouch {
|
|
55
|
+
// The touch fill's duration comes from how far the value moved since the
|
|
56
|
+
// last value, the model change that UIKit measures. It holds until the next
|
|
57
|
+
// change, so a re-render with the same value keeps it.
|
|
58
|
+
const [motion, setMotion] = useState({percentage, change: 0, flip: 0});
|
|
59
|
+
const isNewValue = motion.percentage !== percentage;
|
|
60
|
+
// The share of the track the value moved (0–1). `flip` steps through three
|
|
61
|
+
// values on every change (see the fill's touch transform).
|
|
62
|
+
const change = isNewValue
|
|
63
|
+
? Math.abs(percentage - motion.percentage) / 100
|
|
64
|
+
: motion.change;
|
|
65
|
+
const flip = isNewValue ? (motion.flip + 1) % 3 : motion.flip;
|
|
66
|
+
if (isNewValue) {
|
|
67
|
+
setMotion({percentage, change, flip});
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// Under a finger the fill draws at least TOUCH_MIN_FILL_PX long, so a mark
|
|
71
|
+
// it covers there sits on the fill. That length is a share of the track's
|
|
72
|
+
// width, which only layout knows. So the track is measured, but only under
|
|
73
|
+
// a finger and only when the bar has marks.
|
|
74
|
+
const {isTouch} = useTouchPresentation();
|
|
75
|
+
const watchesTrack = isTouch && hasMarks;
|
|
76
|
+
const trackRef = useRef<HTMLDivElement | null>(null);
|
|
77
|
+
const [trackWidth, setTrackWidth] = useState(0);
|
|
78
|
+
useEffect(() => {
|
|
79
|
+
const element = trackRef.current;
|
|
80
|
+
if (!watchesTrack || element == null) {
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
// The shared observer; its first call (on registration) carries no
|
|
84
|
+
// contentRect, so that one reads the box.
|
|
85
|
+
return observeResize(element, entry => {
|
|
86
|
+
setTrackWidth(
|
|
87
|
+
entry.contentRect?.width ?? element.getBoundingClientRect().width,
|
|
88
|
+
);
|
|
89
|
+
});
|
|
90
|
+
}, [watchesTrack]);
|
|
91
|
+
const drawnPercentage =
|
|
92
|
+
watchesTrack && percentage > 0 && trackWidth > 0
|
|
93
|
+
? Math.max(percentage, (TOUCH_MIN_FILL_PX / trackWidth) * 100)
|
|
94
|
+
: percentage;
|
|
95
|
+
|
|
96
|
+
// CSS applies the theme's tempo to this duration.
|
|
97
|
+
const fillTrackMs = resolveMotion(null, progressBar, 'fillTrackMs');
|
|
98
|
+
const curve = resolveMotion(null, progressBar.curves.fill);
|
|
99
|
+
return {
|
|
100
|
+
fillVars: {
|
|
101
|
+
'--_progress-bar-move': `${Math.round(change * fillTrackMs * 1000) / 1000}ms`,
|
|
102
|
+
'--_progress-bar-curve': formatMotionRoleValue('curve', curve),
|
|
103
|
+
'--_progress-bar-flip': String(flip),
|
|
104
|
+
'--_progress-bar-min': `${TOUCH_MIN_FILL_PX}px`,
|
|
105
|
+
},
|
|
106
|
+
trackRef,
|
|
107
|
+
drawnPercentage,
|
|
108
|
+
};
|
|
109
|
+
}
|
|
@@ -46,6 +46,7 @@ import {
|
|
|
46
46
|
} from '../Field';
|
|
47
47
|
import {Divider} from '../Divider';
|
|
48
48
|
import {layerAnimations} from '../Layer/layerAnimations.stylex';
|
|
49
|
+
import {reducedMotionFadeConsts} from '../motion/reducedMotion.stylex';
|
|
49
50
|
import {useKeepLayerOpenProps, type LayerPlacement} from '../Layer/useLayer';
|
|
50
51
|
import {InternalInputClearButton} from '../Field/InputClearButton';
|
|
51
52
|
import {Spinner} from '../Spinner';
|
|
@@ -113,6 +114,14 @@ import english from '../i18n/generated-english/Selector/Selector.generated';
|
|
|
113
114
|
|
|
114
115
|
registerEnglishFallback(english);
|
|
115
116
|
|
|
117
|
+
/**
|
|
118
|
+
* A guess, confirmed by the open measure: more options than this overflow
|
|
119
|
+
* the popover listbox's 300 px block size even at 24 px rows, under the
|
|
120
|
+
* smallest default row (28 px). A wrong guess (a theme with shorter rows, a
|
|
121
|
+
* taller bottom sheet) costs only the measure's correction.
|
|
122
|
+
*/
|
|
123
|
+
const MOST_ROWS_THAT_FIT = 12;
|
|
124
|
+
|
|
116
125
|
const styles = stylex.create({
|
|
117
126
|
// Trigger container — the enhanced click target wrapping the combobox button and clear button as siblings
|
|
118
127
|
triggerContainer: {
|
|
@@ -350,7 +359,21 @@ const styles = stylex.create({
|
|
|
350
359
|
paddingBlock: spacingVars['--spacing-1'],
|
|
351
360
|
paddingInline: spacingVars['--spacing-1'],
|
|
352
361
|
opacity: 1,
|
|
353
|
-
|
|
362
|
+
// Under Reduce Motion the list appears at once on a fine pointer, and
|
|
363
|
+
// takes the shared cross-fade on a coarse one.
|
|
364
|
+
transitionProperty: 'opacity',
|
|
365
|
+
transitionDuration: {
|
|
366
|
+
default: durationVars['--duration-fast'],
|
|
367
|
+
'@media (prefers-reduced-motion: reduce) and (pointer: coarse)':
|
|
368
|
+
reducedMotionFadeConsts.duration,
|
|
369
|
+
'@media (prefers-reduced-motion: reduce) and (not (pointer: coarse))':
|
|
370
|
+
'0s',
|
|
371
|
+
},
|
|
372
|
+
transitionTimingFunction: {
|
|
373
|
+
default: 'ease',
|
|
374
|
+
'@media (prefers-reduced-motion: reduce) and (pointer: coarse)':
|
|
375
|
+
reducedMotionFadeConsts.easing,
|
|
376
|
+
},
|
|
354
377
|
},
|
|
355
378
|
dropdownInput: {
|
|
356
379
|
// The input trigger's text inset includes its border. Mirror that extra
|
|
@@ -376,7 +399,8 @@ const styles = stylex.create({
|
|
|
376
399
|
},
|
|
377
400
|
dropdownHidden: {
|
|
378
401
|
opacity: 0,
|
|
379
|
-
|
|
402
|
+
// A longhand: the shorthand would lose to the list's longhands.
|
|
403
|
+
transitionProperty: 'none',
|
|
380
404
|
},
|
|
381
405
|
|
|
382
406
|
// Popover container (for anchor positioning)
|
|
@@ -1407,10 +1431,13 @@ export function Selector<T extends SelectorOptionType>(
|
|
|
1407
1431
|
return match == null ? -1 : Number(match[1]);
|
|
1408
1432
|
}, []);
|
|
1409
1433
|
// Re-measured when the option set changes; the count is the dependency.
|
|
1434
|
+
// A list longer than the most rows that can fit cannot fit, so it starts
|
|
1435
|
+
// as a scroll container and the open measure only confirms it.
|
|
1410
1436
|
const listboxHasOverflow = useMenuOverflow(
|
|
1411
1437
|
listboxRef,
|
|
1412
1438
|
filteredItems.length,
|
|
1413
1439
|
surface.isOpen,
|
|
1440
|
+
filteredItems.length > MOST_ROWS_THAT_FIT,
|
|
1414
1441
|
);
|
|
1415
1442
|
const listboxPress = useMenuPress({
|
|
1416
1443
|
menuRef: listboxRef,
|
|
@@ -237,12 +237,14 @@ export const docs = {
|
|
|
237
237
|
{
|
|
238
238
|
name: 'containerClassName',
|
|
239
239
|
type: 'string',
|
|
240
|
-
description:
|
|
240
|
+
description:
|
|
241
|
+
'Class name for the field container. Same targeting as `containerXstyle`.',
|
|
241
242
|
},
|
|
242
243
|
{
|
|
243
244
|
name: 'containerStyle',
|
|
244
245
|
type: 'CSSProperties',
|
|
245
|
-
description:
|
|
246
|
+
description:
|
|
247
|
+
'Inline styles for the field container. Same targeting as `containerXstyle`.',
|
|
246
248
|
},
|
|
247
249
|
{
|
|
248
250
|
name: 'controlXstyle',
|
|
@@ -295,7 +297,7 @@ export const docs = {
|
|
|
295
297
|
],
|
|
296
298
|
anatomy,
|
|
297
299
|
description:
|
|
298
|
-
'A draggable control for selecting a numeric value or range within defined bounds. Supports single value and range selection, tick marks, custom value formatting, and vertical orientation. Use it when users need to explore a continuous range, such as volume, price, or percentage.',
|
|
300
|
+
'A draggable control for selecting a numeric value or range within defined bounds. Supports single value and range selection, tick marks, custom value formatting, and vertical orientation. Use it when users need to explore a continuous range, such as volume, price, or percentage. Under a finger, on any touch device, it tracks as UISlider does: only the thumb takes a drag, through a target larger than it, the value follows the finger’s travel rather than jumping to it, a release coasts on before `onChangeEnd`, a swipe from the track scrolls the page, and a tap on the track or a mark sets the value when the finger lifts (the thumb springs there). A mouse drag on a thumb tracks the same way, past the same 10 px in any direction, with no lift or coast; a mouse press on the track moves the thumb there, and only a primary click presses. A pen drags as a finger does, claiming only along the track, and its drag ends at the release with no coast. A wheel or a trackpad scroll over the slider goes to whatever scrolls under it.',
|
|
299
301
|
bestPractices: [
|
|
300
302
|
{
|
|
301
303
|
guidance: true,
|
|
@@ -508,7 +510,7 @@ export const docsZh = {
|
|
|
508
510
|
usage: {
|
|
509
511
|
anatomy,
|
|
510
512
|
description:
|
|
511
|
-
'A draggable control for selecting a numeric value or range within defined bounds. Supports single value and range selection, tick marks, custom value formatting, and vertical orientation. Use it when users need to explore a continuous range, such as volume, price, or percentage.',
|
|
513
|
+
'A draggable control for selecting a numeric value or range within defined bounds. Supports single value and range selection, tick marks, custom value formatting, and vertical orientation. Use it when users need to explore a continuous range, such as volume, price, or percentage. Under a finger, on any touch device, it tracks as UISlider does: only the thumb takes a drag, through a target larger than it, the value follows the finger’s travel rather than jumping to it, a release coasts on before `onChangeEnd`, a swipe from the track scrolls the page, and a tap on the track or a mark sets the value when the finger lifts (the thumb springs there). A mouse drag on a thumb tracks the same way, past the same 10 px in any direction, with no lift or coast; a mouse press on the track moves the thumb there, and only a primary click presses. A pen drags as a finger does, claiming only along the track, and its drag ends at the release with no coast. A wheel or a trackpad scroll over the slider goes to whatever scrolls under it.',
|
|
512
514
|
bestPractices: [
|
|
513
515
|
{
|
|
514
516
|
guidance: true,
|
|
@@ -544,7 +546,7 @@ export const docsDense = {
|
|
|
544
546
|
usage: {
|
|
545
547
|
anatomy,
|
|
546
548
|
description:
|
|
547
|
-
'A draggable control for selecting a numeric value or range within defined bounds. Supports single value and range selection, tick marks, custom value formatting, and vertical orientation. Use it when users need to explore a continuous range, such as volume, price, or percentage.',
|
|
549
|
+
'A draggable control for selecting a numeric value or range within defined bounds. Supports single value and range selection, tick marks, custom value formatting, and vertical orientation. Use it when users need to explore a continuous range, such as volume, price, or percentage. Under a finger, on any touch device, it tracks as UISlider does: only the thumb takes a drag, through a target larger than it, the value follows the finger’s travel rather than jumping to it, a release coasts on before `onChangeEnd`, a swipe from the track scrolls the page, and a tap on the track or a mark sets the value when the finger lifts (the thumb springs there). A mouse drag on a thumb tracks the same way, past the same 10 px in any direction, with no lift or coast; a mouse press on the track moves the thumb there, and only a primary click presses. A pen drags as a finger does, claiming only along the track, and its drag ends at the release with no coast. A wheel or a trackpad scroll over the slider goes to whatever scrolls under it.',
|
|
548
550
|
bestPractices: [
|
|
549
551
|
{
|
|
550
552
|
guidance: true,
|
|
@@ -574,10 +576,12 @@ export const docsDense = {
|
|
|
574
576
|
],
|
|
575
577
|
},
|
|
576
578
|
propDescriptions: {
|
|
577
|
-
containerXstyle:
|
|
579
|
+
containerXstyle:
|
|
580
|
+
'StyleX styles for the field container; no effect in InputGroup or horizontal-labels FormLayout. No root xstyle/className/style.',
|
|
578
581
|
containerClassName: 'Class name for the field container.',
|
|
579
582
|
containerStyle: 'Inline styles for the field container.',
|
|
580
|
-
controlXstyle:
|
|
583
|
+
controlXstyle:
|
|
584
|
+
'StyleX styles for the visual control box (border, background, icons); not the native input.',
|
|
581
585
|
controlClassName: 'Class name for the visual control box.',
|
|
582
586
|
controlStyle: 'Inline styles for the visual control box.',
|
|
583
587
|
label: 'Label text (always rendered for a11y).',
|