@radix-ng/primitives 0.51.0 → 1.0.0-beta.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/fesm2022/radix-ng-primitives-accordion.mjs +105 -38
- package/fesm2022/radix-ng-primitives-accordion.mjs.map +1 -1
- package/fesm2022/radix-ng-primitives-alert-dialog.mjs +221 -129
- package/fesm2022/radix-ng-primitives-alert-dialog.mjs.map +1 -1
- package/fesm2022/radix-ng-primitives-arrow.mjs +20 -4
- package/fesm2022/radix-ng-primitives-arrow.mjs.map +1 -1
- package/fesm2022/radix-ng-primitives-aspect-ratio.mjs.map +1 -1
- package/fesm2022/radix-ng-primitives-avatar.mjs +54 -61
- package/fesm2022/radix-ng-primitives-avatar.mjs.map +1 -1
- package/fesm2022/radix-ng-primitives-button.mjs +123 -0
- package/fesm2022/radix-ng-primitives-button.mjs.map +1 -0
- package/fesm2022/radix-ng-primitives-calendar.mjs.map +1 -1
- package/fesm2022/radix-ng-primitives-checkbox.mjs +378 -54
- package/fesm2022/radix-ng-primitives-checkbox.mjs.map +1 -1
- package/fesm2022/radix-ng-primitives-collapsible.mjs +182 -81
- package/fesm2022/radix-ng-primitives-collapsible.mjs.map +1 -1
- package/fesm2022/radix-ng-primitives-collection.mjs +40 -57
- package/fesm2022/radix-ng-primitives-collection.mjs.map +1 -1
- package/fesm2022/radix-ng-primitives-config.mjs.map +1 -1
- package/fesm2022/radix-ng-primitives-context-menu.mjs +140 -424
- package/fesm2022/radix-ng-primitives-context-menu.mjs.map +1 -1
- package/fesm2022/radix-ng-primitives-core.mjs +735 -744
- package/fesm2022/radix-ng-primitives-core.mjs.map +1 -1
- package/fesm2022/radix-ng-primitives-cropper.mjs +1 -0
- package/fesm2022/radix-ng-primitives-cropper.mjs.map +1 -1
- package/fesm2022/radix-ng-primitives-date-field.mjs +51 -45
- package/fesm2022/radix-ng-primitives-date-field.mjs.map +1 -1
- package/fesm2022/radix-ng-primitives-dialog.mjs +655 -327
- package/fesm2022/radix-ng-primitives-dialog.mjs.map +1 -1
- package/fesm2022/radix-ng-primitives-dismissable-layer.mjs +70 -46
- package/fesm2022/radix-ng-primitives-dismissable-layer.mjs.map +1 -1
- package/fesm2022/radix-ng-primitives-drawer.mjs +1059 -0
- package/fesm2022/radix-ng-primitives-drawer.mjs.map +1 -0
- package/fesm2022/radix-ng-primitives-editable.mjs.map +1 -1
- package/fesm2022/radix-ng-primitives-field.mjs +363 -0
- package/fesm2022/radix-ng-primitives-field.mjs.map +1 -0
- package/fesm2022/radix-ng-primitives-fieldset.mjs +79 -0
- package/fesm2022/radix-ng-primitives-fieldset.mjs.map +1 -0
- package/fesm2022/radix-ng-primitives-focus-scope.mjs +23 -8
- package/fesm2022/radix-ng-primitives-focus-scope.mjs.map +1 -1
- package/fesm2022/radix-ng-primitives-input.mjs +172 -0
- package/fesm2022/radix-ng-primitives-input.mjs.map +1 -0
- package/fesm2022/radix-ng-primitives-label.mjs +6 -6
- package/fesm2022/radix-ng-primitives-label.mjs.map +1 -1
- package/fesm2022/radix-ng-primitives-menu.mjs +1480 -344
- package/fesm2022/radix-ng-primitives-menu.mjs.map +1 -1
- package/fesm2022/radix-ng-primitives-menubar.mjs +290 -162
- package/fesm2022/radix-ng-primitives-menubar.mjs.map +1 -1
- package/fesm2022/radix-ng-primitives-meter.mjs +271 -0
- package/fesm2022/radix-ng-primitives-meter.mjs.map +1 -0
- package/fesm2022/radix-ng-primitives-navigation-menu.mjs +1052 -1553
- package/fesm2022/radix-ng-primitives-navigation-menu.mjs.map +1 -1
- package/fesm2022/radix-ng-primitives-number-field.mjs +1102 -367
- package/fesm2022/radix-ng-primitives-number-field.mjs.map +1 -1
- package/fesm2022/radix-ng-primitives-pagination.mjs.map +1 -1
- package/fesm2022/radix-ng-primitives-popover.mjs +978 -989
- package/fesm2022/radix-ng-primitives-popover.mjs.map +1 -1
- package/fesm2022/radix-ng-primitives-popper.mjs +91 -41
- package/fesm2022/radix-ng-primitives-popper.mjs.map +1 -1
- package/fesm2022/radix-ng-primitives-portal.mjs +34 -10
- package/fesm2022/radix-ng-primitives-portal.mjs.map +1 -1
- package/fesm2022/radix-ng-primitives-presence.mjs +134 -246
- package/fesm2022/radix-ng-primitives-presence.mjs.map +1 -1
- package/fesm2022/radix-ng-primitives-preview-card.mjs +997 -0
- package/fesm2022/radix-ng-primitives-preview-card.mjs.map +1 -0
- package/fesm2022/radix-ng-primitives-progress.mjs +223 -84
- package/fesm2022/radix-ng-primitives-progress.mjs.map +1 -1
- package/fesm2022/radix-ng-primitives-radio.mjs +191 -51
- package/fesm2022/radix-ng-primitives-radio.mjs.map +1 -1
- package/fesm2022/radix-ng-primitives-roving-focus.mjs +96 -50
- package/fesm2022/radix-ng-primitives-roving-focus.mjs.map +1 -1
- package/fesm2022/radix-ng-primitives-select.mjs +791 -509
- package/fesm2022/radix-ng-primitives-select.mjs.map +1 -1
- package/fesm2022/radix-ng-primitives-separator.mjs +12 -35
- package/fesm2022/radix-ng-primitives-separator.mjs.map +1 -1
- package/fesm2022/radix-ng-primitives-slider.mjs +969 -717
- package/fesm2022/radix-ng-primitives-slider.mjs.map +1 -1
- package/fesm2022/radix-ng-primitives-stepper.mjs +15 -19
- package/fesm2022/radix-ng-primitives-stepper.mjs.map +1 -1
- package/fesm2022/radix-ng-primitives-switch.mjs +125 -113
- package/fesm2022/radix-ng-primitives-switch.mjs.map +1 -1
- package/fesm2022/radix-ng-primitives-tabs.mjs +381 -108
- package/fesm2022/radix-ng-primitives-tabs.mjs.map +1 -1
- package/fesm2022/radix-ng-primitives-time-field.mjs +55 -46
- package/fesm2022/radix-ng-primitives-time-field.mjs.map +1 -1
- package/fesm2022/radix-ng-primitives-toggle-group.mjs +121 -247
- package/fesm2022/radix-ng-primitives-toggle-group.mjs.map +1 -1
- package/fesm2022/radix-ng-primitives-toggle.mjs +98 -61
- package/fesm2022/radix-ng-primitives-toggle.mjs.map +1 -1
- package/fesm2022/radix-ng-primitives-toolbar.mjs +303 -92
- package/fesm2022/radix-ng-primitives-toolbar.mjs.map +1 -1
- package/fesm2022/radix-ng-primitives-tooltip.mjs +690 -1071
- package/fesm2022/radix-ng-primitives-tooltip.mjs.map +1 -1
- package/fesm2022/radix-ng-primitives-visually-hidden.mjs +25 -66
- package/fesm2022/radix-ng-primitives-visually-hidden.mjs.map +1 -1
- package/meter/README.md +3 -0
- package/navigation-menu/README.md +2 -1
- package/package.json +31 -18
- package/portal/README.md +2 -0
- package/preview-card/README.md +3 -0
- package/schematics/collection.json +1 -0
- package/schematics/ng-add/index.d.ts +3 -2
- package/schematics/ng-add/index.js +62 -31
- package/schematics/ng-add/index.js.map +1 -1
- package/schematics/ng-add/package-config.d.ts +4 -2
- package/schematics/ng-add/package-config.js +10 -2
- package/schematics/ng-add/package-config.js.map +1 -1
- package/schematics/ng-add/schema.d.ts +3 -0
- package/schematics/ng-add/schema.js +3 -0
- package/schematics/ng-add/schema.js.map +1 -0
- package/schematics/ng-add/schema.json +14 -0
- package/select/README.md +2 -0
- package/types/radix-ng-primitives-accordion.d.ts +48 -14
- package/types/radix-ng-primitives-alert-dialog.d.ts +95 -38
- package/types/radix-ng-primitives-arrow.d.ts +1 -1
- package/types/radix-ng-primitives-aspect-ratio.d.ts +1 -1
- package/types/radix-ng-primitives-avatar.d.ts +7 -11
- package/types/radix-ng-primitives-button.d.ts +73 -0
- package/types/radix-ng-primitives-calendar.d.ts +1 -2
- package/types/radix-ng-primitives-checkbox.d.ts +201 -32
- package/types/radix-ng-primitives-collapsible.d.ts +112 -39
- package/types/radix-ng-primitives-collection.d.ts +38 -34
- package/types/radix-ng-primitives-config.d.ts +1 -1
- package/types/radix-ng-primitives-context-menu.d.ts +60 -116
- package/types/radix-ng-primitives-core.d.ts +307 -236
- package/types/radix-ng-primitives-cropper.d.ts +2 -2
- package/types/radix-ng-primitives-date-field.d.ts +38 -23
- package/types/radix-ng-primitives-dialog.d.ts +282 -165
- package/types/radix-ng-primitives-dismissable-layer.d.ts +15 -7
- package/types/radix-ng-primitives-drawer.d.ts +448 -0
- package/types/radix-ng-primitives-editable.d.ts +1 -1
- package/types/radix-ng-primitives-field.d.ts +373 -0
- package/types/radix-ng-primitives-fieldset.d.ts +48 -0
- package/types/radix-ng-primitives-focus-scope.d.ts +13 -5
- package/types/radix-ng-primitives-input.d.ts +87 -0
- package/types/radix-ng-primitives-label.d.ts +0 -1
- package/types/radix-ng-primitives-menu.d.ts +572 -99
- package/types/radix-ng-primitives-menubar.d.ts +60 -50
- package/types/radix-ng-primitives-meter.d.ts +193 -0
- package/types/radix-ng-primitives-navigation-menu.d.ts +422 -340
- package/types/radix-ng-primitives-number-field.d.ts +405 -145
- package/types/radix-ng-primitives-pagination.d.ts +2 -2
- package/types/radix-ng-primitives-popover.d.ts +365 -351
- package/types/radix-ng-primitives-popper.d.ts +49 -9
- package/types/radix-ng-primitives-portal.d.ts +14 -6
- package/types/radix-ng-primitives-presence.d.ts +28 -76
- package/types/radix-ng-primitives-preview-card.d.ts +359 -0
- package/types/radix-ng-primitives-progress.d.ts +174 -48
- package/types/radix-ng-primitives-radio.d.ts +55 -25
- package/types/radix-ng-primitives-roving-focus.d.ts +30 -21
- package/types/radix-ng-primitives-select.d.ts +475 -177
- package/types/radix-ng-primitives-separator.d.ts +7 -32
- package/types/radix-ng-primitives-slider.d.ts +315 -201
- package/types/radix-ng-primitives-stepper.d.ts +5 -7
- package/types/radix-ng-primitives-switch.d.ts +86 -71
- package/types/radix-ng-primitives-tabs.d.ts +213 -79
- package/types/radix-ng-primitives-time-field.d.ts +42 -27
- package/types/radix-ng-primitives-toggle-group.d.ts +85 -164
- package/types/radix-ng-primitives-toggle.d.ts +43 -53
- package/types/radix-ng-primitives-toolbar.d.ts +163 -38
- package/types/radix-ng-primitives-tooltip.d.ts +347 -384
- package/types/radix-ng-primitives-visually-hidden.d.ts +19 -19
- package/dropdown-menu/README.md +0 -1
- package/fesm2022/radix-ng-primitives-dropdown-menu.mjs +0 -581
- package/fesm2022/radix-ng-primitives-dropdown-menu.mjs.map +0 -1
- package/fesm2022/radix-ng-primitives-hover-card.mjs +0 -1238
- package/fesm2022/radix-ng-primitives-hover-card.mjs.map +0 -1
- package/fesm2022/radix-ng-primitives-select2.mjs +0 -897
- package/fesm2022/radix-ng-primitives-select2.mjs.map +0 -1
- package/fesm2022/radix-ng-primitives-tooltip2.mjs +0 -735
- package/fesm2022/radix-ng-primitives-tooltip2.mjs.map +0 -1
- package/hover-card/README.md +0 -3
- package/select2/README.md +0 -3
- package/tooltip2/README.md +0 -3
- package/types/radix-ng-primitives-dropdown-menu.d.ts +0 -171
- package/types/radix-ng-primitives-hover-card.d.ts +0 -471
- package/types/radix-ng-primitives-select2.d.ts +0 -511
- package/types/radix-ng-primitives-tooltip2.d.ts +0 -325
|
@@ -1,503 +1,1238 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
|
-
import {
|
|
2
|
+
import { numberAttribute, computed, signal, inject, DestroyRef, input, booleanAttribute, Directive, ElementRef, model, output, effect, untracked, NgModule } from '@angular/core';
|
|
3
|
+
import * as i1 from '@radix-ng/primitives/core';
|
|
4
|
+
import { createContext, clamp, getActiveElement, injectControlValueAccessor, injectId, RdxControlValueAccessor } from '@radix-ng/primitives/core';
|
|
3
5
|
import { NumberFormatter, NumberParser } from '@internationalized/number';
|
|
4
|
-
import
|
|
5
|
-
import {
|
|
6
|
-
import { getActiveElement, isNullish, clamp, snapValueToStep, provideToken } from '@radix-ng/primitives/core';
|
|
6
|
+
import * as i1$1 from '@radix-ng/primitives/portal';
|
|
7
|
+
import { RdxPortal } from '@radix-ng/primitives/portal';
|
|
7
8
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
9
|
+
/**
|
|
10
|
+
* The Number Field context exposes the root directive instance to every child part.
|
|
11
|
+
* The root owns all state, parsing/formatting and value-change logic; parts read
|
|
12
|
+
* signals and call methods off it.
|
|
13
|
+
*
|
|
14
|
+
* @see https://base-ui.com/react/components/number-field
|
|
15
|
+
*/
|
|
16
|
+
const [injectNumberFieldRootContext, provideNumberFieldRootContext] = createContext('RdxNumberFieldRootContext');
|
|
12
17
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
const resetTimeout = () => {
|
|
22
|
-
const timer = timeout();
|
|
23
|
-
if (timer !== undefined) {
|
|
24
|
-
window.clearTimeout(timer);
|
|
25
|
-
timeout.set(undefined);
|
|
26
|
-
}
|
|
27
|
-
};
|
|
28
|
-
const onIncrementPressStart = (delay) => {
|
|
29
|
-
resetTimeout();
|
|
30
|
-
if (options.disabled())
|
|
31
|
-
return;
|
|
32
|
-
triggerHook.next();
|
|
33
|
-
timeout.set(window.setTimeout(() => {
|
|
34
|
-
onIncrementPressStart(60);
|
|
35
|
-
}, delay));
|
|
36
|
-
};
|
|
37
|
-
const onPressStart = (event) => {
|
|
38
|
-
if (event.button !== 0 || isPressed())
|
|
39
|
-
return;
|
|
40
|
-
event.preventDefault();
|
|
41
|
-
isPressed.set(true);
|
|
42
|
-
onIncrementPressStart(400);
|
|
43
|
-
};
|
|
44
|
-
const onPressRelease = () => {
|
|
45
|
-
isPressed.set(false);
|
|
46
|
-
resetTimeout();
|
|
47
|
-
};
|
|
48
|
-
effect(() => {
|
|
49
|
-
// Skip SSR environments
|
|
50
|
-
if (typeof window === 'undefined')
|
|
51
|
-
return;
|
|
52
|
-
const targetElement = options.target?.() || window;
|
|
53
|
-
const destroy$ = new Subject();
|
|
54
|
-
const pointerDownSub = fromEvent(targetElement, 'pointerdown')
|
|
55
|
-
.pipe(takeUntil(destroy$))
|
|
56
|
-
.subscribe((e) => onPressStart(e));
|
|
57
|
-
const pointerUpSub = fromEvent(window, 'pointerup').pipe(takeUntil(destroy$)).subscribe(onPressRelease);
|
|
58
|
-
const pointerCancelSub = fromEvent(window, 'pointercancel')
|
|
59
|
-
.pipe(takeUntil(destroy$))
|
|
60
|
-
.subscribe(onPressRelease);
|
|
61
|
-
this.destroyRef.onDestroy(() => {
|
|
62
|
-
destroy$.next();
|
|
63
|
-
destroy$.complete();
|
|
64
|
-
pointerDownSub.unsubscribe();
|
|
65
|
-
pointerUpSub.unsubscribe();
|
|
66
|
-
pointerCancelSub.unsubscribe();
|
|
67
|
-
});
|
|
68
|
-
});
|
|
69
|
-
return {
|
|
70
|
-
isPressed: isPressed.asReadonly(),
|
|
71
|
-
onTrigger: (fn) => {
|
|
72
|
-
const sub = triggerHook.subscribe(fn);
|
|
73
|
-
this.destroyRef.onDestroy(() => sub.unsubscribe());
|
|
74
|
-
}
|
|
75
|
-
};
|
|
18
|
+
/**
|
|
19
|
+
* Coerces an optional numeric input, returning `undefined` for nullish/empty/non-numeric values.
|
|
20
|
+
* Unlike `numberAttribute`, `numberOrUndefined(undefined)` is `undefined` (not `NaN`), so an unset
|
|
21
|
+
* `[min]`/`[max]` does not poison clamping.
|
|
22
|
+
*/
|
|
23
|
+
function numberOrUndefined(value) {
|
|
24
|
+
if (value == null || value === '') {
|
|
25
|
+
return undefined;
|
|
76
26
|
}
|
|
77
|
-
|
|
78
|
-
|
|
27
|
+
const parsed = numberAttribute(value);
|
|
28
|
+
return Number.isNaN(parsed) ? undefined : parsed;
|
|
79
29
|
}
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
30
|
+
/** Default step used when no `step` is provided. */
|
|
31
|
+
const DEFAULT_STEP = 1;
|
|
32
|
+
/** Delay between auto-repeat ticks while holding an increment/decrement button. */
|
|
33
|
+
const CHANGE_VALUE_TICK_DELAY = 60;
|
|
34
|
+
/** Delay before auto-repeat starts while holding an increment/decrement button. */
|
|
35
|
+
const START_AUTO_CHANGE_DELAY = 400;
|
|
36
|
+
/** Pointer travel (px) that cancels a hold, treating the gesture as a scroll. */
|
|
37
|
+
const SCROLLING_POINTER_MOVE_DISTANCE = 8;
|
|
38
|
+
const STEP_EPSILON_FACTOR = 1e-10;
|
|
39
|
+
// Matches Intl.NumberFormat's decimal maximumFractionDigits default.
|
|
40
|
+
const DEFAULT_DIGITS = 3;
|
|
41
|
+
/** Reactive `Intl`-backed formatter built from the current locale and format options. */
|
|
83
42
|
function useNumberFormatter(locale, options = signal({})) {
|
|
84
|
-
return computed(() => new NumberFormatter(locale(), options()));
|
|
43
|
+
return computed(() => new NumberFormatter(locale(), options() ?? {}));
|
|
85
44
|
}
|
|
45
|
+
/** Reactive `Intl`-backed parser built from the current locale and format options. */
|
|
86
46
|
function useNumberParser(locale, options = signal({})) {
|
|
87
|
-
return computed(() => new NumberParser(locale(), options()));
|
|
47
|
+
return computed(() => new NumberParser(locale(), options() ?? {}));
|
|
48
|
+
}
|
|
49
|
+
/** Whether the format options would cause `Intl.NumberFormat` to round the value. */
|
|
50
|
+
function hasNumberFormatRoundingOptions(format) {
|
|
51
|
+
const opts = format;
|
|
52
|
+
return !!(opts &&
|
|
53
|
+
(opts.maximumFractionDigits != null ||
|
|
54
|
+
opts.minimumFractionDigits != null ||
|
|
55
|
+
opts.maximumSignificantDigits != null ||
|
|
56
|
+
opts.minimumSignificantDigits != null ||
|
|
57
|
+
opts['roundingIncrement'] != null ||
|
|
58
|
+
opts['roundingMode'] != null ||
|
|
59
|
+
opts['roundingPriority'] != null));
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Strips floating-point representation errors (e.g. `0.1 + 0.2`) using the format's
|
|
63
|
+
* rounding options when present, otherwise rounding to {@link DEFAULT_DIGITS} digits.
|
|
64
|
+
*/
|
|
65
|
+
function removeFloatingPointErrors(value, locale, format) {
|
|
66
|
+
if (!Number.isFinite(value)) {
|
|
67
|
+
return value;
|
|
68
|
+
}
|
|
69
|
+
if (!hasNumberFormatRoundingOptions(format)) {
|
|
70
|
+
return Number(value.toFixed(DEFAULT_DIGITS));
|
|
71
|
+
}
|
|
72
|
+
const formatter = new NumberFormatter('en-US', {
|
|
73
|
+
...format,
|
|
74
|
+
// These options alter only display decoration, not numeric rounding.
|
|
75
|
+
signDisplay: 'auto',
|
|
76
|
+
currencySign: 'standard',
|
|
77
|
+
notation: format.notation === 'compact' ? 'standard' : format.notation,
|
|
78
|
+
useGrouping: false
|
|
79
|
+
});
|
|
80
|
+
const roundedText = formatter.format(value);
|
|
81
|
+
const roundedValue = new NumberParser('en-US', format ?? {}).parse(roundedText);
|
|
82
|
+
if (Number.isNaN(roundedValue)) {
|
|
83
|
+
return value;
|
|
84
|
+
}
|
|
85
|
+
return formatter.format(roundedValue) === roundedText ? roundedValue : value;
|
|
88
86
|
}
|
|
89
|
-
function
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
87
|
+
function snapToStep(value, base, step, mode = 'directional') {
|
|
88
|
+
const stepSize = Math.abs(step);
|
|
89
|
+
const direction = Math.sign(step);
|
|
90
|
+
const tolerance = stepSize * STEP_EPSILON_FACTOR * direction;
|
|
91
|
+
const rawSteps = value - base + tolerance;
|
|
92
|
+
if (mode === 'nearest') {
|
|
93
|
+
return base + Math.round(rawSteps / step) * step;
|
|
94
|
+
}
|
|
95
|
+
const snappedSteps = direction > 0 ? Math.floor(rawSteps / stepSize) : Math.ceil(rawSteps / stepSize);
|
|
96
|
+
return base + snappedSteps * stepSize;
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Snaps (optionally), clamps (optionally) and rounds an unvalidated value, mirroring
|
|
100
|
+
* Base UI's `toValidatedNumber`. Snapping happens before clamping so non-step-aligned
|
|
101
|
+
* boundaries stay reachable.
|
|
102
|
+
*/
|
|
103
|
+
function toValidatedNumber(value, options) {
|
|
104
|
+
if (value === null) {
|
|
105
|
+
return value;
|
|
106
|
+
}
|
|
107
|
+
const { step, minWithDefault, maxWithDefault, minWithZeroDefault, format, locale, snapOnStep, small } = options;
|
|
108
|
+
let nextValue = value;
|
|
109
|
+
if (step != null && snapOnStep && step !== 0) {
|
|
110
|
+
const base = small || minWithDefault === Number.MIN_SAFE_INTEGER ? minWithZeroDefault : minWithDefault;
|
|
111
|
+
nextValue = snapToStep(nextValue, base, step, small ? 'nearest' : 'directional');
|
|
112
|
+
}
|
|
113
|
+
if (options.clamp) {
|
|
114
|
+
nextValue = clamp(nextValue, minWithDefault, maxWithDefault);
|
|
115
|
+
}
|
|
116
|
+
const roundedValue = removeFloatingPointErrors(nextValue, locale, format);
|
|
117
|
+
return options.clamp ? clamp(roundedValue, minWithDefault, maxWithDefault) : roundedValue;
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Pointer press-and-hold with auto-repeat, modelled on Base UI's `usePressAndHold`.
|
|
121
|
+
* Must be called in an injection context — it registers cleanup via `DestroyRef`.
|
|
122
|
+
*/
|
|
123
|
+
function createPressAndHold(options) {
|
|
124
|
+
const destroyRef = inject(DestroyRef);
|
|
125
|
+
const isPressing = signal(false, ...(ngDevMode ? [{ debugName: "isPressing" }] : /* istanbul ignore next */ []));
|
|
126
|
+
let startEvent = null;
|
|
127
|
+
let startX = 0;
|
|
128
|
+
let startY = 0;
|
|
129
|
+
let didTick = false;
|
|
130
|
+
let tickTimer;
|
|
131
|
+
const clearTick = () => {
|
|
132
|
+
if (tickTimer !== undefined) {
|
|
133
|
+
clearTimeout(tickTimer);
|
|
134
|
+
tickTimer = undefined;
|
|
135
|
+
}
|
|
136
|
+
};
|
|
137
|
+
const scheduleTick = (delay) => {
|
|
138
|
+
tickTimer = setTimeout(() => {
|
|
139
|
+
if (options.disabled() || !startEvent) {
|
|
140
|
+
stop(startEvent);
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
didTick = true;
|
|
144
|
+
const shouldContinue = options.tick(startEvent);
|
|
145
|
+
if (shouldContinue === false) {
|
|
146
|
+
clearTick();
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
scheduleTick(options.tickDelay);
|
|
150
|
+
}, delay);
|
|
151
|
+
};
|
|
152
|
+
const onPointerMove = (event) => {
|
|
153
|
+
// Treat a moving touch/pen gesture as a scroll and cancel the hold.
|
|
154
|
+
if (startEvent && startEvent.pointerType !== 'mouse') {
|
|
155
|
+
const dx = event.clientX - startX;
|
|
156
|
+
const dy = event.clientY - startY;
|
|
157
|
+
if (Math.hypot(dx, dy) > options.scrollDistance) {
|
|
158
|
+
stop(event);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
};
|
|
162
|
+
const stop = (event) => {
|
|
163
|
+
if (!isPressing()) {
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
clearTick();
|
|
167
|
+
window.removeEventListener('pointerup', stop, true);
|
|
168
|
+
window.removeEventListener('pointercancel', stop, true);
|
|
169
|
+
window.removeEventListener('pointermove', onPointerMove, true);
|
|
170
|
+
isPressing.set(false);
|
|
171
|
+
if (event) {
|
|
172
|
+
options.onStop?.(event);
|
|
173
|
+
}
|
|
174
|
+
};
|
|
175
|
+
destroyRef.onDestroy(() => stop(null));
|
|
176
|
+
return {
|
|
177
|
+
isPressing: isPressing.asReadonly(),
|
|
178
|
+
shouldSkipClick: () => didTick,
|
|
179
|
+
onPointerDown(event) {
|
|
180
|
+
if (options.disabled() || isPressing()) {
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
183
|
+
startEvent = event;
|
|
184
|
+
startX = event.clientX;
|
|
185
|
+
startY = event.clientY;
|
|
186
|
+
didTick = false;
|
|
187
|
+
isPressing.set(true);
|
|
188
|
+
window.addEventListener('pointerup', stop, true);
|
|
189
|
+
window.addEventListener('pointercancel', stop, true);
|
|
190
|
+
window.addEventListener('pointermove', onPointerMove, true);
|
|
191
|
+
scheduleTick(options.startDelay);
|
|
192
|
+
}
|
|
193
|
+
};
|
|
107
194
|
}
|
|
108
195
|
|
|
109
|
-
|
|
196
|
+
// Treat pen as touch-like to avoid forcing the software keyboard on stylus taps.
|
|
197
|
+
function isTouchLikePointerType(pointerType) {
|
|
198
|
+
return pointerType === 'touch' || pointerType === 'pen';
|
|
199
|
+
}
|
|
200
|
+
/**
|
|
201
|
+
* Shared behaviour for the increment and decrement stepper buttons: single-press,
|
|
202
|
+
* press-and-hold auto-repeat, and committing a dirty (typed-but-not-blurred) input
|
|
203
|
+
* value before stepping.
|
|
204
|
+
*
|
|
205
|
+
* @see https://base-ui.com/react/components/number-field
|
|
206
|
+
*/
|
|
207
|
+
class RdxNumberFieldButton {
|
|
208
|
+
get direction() {
|
|
209
|
+
return this.isIncrement ? 1 : -1;
|
|
210
|
+
}
|
|
211
|
+
get pressReason() {
|
|
212
|
+
return this.isIncrement ? 'increment-press' : 'decrement-press';
|
|
213
|
+
}
|
|
110
214
|
constructor() {
|
|
111
|
-
this.elementRef = inject((ElementRef));
|
|
112
|
-
this.pressedHold = inject(PressedHoldService);
|
|
113
215
|
this.rootContext = injectNumberFieldRootContext();
|
|
114
|
-
this.disabled = input(false, { ...(ngDevMode ? { debugName: "disabled" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
115
216
|
/**
|
|
116
|
-
*
|
|
217
|
+
* When `true`, the button is disabled in addition to inheriting the root's disabled state.
|
|
218
|
+
* @default false
|
|
117
219
|
*/
|
|
118
|
-
this.
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
this.rootContext.
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
disabled: this.
|
|
220
|
+
this.disabled = input(false, { ...(ngDevMode ? { debugName: "disabled" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
221
|
+
/** @ignore Disabled for display/focus purposes (own state, root disabled, or bound reached). */
|
|
222
|
+
this.buttonDisabled = computed(() => this.disabled() ||
|
|
223
|
+
this.rootContext.isDisabled() ||
|
|
224
|
+
(this.isIncrement ? this.rootContext.isIncrementDisabled() : this.rootContext.isDecrementDisabled()), ...(ngDevMode ? [{ debugName: "buttonDisabled" }] : /* istanbul ignore next */ []));
|
|
225
|
+
/** @ignore Disabled for interaction purposes (also blocked while read-only). */
|
|
226
|
+
this.interactionDisabled = computed(() => this.buttonDisabled() || this.rootContext.readonly(), ...(ngDevMode ? [{ debugName: "interactionDisabled" }] : /* istanbul ignore next */ []));
|
|
227
|
+
const root = this.rootContext;
|
|
228
|
+
this.press = createPressAndHold({
|
|
229
|
+
disabled: () => this.interactionDisabled(),
|
|
230
|
+
startDelay: START_AUTO_CHANGE_DELAY,
|
|
231
|
+
tickDelay: CHANGE_VALUE_TICK_DELAY,
|
|
232
|
+
scrollDistance: SCROLLING_POINTER_MOVE_DISTANCE,
|
|
233
|
+
tick: (event) => {
|
|
234
|
+
const amount = root.getStepAmount(event);
|
|
235
|
+
return root.incrementValue(amount, {
|
|
236
|
+
direction: this.direction,
|
|
237
|
+
event,
|
|
238
|
+
reason: this.pressReason
|
|
239
|
+
});
|
|
240
|
+
},
|
|
241
|
+
onStop: () => root.commitValue(root.lastChangedValue ?? root.currentValue())
|
|
242
|
+
});
|
|
243
|
+
}
|
|
244
|
+
onClick(event) {
|
|
245
|
+
if (event.defaultPrevented || this.interactionDisabled() || this.press.shouldSkipClick()) {
|
|
246
|
+
return;
|
|
247
|
+
}
|
|
248
|
+
this.commitDirtyValue(event);
|
|
249
|
+
const amount = this.rootContext.getStepAmount(event);
|
|
250
|
+
const prev = this.rootContext.currentValue();
|
|
251
|
+
this.rootContext.incrementValue(amount, {
|
|
252
|
+
direction: this.direction,
|
|
253
|
+
event,
|
|
254
|
+
reason: this.pressReason
|
|
128
255
|
});
|
|
256
|
+
const committed = this.rootContext.lastChangedValue ?? this.rootContext.currentValue();
|
|
257
|
+
if (committed !== prev) {
|
|
258
|
+
this.rootContext.commitValue(committed);
|
|
259
|
+
}
|
|
129
260
|
}
|
|
130
|
-
|
|
131
|
-
|
|
261
|
+
onPointerDown(event) {
|
|
262
|
+
const isMainButton = !event.button || event.button === 0;
|
|
263
|
+
if (event.defaultPrevented || this.rootContext.readonly() || !isMainButton || this.buttonDisabled()) {
|
|
264
|
+
return;
|
|
265
|
+
}
|
|
266
|
+
// Sync the dirty input value before starting the hold sequence.
|
|
267
|
+
this.commitDirtyValue(event);
|
|
268
|
+
if (!isTouchLikePointerType(event.pointerType)) {
|
|
269
|
+
// Focus the input so the user can continue with the keyboard.
|
|
270
|
+
this.rootContext.inputEl()?.focus();
|
|
271
|
+
}
|
|
272
|
+
this.press.onPointerDown(event);
|
|
132
273
|
}
|
|
133
|
-
|
|
134
|
-
|
|
274
|
+
/** Commits a typed-but-not-yet-blurred input value so stepping starts from it. */
|
|
275
|
+
commitDirtyValue(event) {
|
|
276
|
+
const root = this.rootContext;
|
|
277
|
+
root.allowInputSync = true;
|
|
278
|
+
const parsed = root.parseNumber(root.inputValue());
|
|
279
|
+
if (parsed !== null) {
|
|
280
|
+
root.setValue(parsed, this.pressReason, event, this.direction);
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxNumberFieldButton, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
284
|
+
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.2.9", type: RdxNumberFieldButton, isStandalone: true, inputs: { disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null } }, host: { attributes: { "type": "button", "tabindex": "-1" }, listeners: { "click": "onClick($event)", "pointerdown": "onPointerDown($event)", "contextmenu": "$event.preventDefault()" }, properties: { "attr.aria-controls": "rootContext.id()", "attr.aria-readonly": "rootContext.readonly() ? \"true\" : undefined", "attr.disabled": "buttonDisabled() ? \"\" : undefined", "attr.data-disabled": "buttonDisabled() ? \"\" : undefined", "attr.data-pressed": "press.isPressing() ? \"\" : undefined", "style.user-select": "\"none\"", "style.-webkit-user-select": "\"none\"" } }, ngImport: i0 }); }
|
|
135
285
|
}
|
|
136
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type:
|
|
286
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxNumberFieldButton, decorators: [{
|
|
137
287
|
type: Directive,
|
|
138
288
|
args: [{
|
|
139
|
-
selector: 'button[rdxNumberFieldDecrement]',
|
|
140
|
-
providers: [PressedHoldService],
|
|
141
289
|
host: {
|
|
290
|
+
type: 'button',
|
|
142
291
|
tabindex: '-1',
|
|
143
|
-
|
|
144
|
-
'[attr.aria-
|
|
145
|
-
'[attr.disabled]': '
|
|
146
|
-
'[attr.data-disabled]': '
|
|
147
|
-
'[attr.data-pressed]': '
|
|
148
|
-
'[style.user-select]': '
|
|
292
|
+
'[attr.aria-controls]': 'rootContext.id()',
|
|
293
|
+
'[attr.aria-readonly]': 'rootContext.readonly() ? "true" : undefined',
|
|
294
|
+
'[attr.disabled]': 'buttonDisabled() ? "" : undefined',
|
|
295
|
+
'[attr.data-disabled]': 'buttonDisabled() ? "" : undefined',
|
|
296
|
+
'[attr.data-pressed]': 'press.isPressing() ? "" : undefined',
|
|
297
|
+
'[style.user-select]': '"none"',
|
|
298
|
+
'[style.-webkit-user-select]': '"none"',
|
|
299
|
+
'(click)': 'onClick($event)',
|
|
300
|
+
'(pointerdown)': 'onPointerDown($event)',
|
|
149
301
|
'(contextmenu)': '$event.preventDefault()'
|
|
150
302
|
}
|
|
151
303
|
}]
|
|
152
|
-
}], propDecorators: { disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }] } });
|
|
304
|
+
}], ctorParameters: () => [], propDecorators: { disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }] } });
|
|
153
305
|
|
|
154
|
-
|
|
306
|
+
/**
|
|
307
|
+
* A stepper button that decreases the field value when clicked or held.
|
|
308
|
+
*
|
|
309
|
+
* @see https://base-ui.com/react/components/number-field
|
|
310
|
+
*/
|
|
311
|
+
class RdxNumberFieldDecrement extends RdxNumberFieldButton {
|
|
312
|
+
constructor() {
|
|
313
|
+
super(...arguments);
|
|
314
|
+
this.isIncrement = false;
|
|
315
|
+
}
|
|
316
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxNumberFieldDecrement, deps: null, target: i0.ɵɵFactoryTarget.Directive }); }
|
|
317
|
+
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.2.9", type: RdxNumberFieldDecrement, isStandalone: true, selector: "button[rdxNumberFieldDecrement]", host: { attributes: { "aria-label": "Decrease" } }, exportAs: ["rdxNumberFieldDecrement"], usesInheritance: true, ngImport: i0 }); }
|
|
318
|
+
}
|
|
319
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxNumberFieldDecrement, decorators: [{
|
|
320
|
+
type: Directive,
|
|
321
|
+
args: [{
|
|
322
|
+
selector: 'button[rdxNumberFieldDecrement]',
|
|
323
|
+
exportAs: 'rdxNumberFieldDecrement',
|
|
324
|
+
host: {
|
|
325
|
+
'aria-label': 'Decrease'
|
|
326
|
+
}
|
|
327
|
+
}]
|
|
328
|
+
}] });
|
|
329
|
+
|
|
330
|
+
/**
|
|
331
|
+
* Groups the input with the increment and decrement buttons.
|
|
332
|
+
*
|
|
333
|
+
* @see https://base-ui.com/react/components/number-field
|
|
334
|
+
*/
|
|
335
|
+
class RdxNumberFieldGroup {
|
|
155
336
|
constructor() {
|
|
156
|
-
this.elementRef = inject((ElementRef));
|
|
157
|
-
this.pressedHold = inject(PressedHoldService);
|
|
158
337
|
this.rootContext = injectNumberFieldRootContext();
|
|
159
|
-
this.disabled = input(false, { ...(ngDevMode ? { debugName: "disabled" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
160
|
-
/**
|
|
161
|
-
* @ignore
|
|
162
|
-
*/
|
|
163
|
-
this.isDisabled = computed(() => this.rootContext.disabled() ||
|
|
164
|
-
this.rootContext.readonly() ||
|
|
165
|
-
this.disabled() ||
|
|
166
|
-
this.rootContext.isIncreaseDisabled(), ...(ngDevMode ? [{ debugName: "isDisabled" }] : /* istanbul ignore next */ []));
|
|
167
|
-
/**
|
|
168
|
-
* @ignore
|
|
169
|
-
*/
|
|
170
|
-
this.useHold = this.pressedHold.create({
|
|
171
|
-
target: signal(this.elementRef.nativeElement),
|
|
172
|
-
disabled: this.disabled
|
|
173
|
-
});
|
|
174
338
|
}
|
|
175
|
-
|
|
176
|
-
|
|
339
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxNumberFieldGroup, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
340
|
+
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.2.9", type: RdxNumberFieldGroup, isStandalone: true, selector: "div[rdxNumberFieldGroup]", host: { attributes: { "role": "group" }, properties: { "attr.data-disabled": "rootContext.isDisabled() ? \"\" : undefined", "attr.data-readonly": "rootContext.readonly() ? \"\" : undefined", "attr.data-required": "rootContext.required() ? \"\" : undefined", "attr.data-scrubbing": "rootContext.isScrubbing() ? \"\" : undefined" } }, exportAs: ["rdxNumberFieldGroup"], ngImport: i0 }); }
|
|
341
|
+
}
|
|
342
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxNumberFieldGroup, decorators: [{
|
|
343
|
+
type: Directive,
|
|
344
|
+
args: [{
|
|
345
|
+
selector: 'div[rdxNumberFieldGroup]',
|
|
346
|
+
exportAs: 'rdxNumberFieldGroup',
|
|
347
|
+
host: {
|
|
348
|
+
role: 'group',
|
|
349
|
+
'[attr.data-disabled]': 'rootContext.isDisabled() ? "" : undefined',
|
|
350
|
+
'[attr.data-readonly]': 'rootContext.readonly() ? "" : undefined',
|
|
351
|
+
'[attr.data-required]': 'rootContext.required() ? "" : undefined',
|
|
352
|
+
'[attr.data-scrubbing]': 'rootContext.isScrubbing() ? "" : undefined'
|
|
353
|
+
}
|
|
354
|
+
}]
|
|
355
|
+
}] });
|
|
356
|
+
|
|
357
|
+
/**
|
|
358
|
+
* The hidden native `input[type=number]` that mirrors the field value for native form submission
|
|
359
|
+
* and browser constraint validation (min/max/step/required). Place it inside the root, alongside
|
|
360
|
+
* the visible `[rdxNumberFieldInput]`.
|
|
361
|
+
*
|
|
362
|
+
* @see https://base-ui.com/react/components/number-field
|
|
363
|
+
*/
|
|
364
|
+
class RdxNumberFieldHiddenInput {
|
|
365
|
+
constructor() {
|
|
366
|
+
this.rootContext = injectNumberFieldRootContext();
|
|
177
367
|
}
|
|
178
|
-
|
|
179
|
-
|
|
368
|
+
/** Move focus to the visible input when the hidden one is focused (e.g. via form validation). */
|
|
369
|
+
onFocus() {
|
|
370
|
+
this.rootContext.inputEl()?.focus();
|
|
371
|
+
}
|
|
372
|
+
/** Handle browser autofill, which writes directly to the hidden numeric input. */
|
|
373
|
+
onChange(event) {
|
|
374
|
+
const root = this.rootContext;
|
|
375
|
+
if (root.isDisabled() || root.readonly()) {
|
|
376
|
+
return;
|
|
377
|
+
}
|
|
378
|
+
const nextValue = event.target.valueAsNumber;
|
|
379
|
+
const parsedValue = Number.isNaN(nextValue) ? null : nextValue;
|
|
380
|
+
root.allowInputSync = true;
|
|
381
|
+
root.setValue(parsedValue, 'none', event);
|
|
382
|
+
root.commitValue(root.lastChangedValue ?? root.currentValue());
|
|
383
|
+
}
|
|
384
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxNumberFieldHiddenInput, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
385
|
+
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.2.9", type: RdxNumberFieldHiddenInput, isStandalone: true, selector: "input[rdxNumberFieldHiddenInput]", host: { attributes: { "type": "number", "tabindex": "-1", "aria-hidden": "true" }, listeners: { "focus": "onFocus()", "change": "onChange($event)" }, properties: { "attr.name": "rootContext.name()", "attr.form": "rootContext.form()", "value": "rootContext.currentValue() ?? \"\"", "attr.min": "rootContext.min()", "attr.max": "rootContext.max()", "attr.step": "rootContext.step()", "disabled": "rootContext.isDisabled()", "attr.required": "rootContext.required() ? \"\" : undefined" }, styleAttribute: "transform: translateX(-100%); position: absolute; overflow: hidden; pointer-events: none; opacity: 0; margin: 0;" }, exportAs: ["rdxNumberFieldHiddenInput"], ngImport: i0 }); }
|
|
180
386
|
}
|
|
181
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type:
|
|
387
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxNumberFieldHiddenInput, decorators: [{
|
|
182
388
|
type: Directive,
|
|
183
389
|
args: [{
|
|
184
|
-
selector: '
|
|
185
|
-
|
|
390
|
+
selector: 'input[rdxNumberFieldHiddenInput]',
|
|
391
|
+
exportAs: 'rdxNumberFieldHiddenInput',
|
|
186
392
|
host: {
|
|
393
|
+
type: 'number',
|
|
187
394
|
tabindex: '-1',
|
|
188
|
-
|
|
189
|
-
'[attr.
|
|
190
|
-
'[attr.
|
|
191
|
-
'[
|
|
192
|
-
'[attr.
|
|
193
|
-
'[
|
|
194
|
-
'
|
|
395
|
+
'aria-hidden': 'true',
|
|
396
|
+
'[attr.name]': 'rootContext.name()',
|
|
397
|
+
'[attr.form]': 'rootContext.form()',
|
|
398
|
+
'[value]': 'rootContext.currentValue() ?? ""',
|
|
399
|
+
'[attr.min]': 'rootContext.min()',
|
|
400
|
+
'[attr.max]': 'rootContext.max()',
|
|
401
|
+
'[attr.step]': 'rootContext.step()',
|
|
402
|
+
'[disabled]': 'rootContext.isDisabled()',
|
|
403
|
+
'[attr.required]': 'rootContext.required() ? "" : undefined',
|
|
404
|
+
style: 'transform: translateX(-100%); position: absolute; overflow: hidden; pointer-events: none; opacity: 0; margin: 0;',
|
|
405
|
+
'(focus)': 'onFocus()',
|
|
406
|
+
'(change)': 'onChange($event)'
|
|
195
407
|
}
|
|
196
408
|
}]
|
|
197
|
-
}]
|
|
409
|
+
}] });
|
|
198
410
|
|
|
199
|
-
|
|
411
|
+
/**
|
|
412
|
+
* A stepper button that increases the field value when clicked or held.
|
|
413
|
+
*
|
|
414
|
+
* @see https://base-ui.com/react/components/number-field
|
|
415
|
+
*/
|
|
416
|
+
class RdxNumberFieldIncrement extends RdxNumberFieldButton {
|
|
200
417
|
constructor() {
|
|
201
|
-
|
|
418
|
+
super(...arguments);
|
|
419
|
+
this.isIncrement = true;
|
|
420
|
+
}
|
|
421
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxNumberFieldIncrement, deps: null, target: i0.ɵɵFactoryTarget.Directive }); }
|
|
422
|
+
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.2.9", type: RdxNumberFieldIncrement, isStandalone: true, selector: "button[rdxNumberFieldIncrement]", host: { attributes: { "aria-label": "Increase" } }, exportAs: ["rdxNumberFieldIncrement"], usesInheritance: true, ngImport: i0 }); }
|
|
423
|
+
}
|
|
424
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxNumberFieldIncrement, decorators: [{
|
|
425
|
+
type: Directive,
|
|
426
|
+
args: [{
|
|
427
|
+
selector: 'button[rdxNumberFieldIncrement]',
|
|
428
|
+
exportAs: 'rdxNumberFieldIncrement',
|
|
429
|
+
host: {
|
|
430
|
+
'aria-label': 'Increase'
|
|
431
|
+
}
|
|
432
|
+
}]
|
|
433
|
+
}] });
|
|
434
|
+
|
|
435
|
+
/**
|
|
436
|
+
* The native text input that displays the formatted value and accepts typed input.
|
|
437
|
+
*
|
|
438
|
+
* @see https://base-ui.com/react/components/number-field
|
|
439
|
+
*/
|
|
440
|
+
class RdxNumberFieldInput {
|
|
441
|
+
constructor() {
|
|
442
|
+
this.elementRef = inject(ElementRef);
|
|
202
443
|
this.rootContext = injectNumberFieldRootContext();
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
this.rootContext.setInputValue(this.inputValue());
|
|
207
|
-
});
|
|
444
|
+
/** Browsers place the caret at the start; we move it to the end on the first focus. */
|
|
445
|
+
this.hasTouchedInput = false;
|
|
446
|
+
this.rootContext.registerInput(this.elementRef.nativeElement);
|
|
208
447
|
}
|
|
209
|
-
|
|
210
|
-
this.rootContext
|
|
448
|
+
onFocus(event) {
|
|
449
|
+
const root = this.rootContext;
|
|
450
|
+
if (root.readonly() || root.isDisabled() || this.hasTouchedInput) {
|
|
451
|
+
return;
|
|
452
|
+
}
|
|
453
|
+
this.hasTouchedInput = true;
|
|
454
|
+
const target = event.target;
|
|
455
|
+
const length = target.value.length;
|
|
456
|
+
target.setSelectionRange(length, length);
|
|
211
457
|
}
|
|
212
458
|
onBeforeInput(event) {
|
|
213
|
-
|
|
214
|
-
|
|
459
|
+
// Only validate insertions; deletions and composition have `data === null`.
|
|
460
|
+
if (event.data == null) {
|
|
461
|
+
return;
|
|
462
|
+
}
|
|
463
|
+
const target = event.target;
|
|
215
464
|
const nextValue = target.value.slice(0, target.selectionStart ?? undefined) +
|
|
216
|
-
|
|
465
|
+
event.data +
|
|
217
466
|
target.value.slice(target.selectionEnd ?? undefined);
|
|
218
|
-
if (!this.rootContext.
|
|
219
|
-
|
|
467
|
+
if (!this.rootContext.isValidPartial(nextValue)) {
|
|
468
|
+
event.preventDefault();
|
|
220
469
|
}
|
|
221
470
|
}
|
|
222
|
-
|
|
223
|
-
|
|
471
|
+
onInput(event) {
|
|
472
|
+
const root = this.rootContext;
|
|
473
|
+
const targetValue = event.target.value;
|
|
474
|
+
root.allowInputSync = false;
|
|
475
|
+
if (targetValue.trim() === '') {
|
|
476
|
+
root.setInputValue(targetValue);
|
|
477
|
+
root.setValue(null, 'input-clear', event);
|
|
224
478
|
return;
|
|
225
479
|
}
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
480
|
+
const parsedValue = root.parseNumber(targetValue);
|
|
481
|
+
root.setInputValue(targetValue);
|
|
482
|
+
if (parsedValue !== null) {
|
|
483
|
+
root.setValue(parsedValue, 'input-change', event);
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
onKeydown(event) {
|
|
487
|
+
const root = this.rootContext;
|
|
488
|
+
if (root.readonly() || root.isDisabled()) {
|
|
234
489
|
return;
|
|
235
490
|
}
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
491
|
+
root.allowInputSync = true;
|
|
492
|
+
const key = event.key;
|
|
493
|
+
if (key === 'ArrowUp' || key === 'ArrowDown') {
|
|
494
|
+
event.preventDefault();
|
|
495
|
+
const parsed = root.parseNumber(root.inputValue());
|
|
496
|
+
const amount = root.getStepAmount(event);
|
|
497
|
+
root.incrementValue(amount, {
|
|
498
|
+
direction: key === 'ArrowUp' ? 1 : -1,
|
|
499
|
+
currentValue: parsed,
|
|
500
|
+
event,
|
|
501
|
+
reason: 'keyboard'
|
|
502
|
+
});
|
|
503
|
+
root.commitValue(root.lastChangedValue ?? root.currentValue());
|
|
239
504
|
}
|
|
240
|
-
else if (
|
|
241
|
-
|
|
505
|
+
else if (key === 'Home' && root.min() != null) {
|
|
506
|
+
event.preventDefault();
|
|
507
|
+
root.setValue(root.min(), 'keyboard', event);
|
|
508
|
+
root.commitValue(root.lastChangedValue ?? root.currentValue());
|
|
509
|
+
}
|
|
510
|
+
else if (key === 'End' && root.max() != null) {
|
|
511
|
+
event.preventDefault();
|
|
512
|
+
root.setValue(root.max(), 'keyboard', event);
|
|
513
|
+
root.commitValue(root.lastChangedValue ?? root.currentValue());
|
|
242
514
|
}
|
|
243
515
|
}
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
event.
|
|
250
|
-
|
|
251
|
-
}
|
|
252
|
-
onKeydownHome(event) {
|
|
253
|
-
event.preventDefault();
|
|
254
|
-
this.rootContext.handleMinMaxValue('min');
|
|
255
|
-
}
|
|
256
|
-
onKeydownEnd(event) {
|
|
516
|
+
onPaste(event) {
|
|
517
|
+
const root = this.rootContext;
|
|
518
|
+
if (root.readonly() || root.isDisabled()) {
|
|
519
|
+
return;
|
|
520
|
+
}
|
|
521
|
+
const pastedData = event.clipboardData?.getData('text/plain') ?? '';
|
|
522
|
+
// Prevent the subsequent `input` event from also handling the paste.
|
|
257
523
|
event.preventDefault();
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
onChange() {
|
|
265
|
-
this.inputValue.set(this.rootContext.textValue());
|
|
266
|
-
}
|
|
267
|
-
onKeydownEnter(event) {
|
|
268
|
-
const target = event.target;
|
|
269
|
-
this.rootContext.applyInputValue(target.value);
|
|
524
|
+
const parsedValue = root.parseNumber(pastedData);
|
|
525
|
+
if (parsedValue !== null) {
|
|
526
|
+
root.allowInputSync = false;
|
|
527
|
+
root.setValue(parsedValue, 'input-paste', event);
|
|
528
|
+
root.setInputValue(pastedData);
|
|
529
|
+
}
|
|
270
530
|
}
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
531
|
+
onBlur(event) {
|
|
532
|
+
const root = this.rootContext;
|
|
533
|
+
if (root.readonly() || root.isDisabled()) {
|
|
534
|
+
return;
|
|
535
|
+
}
|
|
536
|
+
root.markAsTouched();
|
|
537
|
+
const hadManualInput = !root.allowInputSync;
|
|
538
|
+
const hadPendingCommit = root.hasPendingCommit;
|
|
539
|
+
root.allowInputSync = true;
|
|
540
|
+
const text = root.inputValue();
|
|
541
|
+
if (text.trim() === '') {
|
|
542
|
+
root.setValue(null, 'input-clear', event);
|
|
543
|
+
root.commitValue(null);
|
|
544
|
+
return;
|
|
545
|
+
}
|
|
546
|
+
const parsedValue = root.parseNumber(text);
|
|
547
|
+
if (parsedValue === null) {
|
|
548
|
+
return;
|
|
549
|
+
}
|
|
550
|
+
const value = root.currentValue();
|
|
551
|
+
const shouldUpdate = value !== parsedValue;
|
|
552
|
+
const shouldCommit = hadManualInput || shouldUpdate || hadPendingCommit;
|
|
553
|
+
let committedValue = parsedValue;
|
|
554
|
+
if (shouldUpdate) {
|
|
555
|
+
root.setValue(parsedValue, 'input-blur', event);
|
|
556
|
+
committedValue = root.lastChangedValue ?? parsedValue;
|
|
557
|
+
}
|
|
558
|
+
if (shouldCommit) {
|
|
559
|
+
root.commitValue(committedValue);
|
|
560
|
+
}
|
|
561
|
+
const canonicalText = root.formatNumber(committedValue);
|
|
562
|
+
if (root.inputValue() !== canonicalText) {
|
|
563
|
+
root.setInputValue(canonicalText);
|
|
564
|
+
}
|
|
274
565
|
}
|
|
275
|
-
|
|
566
|
+
onWheel(event) {
|
|
567
|
+
const root = this.rootContext;
|
|
568
|
+
if (!root.allowWheelScrub() || root.isDisabled() || root.readonly()) {
|
|
569
|
+
return;
|
|
570
|
+
}
|
|
571
|
+
// Allow pinch-zoom and only scrub while focused.
|
|
572
|
+
if (event.ctrlKey || getActiveElement() !== this.elementRef.nativeElement) {
|
|
573
|
+
return;
|
|
574
|
+
}
|
|
276
575
|
event.preventDefault();
|
|
277
|
-
|
|
576
|
+
root.allowInputSync = true;
|
|
577
|
+
const amount = root.getStepAmount(event);
|
|
578
|
+
root.incrementValue(amount, {
|
|
579
|
+
direction: event.deltaY > 0 ? -1 : 1,
|
|
580
|
+
event,
|
|
581
|
+
reason: 'wheel'
|
|
582
|
+
});
|
|
278
583
|
}
|
|
279
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type:
|
|
280
|
-
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.2.9", type:
|
|
584
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxNumberFieldInput, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
585
|
+
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.2.9", type: RdxNumberFieldInput, isStandalone: true, selector: "input[rdxNumberFieldInput]", host: { attributes: { "type": "text", "autocomplete": "off", "autocorrect": "off", "spellcheck": "false", "aria-roledescription": "Number field" }, listeners: { "focus": "onFocus($event)", "blur": "onBlur($event)", "input": "onInput($event)", "beforeinput": "onBeforeInput($event)", "keydown": "onKeydown($event)", "paste": "onPaste($event)", "wheel": "onWheel($event)" }, properties: { "id": "rootContext.id()", "value": "rootContext.inputValue()", "attr.inputmode": "rootContext.inputMode()", "attr.aria-invalid": "rootContext.required() && rootContext.currentValue() === null ? \"true\" : undefined", "disabled": "rootContext.isDisabled()", "attr.readonly": "rootContext.readonly() ? \"\" : undefined", "required": "rootContext.required()", "attr.data-disabled": "rootContext.isDisabled() ? \"\" : undefined", "attr.data-readonly": "rootContext.readonly() ? \"\" : undefined", "attr.data-required": "rootContext.required() ? \"\" : undefined" } }, exportAs: ["rdxNumberFieldInput"], ngImport: i0 }); }
|
|
281
586
|
}
|
|
282
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type:
|
|
587
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxNumberFieldInput, decorators: [{
|
|
283
588
|
type: Directive,
|
|
284
589
|
args: [{
|
|
285
590
|
selector: 'input[rdxNumberFieldInput]',
|
|
591
|
+
exportAs: 'rdxNumberFieldInput',
|
|
286
592
|
host: {
|
|
287
|
-
role: 'spinbutton',
|
|
288
593
|
type: 'text',
|
|
289
|
-
tabindex: '0',
|
|
290
594
|
autocomplete: 'off',
|
|
291
595
|
autocorrect: 'off',
|
|
292
596
|
spellcheck: 'false',
|
|
293
597
|
'aria-roledescription': 'Number field',
|
|
294
|
-
'[
|
|
295
|
-
'[
|
|
296
|
-
'[attr.aria-valuemax]': 'rootContext.max()',
|
|
598
|
+
'[id]': 'rootContext.id()',
|
|
599
|
+
'[value]': 'rootContext.inputValue()',
|
|
297
600
|
'[attr.inputmode]': 'rootContext.inputMode()',
|
|
298
|
-
'[attr.
|
|
299
|
-
'[
|
|
601
|
+
'[attr.aria-invalid]': 'rootContext.required() && rootContext.currentValue() === null ? "true" : undefined',
|
|
602
|
+
'[disabled]': 'rootContext.isDisabled()',
|
|
300
603
|
'[attr.readonly]': 'rootContext.readonly() ? "" : undefined',
|
|
604
|
+
'[required]': 'rootContext.required()',
|
|
605
|
+
'[attr.data-disabled]': 'rootContext.isDisabled() ? "" : undefined',
|
|
301
606
|
'[attr.data-readonly]': 'rootContext.readonly() ? "" : undefined',
|
|
302
|
-
'[attr.
|
|
303
|
-
'(
|
|
607
|
+
'[attr.data-required]': 'rootContext.required() ? "" : undefined',
|
|
608
|
+
'(focus)': 'onFocus($event)',
|
|
609
|
+
'(blur)': 'onBlur($event)',
|
|
304
610
|
'(input)': 'onInput($event)',
|
|
305
|
-
'(blur)': 'onKeydownEnter($event)',
|
|
306
611
|
'(beforeinput)': 'onBeforeInput($event)',
|
|
307
|
-
'(keydown
|
|
308
|
-
'(
|
|
309
|
-
'(
|
|
310
|
-
'(keydown.home)': 'onKeydownHome($event)',
|
|
311
|
-
'(keydown.end)': 'onKeydownEnd($event)',
|
|
312
|
-
'(keydown.pageUp)': 'onKeydownPageUp($event)',
|
|
313
|
-
'(keydown.pageDown)': 'onKeydownPageDown($event)',
|
|
314
|
-
'(wheel)': 'onWheelEvent($event)'
|
|
612
|
+
'(keydown)': 'onKeydown($event)',
|
|
613
|
+
'(paste)': 'onPaste($event)',
|
|
614
|
+
'(wheel)': 'onWheel($event)'
|
|
315
615
|
}
|
|
316
616
|
}]
|
|
317
617
|
}], ctorParameters: () => [] });
|
|
318
618
|
|
|
319
|
-
|
|
619
|
+
const REASONS = {
|
|
620
|
+
inputChange: 'input-change',
|
|
621
|
+
inputClear: 'input-clear',
|
|
622
|
+
inputBlur: 'input-blur',
|
|
623
|
+
inputPaste: 'input-paste',
|
|
624
|
+
keyboard: 'keyboard',
|
|
625
|
+
incrementPress: 'increment-press',
|
|
626
|
+
decrementPress: 'decrement-press',
|
|
627
|
+
wheel: 'wheel',
|
|
628
|
+
scrub: 'scrub',
|
|
629
|
+
none: 'none'
|
|
630
|
+
};
|
|
631
|
+
|
|
632
|
+
const INPUT_REASONS = [
|
|
633
|
+
REASONS.inputChange,
|
|
634
|
+
REASONS.inputClear,
|
|
635
|
+
REASONS.inputBlur,
|
|
636
|
+
REASONS.inputPaste,
|
|
637
|
+
REASONS.none
|
|
638
|
+
];
|
|
639
|
+
/**
|
|
640
|
+
* Groups all parts of the number field and manages its state, parsing/formatting
|
|
641
|
+
* and value-change logic. A single directive drives the whole family — parts read
|
|
642
|
+
* signals and call methods off it through the root context.
|
|
643
|
+
*
|
|
644
|
+
* @see https://base-ui.com/react/components/number-field
|
|
645
|
+
*/
|
|
646
|
+
class RdxNumberFieldRoot {
|
|
320
647
|
constructor() {
|
|
321
|
-
|
|
322
|
-
this.
|
|
323
|
-
|
|
648
|
+
/** @ignore */
|
|
649
|
+
this.cva = injectControlValueAccessor();
|
|
650
|
+
/** The id of the input element. */
|
|
651
|
+
this.id = input(injectId('rdx-number-field-'), ...(ngDevMode ? [{ debugName: "id" }] : /* istanbul ignore next */ []));
|
|
652
|
+
/** The minimum value of the field. */
|
|
653
|
+
this.min = input(undefined, { ...(ngDevMode ? { debugName: "min" } : /* istanbul ignore next */ {}), transform: numberOrUndefined });
|
|
654
|
+
/** The maximum value of the field. */
|
|
655
|
+
this.max = input(undefined, { ...(ngDevMode ? { debugName: "max" } : /* istanbul ignore next */ {}), transform: numberOrUndefined });
|
|
656
|
+
/**
|
|
657
|
+
* Amount to increment and decrement with the buttons, arrow keys and scrub area.
|
|
658
|
+
* @default 1
|
|
659
|
+
*/
|
|
324
660
|
this.step = input(1, { ...(ngDevMode ? { debugName: "step" } : /* istanbul ignore next */ {}), transform: numberAttribute });
|
|
325
|
-
this.stepSnapping = input(false, { ...(ngDevMode ? { debugName: "stepSnapping" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
326
|
-
this.disableWheelChange = input(false, { ...(ngDevMode ? { debugName: "disableWheelChange" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
327
|
-
this.locale = input('en', ...(ngDevMode ? [{ debugName: "locale" }] : /* istanbul ignore next */ []));
|
|
328
|
-
this.disabled = input(false, { ...(ngDevMode ? { debugName: "disabled" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
329
|
-
this.formatOptions = input(...(ngDevMode ? [undefined, { debugName: "formatOptions" }] : /* istanbul ignore next */ []));
|
|
330
661
|
/**
|
|
331
|
-
*
|
|
662
|
+
* The step used when incrementing while the Alt key is held. Snaps to multiples of this value.
|
|
663
|
+
* @default 0.1
|
|
332
664
|
*/
|
|
333
|
-
this.
|
|
665
|
+
this.smallStep = input(0.1, { ...(ngDevMode ? { debugName: "smallStep" } : /* istanbul ignore next */ {}), transform: numberAttribute });
|
|
334
666
|
/**
|
|
335
|
-
*
|
|
667
|
+
* The step used when incrementing while the Shift key is held. Snaps to multiples of this value.
|
|
668
|
+
* @default 10
|
|
336
669
|
*/
|
|
337
|
-
this.
|
|
670
|
+
this.largeStep = input(10, { ...(ngDevMode ? { debugName: "largeStep" } : /* istanbul ignore next */ {}), transform: numberAttribute });
|
|
338
671
|
/**
|
|
339
|
-
*
|
|
672
|
+
* Whether the value should snap to the nearest step when incrementing or decrementing.
|
|
673
|
+
* @default false
|
|
340
674
|
*/
|
|
341
|
-
this.
|
|
342
|
-
(this.clampInputValue(this.value()) === this.min() || (this.min() && !isNaN(this.value()))
|
|
343
|
-
? handleDecimalOperation('-', this.value(), this.step()) < this.min()
|
|
344
|
-
: false), ...(ngDevMode ? [{ debugName: "isDecreaseDisabled" }] : /* istanbul ignore next */ []));
|
|
675
|
+
this.snapOnStep = input(false, { ...(ngDevMode ? { debugName: "snapOnStep" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
345
676
|
/**
|
|
346
|
-
*
|
|
677
|
+
* When `true`, direct text entry may go outside the `min`/`max` range without clamping.
|
|
678
|
+
* Step interactions (arrow keys, buttons, wheel, scrub) still clamp.
|
|
679
|
+
* @default false
|
|
347
680
|
*/
|
|
348
|
-
this.
|
|
349
|
-
(this.clampInputValue(this.value()) === this.max() || (this.min() && !isNaN(this.value()))
|
|
350
|
-
? handleDecimalOperation('+', this.value(), this.step()) > this.max()
|
|
351
|
-
: false), ...(ngDevMode ? [{ debugName: "isIncreaseDisabled" }] : /* istanbul ignore next */ []));
|
|
681
|
+
this.allowOutOfRange = input(false, { ...(ngDevMode ? { debugName: "allowOutOfRange" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
352
682
|
/**
|
|
353
|
-
*
|
|
683
|
+
* Whether the value can be changed with the mouse wheel while the input is focused.
|
|
684
|
+
* @default false
|
|
354
685
|
*/
|
|
355
|
-
this.
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
const hasDecimals = this.numberFormatter().resolvedOptions().maximumFractionDigits > 0;
|
|
361
|
-
return hasDecimals ? 'decimal' : 'numeric';
|
|
362
|
-
}, ...(ngDevMode ? [{ debugName: "inputMode" }] : /* istanbul ignore next */ []));
|
|
686
|
+
this.allowWheelScrub = input(false, { ...(ngDevMode ? { debugName: "allowWheelScrub" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
687
|
+
/** Options used to format the input value (forwarded to `Intl.NumberFormat`). */
|
|
688
|
+
this.format = input(...(ngDevMode ? [undefined, { debugName: "format" }] : /* istanbul ignore next */ []));
|
|
689
|
+
/** The locale used to parse and format the value. */
|
|
690
|
+
this.locale = input('en', ...(ngDevMode ? [{ debugName: "locale" }] : /* istanbul ignore next */ []));
|
|
363
691
|
/**
|
|
364
|
-
*
|
|
365
|
-
*
|
|
366
|
-
* @ignore
|
|
692
|
+
* When `true`, the user cannot interact with the field.
|
|
693
|
+
* @default false
|
|
367
694
|
*/
|
|
368
|
-
this.
|
|
695
|
+
this.disabled = input(false, { ...(ngDevMode ? { debugName: "disabled" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
696
|
+
/**
|
|
697
|
+
* When `true`, the field is focusable but its value cannot be changed.
|
|
698
|
+
* @default false
|
|
699
|
+
*/
|
|
700
|
+
this.readonly = input(false, { ...(ngDevMode ? { debugName: "readonly" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
701
|
+
/**
|
|
702
|
+
* When `true`, the user must enter a value before the owning form can be submitted.
|
|
703
|
+
* @default false
|
|
704
|
+
*/
|
|
705
|
+
this.required = input(false, { ...(ngDevMode ? { debugName: "required" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
706
|
+
/** Name of the hidden input rendered by `[rdxNumberFieldHiddenInput]`, for form submission. */
|
|
707
|
+
this.name = input(...(ngDevMode ? [undefined, { debugName: "name" }] : /* istanbul ignore next */ []));
|
|
708
|
+
/** Id of the form the hidden input belongs to. Useful when it is rendered outside the form. */
|
|
709
|
+
this.form = input(...(ngDevMode ? [undefined, { debugName: "form" }] : /* istanbul ignore next */ []));
|
|
710
|
+
/** The uncontrolled value of the field when it is initially rendered. */
|
|
711
|
+
this.defaultValue = input(...(ngDevMode ? [undefined, { debugName: "defaultValue" }] : /* istanbul ignore next */ []));
|
|
712
|
+
/** The controlled value of the field. Use with `(onValueChange)` or two-way `[(value)]`. */
|
|
713
|
+
this.value = model(null, ...(ngDevMode ? [{ debugName: "value" }] : /* istanbul ignore next */ []));
|
|
714
|
+
/** Emitted when the value changes (during interaction or programmatically). */
|
|
715
|
+
this.onValueChange = output();
|
|
369
716
|
/**
|
|
370
|
-
*
|
|
717
|
+
* Emitted when the value is committed: on blur after typing, or when a pointer is released
|
|
718
|
+
* after scrubbing or pressing a button. Fires together with `onValueChange` for keyboard input.
|
|
371
719
|
*/
|
|
372
|
-
this.
|
|
720
|
+
this.onValueCommitted = output();
|
|
721
|
+
/** @ignore The formatted text shown in the input element. */
|
|
722
|
+
this.inputValue = signal('', ...(ngDevMode ? [{ debugName: "inputValue" }] : /* istanbul ignore next */ []));
|
|
723
|
+
/** @ignore Whether a scrub gesture is in progress. */
|
|
724
|
+
this.isScrubbing = signal(false, ...(ngDevMode ? [{ debugName: "isScrubbing" }] : /* istanbul ignore next */ []));
|
|
725
|
+
/** @ignore The native input element, registered by `[rdxNumberFieldInput]`. */
|
|
726
|
+
this.inputEl = signal(undefined, ...(ngDevMode ? [{ debugName: "inputEl" }] : /* istanbul ignore next */ []));
|
|
727
|
+
/**
|
|
728
|
+
* @ignore Gate that prevents the formatted value from overwriting in-progress typing.
|
|
729
|
+
* Plain field (not a signal): it is toggled imperatively inside event handlers.
|
|
730
|
+
*/
|
|
731
|
+
this.allowInputSync = true;
|
|
732
|
+
/** @ignore Last value produced by `setValue`, used to report the committed value. */
|
|
733
|
+
this.lastChangedValue = null;
|
|
734
|
+
/** @ignore Whether a programmatic change is awaiting a commit. */
|
|
735
|
+
this.hasPendingCommit = false;
|
|
736
|
+
/** @ignore */
|
|
737
|
+
this.isDisabled = computed(() => !!this.cva.disabled(), ...(ngDevMode ? [{ debugName: "isDisabled" }] : /* istanbul ignore next */ []));
|
|
738
|
+
this.formatter = useNumberFormatter(this.locale, this.format);
|
|
739
|
+
this.parser = useNumberParser(this.locale, this.format);
|
|
740
|
+
/** @ignore The current numeric value (`null` when empty). */
|
|
741
|
+
this.currentValue = computed(() => this.cva.value() ?? null, ...(ngDevMode ? [{ debugName: "currentValue" }] : /* istanbul ignore next */ []));
|
|
742
|
+
/** @ignore */
|
|
743
|
+
this.minWithDefault = computed(() => this.min() ?? Number.MIN_SAFE_INTEGER, ...(ngDevMode ? [{ debugName: "minWithDefault" }] : /* istanbul ignore next */ []));
|
|
744
|
+
/** @ignore */
|
|
745
|
+
this.maxWithDefault = computed(() => this.max() ?? Number.MAX_SAFE_INTEGER, ...(ngDevMode ? [{ debugName: "maxWithDefault" }] : /* istanbul ignore next */ []));
|
|
746
|
+
/** @ignore */
|
|
747
|
+
this.minWithZeroDefault = computed(() => this.min() ?? 0, ...(ngDevMode ? [{ debugName: "minWithZeroDefault" }] : /* istanbul ignore next */ []));
|
|
748
|
+
/** @ignore Whether incrementing further is a no-op (value already at max). */
|
|
749
|
+
this.isIncrementDisabled = computed(() => {
|
|
750
|
+
const value = this.currentValue();
|
|
751
|
+
return value != null && value >= this.maxWithDefault();
|
|
752
|
+
}, ...(ngDevMode ? [{ debugName: "isIncrementDisabled" }] : /* istanbul ignore next */ []));
|
|
753
|
+
/** @ignore Whether decrementing further is a no-op (value already at min). */
|
|
754
|
+
this.isDecrementDisabled = computed(() => {
|
|
755
|
+
const value = this.currentValue();
|
|
756
|
+
return value != null && value <= this.minWithDefault();
|
|
757
|
+
}, ...(ngDevMode ? [{ debugName: "isDecrementDisabled" }] : /* istanbul ignore next */ []));
|
|
758
|
+
/** @ignore Software-keyboard hint based on whether the format allows fractional digits. */
|
|
759
|
+
this.inputMode = computed(() => {
|
|
760
|
+
const hasDecimals = (this.formatter().resolvedOptions().maximumFractionDigits ?? 0) > 0;
|
|
761
|
+
return hasDecimals ? 'decimal' : 'numeric';
|
|
762
|
+
}, ...(ngDevMode ? [{ debugName: "inputMode" }] : /* istanbul ignore next */ []));
|
|
763
|
+
// Apply the uncontrolled initial value once it is provided.
|
|
764
|
+
effect(() => {
|
|
765
|
+
const dv = this.defaultValue();
|
|
766
|
+
if (dv !== undefined) {
|
|
767
|
+
untracked(() => {
|
|
768
|
+
this.value.set(dv);
|
|
769
|
+
this.cva.setValue(dv);
|
|
770
|
+
});
|
|
771
|
+
}
|
|
772
|
+
});
|
|
773
|
+
// Keep the visible input in sync with the value whenever it changes externally or the
|
|
774
|
+
// formatting options change. Skipped while the user is typing (`allowInputSync === false`).
|
|
775
|
+
effect(() => {
|
|
776
|
+
const formatted = this.formatNumber(this.currentValue());
|
|
777
|
+
untracked(() => {
|
|
778
|
+
if (this.allowInputSync && this.inputValue() !== formatted) {
|
|
779
|
+
this.inputValue.set(formatted);
|
|
780
|
+
}
|
|
781
|
+
});
|
|
782
|
+
});
|
|
373
783
|
}
|
|
374
|
-
/**
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
// Clamp to min and max, round to the nearest step, and round to the specified number of digits
|
|
379
|
-
let clampedValue;
|
|
380
|
-
if (this.step() === undefined || isNaN(this.step()) || !this.stepSnapping()) {
|
|
381
|
-
clampedValue = clamp(val, this.min(), this.max());
|
|
382
|
-
}
|
|
383
|
-
else {
|
|
384
|
-
clampedValue = snapValueToStep(val, this.min(), this.max(), this.step());
|
|
784
|
+
/** @ignore Formats a numeric value to its display string (empty for `null`/`NaN`). */
|
|
785
|
+
formatNumber(value) {
|
|
786
|
+
if (value === null || Number.isNaN(value)) {
|
|
787
|
+
return '';
|
|
385
788
|
}
|
|
386
|
-
|
|
387
|
-
return clampedValue;
|
|
789
|
+
return this.formatter().format(value);
|
|
388
790
|
}
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
791
|
+
/** @ignore Parses a display string to a number, returning `null` when not parseable. */
|
|
792
|
+
parseNumber(text) {
|
|
793
|
+
const parsed = this.parser().parse(text);
|
|
794
|
+
return Number.isNaN(parsed) ? null : parsed;
|
|
393
795
|
}
|
|
394
|
-
/**
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
796
|
+
/** @ignore Whether `text` is a valid partial number for the current locale and bounds. */
|
|
797
|
+
isValidPartial(text) {
|
|
798
|
+
return this.parser().isValidPartialNumber(text, this.min(), this.max());
|
|
799
|
+
}
|
|
800
|
+
/** @ignore The step magnitude for an interaction, honouring Alt (small) and Shift (large). */
|
|
801
|
+
getStepAmount(event) {
|
|
802
|
+
if (event?.altKey) {
|
|
803
|
+
return this.smallStep();
|
|
400
804
|
}
|
|
401
|
-
|
|
402
|
-
this.
|
|
805
|
+
if (event?.shiftKey) {
|
|
806
|
+
return this.largeStep();
|
|
403
807
|
}
|
|
808
|
+
return this.step();
|
|
404
809
|
}
|
|
405
|
-
/**
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
handleDecrease(multiplier = 1) {
|
|
409
|
-
this.handleChangingValue('decrease', multiplier);
|
|
810
|
+
/** @ignore Registers the native input element. */
|
|
811
|
+
registerInput(el) {
|
|
812
|
+
this.inputEl.set(el);
|
|
410
813
|
}
|
|
411
|
-
/**
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
814
|
+
/** @ignore Sets the displayed text without changing the numeric value. */
|
|
815
|
+
setInputValue(text) {
|
|
816
|
+
this.inputValue.set(text);
|
|
817
|
+
}
|
|
818
|
+
/** @ignore */
|
|
819
|
+
markAsTouched() {
|
|
820
|
+
this.cva.markAsTouched();
|
|
416
821
|
}
|
|
417
822
|
/**
|
|
418
823
|
* @ignore
|
|
824
|
+
* Validates and applies a candidate value, emitting `onValueChange` when it changes.
|
|
825
|
+
* Returns whether a change was fired.
|
|
419
826
|
*/
|
|
420
|
-
|
|
421
|
-
const
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
827
|
+
setValue(unvalidatedValue, reason, event, direction) {
|
|
828
|
+
const keyState = event;
|
|
829
|
+
// Step interactions always clamp; direct text entry may go out of range when allowed.
|
|
830
|
+
const shouldClamp = !this.allowOutOfRange() || !INPUT_REASONS.includes(reason);
|
|
831
|
+
const validatedValue = toValidatedNumber(unvalidatedValue, {
|
|
832
|
+
step: direction ? this.getStepAmount(keyState) * direction : undefined,
|
|
833
|
+
minWithDefault: this.minWithDefault(),
|
|
834
|
+
maxWithDefault: this.maxWithDefault(),
|
|
835
|
+
minWithZeroDefault: this.minWithZeroDefault(),
|
|
836
|
+
format: this.format(),
|
|
837
|
+
locale: this.locale(),
|
|
838
|
+
snapOnStep: this.snapOnStep(),
|
|
839
|
+
small: keyState?.altKey ?? false,
|
|
840
|
+
clamp: shouldClamp
|
|
841
|
+
});
|
|
842
|
+
const value = this.currentValue();
|
|
843
|
+
const isInputReason = INPUT_REASONS.includes(reason);
|
|
844
|
+
const shouldFireChange = validatedValue !== value ||
|
|
845
|
+
(isInputReason && (unvalidatedValue !== value || this.allowInputSync === false));
|
|
846
|
+
if (shouldFireChange) {
|
|
847
|
+
this.applyValue(validatedValue);
|
|
848
|
+
this.onValueChange.emit(validatedValue);
|
|
849
|
+
this.hasPendingCommit = true;
|
|
426
850
|
}
|
|
427
|
-
|
|
428
|
-
if (
|
|
429
|
-
|
|
851
|
+
this.lastChangedValue = validatedValue;
|
|
852
|
+
if (this.allowInputSync) {
|
|
853
|
+
this.setInputValue(this.formatNumber(validatedValue));
|
|
430
854
|
}
|
|
431
|
-
return
|
|
855
|
+
return shouldFireChange;
|
|
432
856
|
}
|
|
433
857
|
/**
|
|
434
858
|
* @ignore
|
|
859
|
+
* Increments (or decrements) the value by `amount * direction`, starting from `currentValue`
|
|
860
|
+
* when supplied (the live, possibly-dirty input value) or the committed value otherwise.
|
|
435
861
|
*/
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
el.value = val;
|
|
441
|
-
return el;
|
|
442
|
-
});
|
|
443
|
-
}
|
|
862
|
+
incrementValue(amount, params) {
|
|
863
|
+
const prev = params.currentValue == null ? this.currentValue() : params.currentValue;
|
|
864
|
+
const nextValue = typeof prev === 'number' ? prev + amount * params.direction : Math.max(0, this.min() ?? 0);
|
|
865
|
+
return this.setValue(nextValue, params.reason, params.event, params.direction);
|
|
444
866
|
}
|
|
445
|
-
/**
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
return this.numberParser().isValidPartialNumber(val, this.min(), this.max());
|
|
867
|
+
/** @ignore Emits the committed value at the end of an interaction. */
|
|
868
|
+
commitValue(value) {
|
|
869
|
+
this.hasPendingCommit = false;
|
|
870
|
+
this.onValueCommitted.emit(value);
|
|
450
871
|
}
|
|
451
|
-
|
|
452
|
-
this.
|
|
453
|
-
|
|
872
|
+
applyValue(value) {
|
|
873
|
+
this.value.set(value);
|
|
874
|
+
this.cva.setValue(value);
|
|
875
|
+
}
|
|
876
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxNumberFieldRoot, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
877
|
+
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.2.9", type: RdxNumberFieldRoot, isStandalone: true, selector: "div[rdxNumberFieldRoot]", inputs: { id: { classPropertyName: "id", publicName: "id", isSignal: true, isRequired: false, transformFunction: null }, min: { classPropertyName: "min", publicName: "min", isSignal: true, isRequired: false, transformFunction: null }, max: { classPropertyName: "max", publicName: "max", isSignal: true, isRequired: false, transformFunction: null }, step: { classPropertyName: "step", publicName: "step", isSignal: true, isRequired: false, transformFunction: null }, smallStep: { classPropertyName: "smallStep", publicName: "smallStep", isSignal: true, isRequired: false, transformFunction: null }, largeStep: { classPropertyName: "largeStep", publicName: "largeStep", isSignal: true, isRequired: false, transformFunction: null }, snapOnStep: { classPropertyName: "snapOnStep", publicName: "snapOnStep", isSignal: true, isRequired: false, transformFunction: null }, allowOutOfRange: { classPropertyName: "allowOutOfRange", publicName: "allowOutOfRange", isSignal: true, isRequired: false, transformFunction: null }, allowWheelScrub: { classPropertyName: "allowWheelScrub", publicName: "allowWheelScrub", isSignal: true, isRequired: false, transformFunction: null }, format: { classPropertyName: "format", publicName: "format", isSignal: true, isRequired: false, transformFunction: null }, locale: { classPropertyName: "locale", publicName: "locale", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, readonly: { classPropertyName: "readonly", publicName: "readonly", isSignal: true, isRequired: false, transformFunction: null }, required: { classPropertyName: "required", publicName: "required", isSignal: true, isRequired: false, transformFunction: null }, name: { classPropertyName: "name", publicName: "name", isSignal: true, isRequired: false, transformFunction: null }, form: { classPropertyName: "form", publicName: "form", isSignal: true, isRequired: false, transformFunction: null }, defaultValue: { classPropertyName: "defaultValue", publicName: "defaultValue", isSignal: true, isRequired: false, transformFunction: null }, value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { value: "valueChange", onValueChange: "onValueChange", onValueCommitted: "onValueCommitted" }, host: { attributes: { "role": "group" }, properties: { "attr.data-disabled": "isDisabled() ? \"\" : undefined", "attr.data-readonly": "readonly() ? \"\" : undefined", "attr.data-required": "required() ? \"\" : undefined", "attr.data-scrubbing": "isScrubbing() ? \"\" : undefined" } }, providers: [provideNumberFieldRootContext(() => inject(RdxNumberFieldRoot))], exportAs: ["rdxNumberFieldRoot"], hostDirectives: [{ directive: i1.RdxControlValueAccessor, inputs: ["value", "value", "disabled", "disabled"] }], ngImport: i0 }); }
|
|
878
|
+
}
|
|
879
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxNumberFieldRoot, decorators: [{
|
|
880
|
+
type: Directive,
|
|
881
|
+
args: [{
|
|
882
|
+
selector: 'div[rdxNumberFieldRoot]',
|
|
883
|
+
exportAs: 'rdxNumberFieldRoot',
|
|
884
|
+
providers: [provideNumberFieldRootContext(() => inject(RdxNumberFieldRoot))],
|
|
885
|
+
hostDirectives: [
|
|
886
|
+
{
|
|
887
|
+
directive: RdxControlValueAccessor,
|
|
888
|
+
inputs: ['value: value', 'disabled']
|
|
889
|
+
}
|
|
890
|
+
],
|
|
891
|
+
host: {
|
|
892
|
+
role: 'group',
|
|
893
|
+
'[attr.data-disabled]': 'isDisabled() ? "" : undefined',
|
|
894
|
+
'[attr.data-readonly]': 'readonly() ? "" : undefined',
|
|
895
|
+
'[attr.data-required]': 'required() ? "" : undefined',
|
|
896
|
+
'[attr.data-scrubbing]': 'isScrubbing() ? "" : undefined'
|
|
897
|
+
}
|
|
898
|
+
}]
|
|
899
|
+
}], ctorParameters: () => [], propDecorators: { id: [{ type: i0.Input, args: [{ isSignal: true, alias: "id", required: false }] }], min: [{ type: i0.Input, args: [{ isSignal: true, alias: "min", required: false }] }], max: [{ type: i0.Input, args: [{ isSignal: true, alias: "max", required: false }] }], step: [{ type: i0.Input, args: [{ isSignal: true, alias: "step", required: false }] }], smallStep: [{ type: i0.Input, args: [{ isSignal: true, alias: "smallStep", required: false }] }], largeStep: [{ type: i0.Input, args: [{ isSignal: true, alias: "largeStep", required: false }] }], snapOnStep: [{ type: i0.Input, args: [{ isSignal: true, alias: "snapOnStep", required: false }] }], allowOutOfRange: [{ type: i0.Input, args: [{ isSignal: true, alias: "allowOutOfRange", required: false }] }], allowWheelScrub: [{ type: i0.Input, args: [{ isSignal: true, alias: "allowWheelScrub", required: false }] }], format: [{ type: i0.Input, args: [{ isSignal: true, alias: "format", required: false }] }], locale: [{ type: i0.Input, args: [{ isSignal: true, alias: "locale", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], readonly: [{ type: i0.Input, args: [{ isSignal: true, alias: "readonly", required: false }] }], required: [{ type: i0.Input, args: [{ isSignal: true, alias: "required", required: false }] }], name: [{ type: i0.Input, args: [{ isSignal: true, alias: "name", required: false }] }], form: [{ type: i0.Input, args: [{ isSignal: true, alias: "form", required: false }] }], defaultValue: [{ type: i0.Input, args: [{ isSignal: true, alias: "defaultValue", required: false }] }], value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }, { type: i0.Output, args: ["valueChange"] }], onValueChange: [{ type: i0.Output, args: ["onValueChange"] }], onValueCommitted: [{ type: i0.Output, args: ["onValueCommitted"] }] } });
|
|
900
|
+
|
|
901
|
+
/**
|
|
902
|
+
* Context shared from the scrub area to its virtual cursor child.
|
|
903
|
+
*
|
|
904
|
+
* @see https://base-ui.com/react/components/number-field
|
|
905
|
+
*/
|
|
906
|
+
const [injectNumberFieldScrubAreaContext, provideNumberFieldScrubAreaContext] = createContext('RdxNumberFieldScrubAreaContext');
|
|
907
|
+
|
|
908
|
+
function isWebKitBrowser$1() {
|
|
909
|
+
return (typeof navigator !== 'undefined' &&
|
|
910
|
+
/AppleWebKit/.test(navigator.userAgent) &&
|
|
911
|
+
!/Chrome/.test(navigator.userAgent));
|
|
912
|
+
}
|
|
913
|
+
function isFirefoxBrowser() {
|
|
914
|
+
return typeof navigator !== 'undefined' && /firefox/i.test(navigator.userAgent);
|
|
915
|
+
}
|
|
916
|
+
/** Calculates the viewport rect the virtual cursor loops within. */
|
|
917
|
+
function getViewportRect(teleportDistance, scrubAreaEl) {
|
|
918
|
+
const win = scrubAreaEl.ownerDocument.defaultView ?? window;
|
|
919
|
+
const rect = scrubAreaEl.getBoundingClientRect();
|
|
920
|
+
if (rect && teleportDistance != null) {
|
|
921
|
+
return {
|
|
922
|
+
x: rect.left - teleportDistance / 2,
|
|
923
|
+
y: rect.top - teleportDistance / 2,
|
|
924
|
+
width: rect.right + teleportDistance / 2,
|
|
925
|
+
height: rect.bottom + teleportDistance / 2
|
|
926
|
+
};
|
|
927
|
+
}
|
|
928
|
+
const vV = win.visualViewport;
|
|
929
|
+
if (vV) {
|
|
930
|
+
return { x: vV.offsetLeft, y: vV.offsetTop, width: vV.offsetLeft + vV.width, height: vV.offsetTop + vV.height };
|
|
931
|
+
}
|
|
932
|
+
return {
|
|
933
|
+
x: 0,
|
|
934
|
+
y: 0,
|
|
935
|
+
width: win.document.documentElement.clientWidth,
|
|
936
|
+
height: win.document.documentElement.clientHeight
|
|
937
|
+
};
|
|
938
|
+
}
|
|
939
|
+
/**
|
|
940
|
+
* An interactive area where the user can click and drag to change the field value.
|
|
941
|
+
* Uses the Pointer Lock API for continuous dragging (disabled in Safari to avoid layout shift).
|
|
942
|
+
*
|
|
943
|
+
* @see https://base-ui.com/react/components/number-field
|
|
944
|
+
*/
|
|
945
|
+
class RdxNumberFieldScrubArea {
|
|
946
|
+
constructor() {
|
|
947
|
+
this.rootContext = injectNumberFieldRootContext();
|
|
948
|
+
/**
|
|
949
|
+
* The direction the cursor must move to change the value.
|
|
950
|
+
* @default 'horizontal'
|
|
951
|
+
*/
|
|
952
|
+
this.direction = input('horizontal', ...(ngDevMode ? [{ debugName: "direction" }] : /* istanbul ignore next */ []));
|
|
953
|
+
/**
|
|
954
|
+
* How many pixels the cursor must move before the value changes. Higher is less sensitive.
|
|
955
|
+
* @default 2
|
|
956
|
+
*/
|
|
957
|
+
this.pixelSensitivity = input(2, { ...(ngDevMode ? { debugName: "pixelSensitivity" } : /* istanbul ignore next */ {}), transform: numberAttribute });
|
|
958
|
+
/**
|
|
959
|
+
* If set, the distance the cursor may move from the scrub area center before it loops back.
|
|
960
|
+
*/
|
|
961
|
+
this.teleportDistance = input(undefined, { ...(ngDevMode ? { debugName: "teleportDistance" } : /* istanbul ignore next */ {}), transform: numberOrUndefined });
|
|
962
|
+
this.isScrubbing = signal(false, ...(ngDevMode ? [{ debugName: "isScrubbing" }] : /* istanbul ignore next */ []));
|
|
963
|
+
this.isTouchInput = signal(false, ...(ngDevMode ? [{ debugName: "isTouchInput" }] : /* istanbul ignore next */ []));
|
|
964
|
+
this.isPointerLockDenied = signal(false, ...(ngDevMode ? [{ debugName: "isPointerLockDenied" }] : /* istanbul ignore next */ []));
|
|
965
|
+
this.cursorEl = signal(null, ...(ngDevMode ? [{ debugName: "cursorEl" }] : /* istanbul ignore next */ []));
|
|
966
|
+
this.scrubAreaEl = inject(ElementRef).nativeElement;
|
|
967
|
+
this.isScrubbingRef = false;
|
|
968
|
+
this.didMove = false;
|
|
969
|
+
this.pointerDownTarget = null;
|
|
970
|
+
this.virtualCursorCoords = { x: 0, y: 0 };
|
|
971
|
+
this.visualScale = 1;
|
|
972
|
+
/** @ignore Exposed to the scrub-area context provider. */
|
|
973
|
+
this.context = {
|
|
974
|
+
isScrubbing: this.isScrubbing.asReadonly(),
|
|
975
|
+
isTouchInput: this.isTouchInput.asReadonly(),
|
|
976
|
+
isPointerLockDenied: this.isPointerLockDenied.asReadonly(),
|
|
977
|
+
registerCursor: (el) => this.cursorEl.set(el)
|
|
978
|
+
};
|
|
979
|
+
this.canScrub = computed(() => !this.rootContext.isDisabled() && !this.rootContext.readonly(), ...(ngDevMode ? [{ debugName: "canScrub" }] : /* istanbul ignore next */ []));
|
|
980
|
+
const root = this.rootContext;
|
|
981
|
+
// Register global listeners only while actively scrubbing.
|
|
982
|
+
effect((onCleanup) => {
|
|
983
|
+
if (!root.inputEl() || !this.canScrub() || !this.isScrubbing()) {
|
|
984
|
+
return;
|
|
985
|
+
}
|
|
986
|
+
let cumulativeDelta = 0;
|
|
987
|
+
const handlePointerMove = (event) => {
|
|
988
|
+
if (!this.isScrubbingRef) {
|
|
989
|
+
return;
|
|
990
|
+
}
|
|
991
|
+
event.preventDefault();
|
|
992
|
+
this.onScrub(event);
|
|
993
|
+
const { movementX, movementY } = event;
|
|
994
|
+
cumulativeDelta += this.direction() === 'vertical' ? movementY : movementX;
|
|
995
|
+
if (Math.abs(cumulativeDelta) >= this.pixelSensitivity()) {
|
|
996
|
+
cumulativeDelta = 0;
|
|
997
|
+
this.didMove = true;
|
|
998
|
+
const dValue = this.direction() === 'vertical' ? -movementY : movementX;
|
|
999
|
+
const stepAmount = root.getStepAmount(event);
|
|
1000
|
+
const rawAmount = dValue * stepAmount;
|
|
1001
|
+
if (rawAmount !== 0) {
|
|
1002
|
+
root.allowInputSync = true;
|
|
1003
|
+
root.incrementValue(Math.abs(rawAmount), {
|
|
1004
|
+
direction: rawAmount >= 0 ? 1 : -1,
|
|
1005
|
+
event,
|
|
1006
|
+
reason: 'scrub'
|
|
1007
|
+
});
|
|
1008
|
+
}
|
|
1009
|
+
}
|
|
1010
|
+
};
|
|
1011
|
+
const handlePointerUp = (event) => {
|
|
1012
|
+
const finish = () => {
|
|
1013
|
+
try {
|
|
1014
|
+
this.scrubAreaEl.ownerDocument.exitPointerLock();
|
|
1015
|
+
}
|
|
1016
|
+
catch {
|
|
1017
|
+
// Ignore — pointer lock may not be active.
|
|
1018
|
+
}
|
|
1019
|
+
this.isScrubbingRef = false;
|
|
1020
|
+
this.onScrubbingChange(false, event);
|
|
1021
|
+
root.commitValue(root.lastChangedValue ?? root.currentValue());
|
|
1022
|
+
// Manually dispatch a click when no movement happened, since preventDefault on
|
|
1023
|
+
// pointerdown suppresses the browser's synthetic click.
|
|
1024
|
+
if (!this.didMove && this.pointerDownTarget && root.inputEl()) {
|
|
1025
|
+
this.pointerDownTarget.dispatchEvent(new MouseEvent('click', { bubbles: true, cancelable: true }));
|
|
1026
|
+
}
|
|
1027
|
+
this.didMove = false;
|
|
1028
|
+
this.pointerDownTarget = null;
|
|
1029
|
+
};
|
|
1030
|
+
if (isFirefoxBrowser()) {
|
|
1031
|
+
// Firefox needs a small delay or pointer lock won't release on a soft click.
|
|
1032
|
+
setTimeout(finish, 20);
|
|
1033
|
+
}
|
|
1034
|
+
else {
|
|
1035
|
+
finish();
|
|
1036
|
+
}
|
|
1037
|
+
};
|
|
1038
|
+
const win = this.scrubAreaEl.ownerDocument.defaultView ?? window;
|
|
1039
|
+
const vV = win.visualViewport;
|
|
1040
|
+
const handleVisualResize = () => {
|
|
1041
|
+
if (vV) {
|
|
1042
|
+
this.visualScale = vV.scale;
|
|
1043
|
+
}
|
|
1044
|
+
};
|
|
1045
|
+
handleVisualResize();
|
|
1046
|
+
win.addEventListener('pointermove', handlePointerMove, true);
|
|
1047
|
+
win.addEventListener('pointerup', handlePointerUp, true);
|
|
1048
|
+
win.addEventListener('pointercancel', handlePointerUp, true);
|
|
1049
|
+
vV?.addEventListener('resize', handleVisualResize);
|
|
1050
|
+
onCleanup(() => {
|
|
1051
|
+
win.removeEventListener('pointermove', handlePointerMove, true);
|
|
1052
|
+
win.removeEventListener('pointerup', handlePointerUp, true);
|
|
1053
|
+
win.removeEventListener('pointercancel', handlePointerUp, true);
|
|
1054
|
+
vV?.removeEventListener('resize', handleVisualResize);
|
|
1055
|
+
});
|
|
1056
|
+
});
|
|
1057
|
+
}
|
|
1058
|
+
async onPointerDown(event) {
|
|
1059
|
+
const isMainButton = !event.button || event.button === 0;
|
|
1060
|
+
if (event.defaultPrevented || !isMainButton || !this.canScrub()) {
|
|
454
1061
|
return;
|
|
455
1062
|
}
|
|
456
|
-
const
|
|
457
|
-
|
|
458
|
-
|
|
1063
|
+
const isTouch = event.pointerType === 'touch';
|
|
1064
|
+
this.isTouchInput.set(isTouch);
|
|
1065
|
+
if (event.pointerType === 'mouse') {
|
|
1066
|
+
event.preventDefault();
|
|
1067
|
+
this.rootContext.inputEl()?.focus();
|
|
459
1068
|
}
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
1069
|
+
this.isScrubbingRef = true;
|
|
1070
|
+
this.didMove = false;
|
|
1071
|
+
this.pointerDownTarget = event.target;
|
|
1072
|
+
this.onScrubbingChange(true, event);
|
|
1073
|
+
// WebKit causes significant layout shift with the native pointer-lock message.
|
|
1074
|
+
if (!isTouch && !isWebKitBrowser$1()) {
|
|
1075
|
+
try {
|
|
1076
|
+
await this.scrubAreaEl.ownerDocument.body.requestPointerLock();
|
|
1077
|
+
this.isPointerLockDenied.set(false);
|
|
463
1078
|
}
|
|
464
|
-
|
|
465
|
-
this.
|
|
1079
|
+
catch {
|
|
1080
|
+
this.isPointerLockDenied.set(true);
|
|
466
1081
|
}
|
|
1082
|
+
finally {
|
|
1083
|
+
if (this.isScrubbingRef) {
|
|
1084
|
+
this.onScrubbingChange(true, event);
|
|
1085
|
+
}
|
|
1086
|
+
}
|
|
1087
|
+
}
|
|
1088
|
+
}
|
|
1089
|
+
onTouchStart(event) {
|
|
1090
|
+
if (!this.canScrub()) {
|
|
1091
|
+
return;
|
|
1092
|
+
}
|
|
1093
|
+
// Prevent scrolling using touch input when scrubbing.
|
|
1094
|
+
if (event.touches.length === 1) {
|
|
1095
|
+
event.preventDefault();
|
|
1096
|
+
}
|
|
1097
|
+
}
|
|
1098
|
+
onScrub(event) {
|
|
1099
|
+
const cursor = this.cursorEl();
|
|
1100
|
+
if (!cursor) {
|
|
1101
|
+
return;
|
|
1102
|
+
}
|
|
1103
|
+
const rect = getViewportRect(this.teleportDistance(), this.scrubAreaEl);
|
|
1104
|
+
const coords = this.virtualCursorCoords;
|
|
1105
|
+
const newCoords = { x: Math.round(coords.x + event.movementX), y: Math.round(coords.y + event.movementY) };
|
|
1106
|
+
const cursorWidth = cursor.offsetWidth;
|
|
1107
|
+
const cursorHeight = cursor.offsetHeight;
|
|
1108
|
+
if (newCoords.x + cursorWidth / 2 < rect.x) {
|
|
1109
|
+
newCoords.x = rect.width - cursorWidth / 2;
|
|
1110
|
+
}
|
|
1111
|
+
else if (newCoords.x + cursorWidth / 2 > rect.width) {
|
|
1112
|
+
newCoords.x = rect.x - cursorWidth / 2;
|
|
1113
|
+
}
|
|
1114
|
+
if (newCoords.y + cursorHeight / 2 < rect.y) {
|
|
1115
|
+
newCoords.y = rect.height - cursorHeight / 2;
|
|
1116
|
+
}
|
|
1117
|
+
else if (newCoords.y + cursorHeight / 2 > rect.height) {
|
|
1118
|
+
newCoords.y = rect.y - cursorHeight / 2;
|
|
1119
|
+
}
|
|
1120
|
+
this.virtualCursorCoords = newCoords;
|
|
1121
|
+
this.updateCursorTransform(newCoords.x, newCoords.y);
|
|
1122
|
+
}
|
|
1123
|
+
onScrubbingChange(scrubbing, event) {
|
|
1124
|
+
this.isScrubbing.set(scrubbing);
|
|
1125
|
+
this.rootContext.isScrubbing.set(scrubbing);
|
|
1126
|
+
const cursor = this.cursorEl();
|
|
1127
|
+
if (!cursor || !scrubbing) {
|
|
1128
|
+
return;
|
|
1129
|
+
}
|
|
1130
|
+
const initialCoords = {
|
|
1131
|
+
x: event.clientX - cursor.offsetWidth / 2,
|
|
1132
|
+
y: event.clientY - cursor.offsetHeight / 2
|
|
1133
|
+
};
|
|
1134
|
+
this.virtualCursorCoords = initialCoords;
|
|
1135
|
+
this.updateCursorTransform(initialCoords.x, initialCoords.y);
|
|
1136
|
+
}
|
|
1137
|
+
updateCursorTransform(x, y) {
|
|
1138
|
+
const cursor = this.cursorEl();
|
|
1139
|
+
if (cursor) {
|
|
1140
|
+
cursor.style.transform = `translate3d(${x}px,${y}px,0) scale(${1 / this.visualScale})`;
|
|
467
1141
|
}
|
|
468
1142
|
}
|
|
469
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type:
|
|
470
|
-
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.2.9", type:
|
|
1143
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxNumberFieldScrubArea, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
1144
|
+
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.2.9", type: RdxNumberFieldScrubArea, isStandalone: true, selector: "[rdxNumberFieldScrubArea]", inputs: { direction: { classPropertyName: "direction", publicName: "direction", isSignal: true, isRequired: false, transformFunction: null }, pixelSensitivity: { classPropertyName: "pixelSensitivity", publicName: "pixelSensitivity", isSignal: true, isRequired: false, transformFunction: null }, teleportDistance: { classPropertyName: "teleportDistance", publicName: "teleportDistance", isSignal: true, isRequired: false, transformFunction: null } }, host: { attributes: { "role": "presentation" }, listeners: { "pointerdown": "onPointerDown($event)", "touchstart": "onTouchStart($event)" }, properties: { "style.touch-action": "\"none\"", "style.user-select": "\"none\"", "style.-webkit-user-select": "\"none\"", "attr.data-disabled": "rootContext.isDisabled() ? \"\" : undefined", "attr.data-readonly": "rootContext.readonly() ? \"\" : undefined", "attr.data-scrubbing": "isScrubbing() ? \"\" : undefined" } }, providers: [provideNumberFieldScrubAreaContext(() => inject(RdxNumberFieldScrubArea).context)], exportAs: ["rdxNumberFieldScrubArea"], ngImport: i0 }); }
|
|
471
1145
|
}
|
|
472
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type:
|
|
1146
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxNumberFieldScrubArea, decorators: [{
|
|
473
1147
|
type: Directive,
|
|
474
1148
|
args: [{
|
|
475
|
-
selector: '[
|
|
476
|
-
exportAs: '
|
|
477
|
-
providers: [
|
|
1149
|
+
selector: '[rdxNumberFieldScrubArea]',
|
|
1150
|
+
exportAs: 'rdxNumberFieldScrubArea',
|
|
1151
|
+
providers: [provideNumberFieldScrubAreaContext(() => inject(RdxNumberFieldScrubArea).context)],
|
|
478
1152
|
host: {
|
|
479
|
-
role: '
|
|
480
|
-
'[
|
|
481
|
-
'[
|
|
1153
|
+
role: 'presentation',
|
|
1154
|
+
'[style.touch-action]': '"none"',
|
|
1155
|
+
'[style.user-select]': '"none"',
|
|
1156
|
+
'[style.-webkit-user-select]': '"none"',
|
|
1157
|
+
'[attr.data-disabled]': 'rootContext.isDisabled() ? "" : undefined',
|
|
1158
|
+
'[attr.data-readonly]': 'rootContext.readonly() ? "" : undefined',
|
|
1159
|
+
'[attr.data-scrubbing]': 'isScrubbing() ? "" : undefined',
|
|
1160
|
+
'(pointerdown)': 'onPointerDown($event)',
|
|
1161
|
+
'(touchstart)': 'onTouchStart($event)'
|
|
482
1162
|
}
|
|
483
1163
|
}]
|
|
484
|
-
}],
|
|
1164
|
+
}], ctorParameters: () => [], propDecorators: { direction: [{ type: i0.Input, args: [{ isSignal: true, alias: "direction", required: false }] }], pixelSensitivity: [{ type: i0.Input, args: [{ isSignal: true, alias: "pixelSensitivity", required: false }] }], teleportDistance: [{ type: i0.Input, args: [{ isSignal: true, alias: "teleportDistance", required: false }] }] } });
|
|
1165
|
+
|
|
1166
|
+
function isWebKitBrowser() {
|
|
1167
|
+
return (typeof navigator !== 'undefined' &&
|
|
1168
|
+
/AppleWebKit/.test(navigator.userAgent) &&
|
|
1169
|
+
!/Chrome/.test(navigator.userAgent));
|
|
1170
|
+
}
|
|
1171
|
+
/**
|
|
1172
|
+
* A custom element shown instead of the native cursor while scrubbing. It is portaled to the
|
|
1173
|
+
* document body and positioned with the Pointer Lock API. Hidden in Safari (which would shift
|
|
1174
|
+
* layout with the native pointer-lock notification) and for touch input.
|
|
1175
|
+
*
|
|
1176
|
+
* @see https://base-ui.com/react/components/number-field
|
|
1177
|
+
*/
|
|
1178
|
+
class RdxNumberFieldScrubAreaCursor {
|
|
1179
|
+
constructor() {
|
|
1180
|
+
this.elementRef = inject(ElementRef);
|
|
1181
|
+
this.scrubContext = injectNumberFieldScrubAreaContext();
|
|
1182
|
+
this.shouldRender = computed(() => this.scrubContext.isScrubbing() &&
|
|
1183
|
+
!isWebKitBrowser() &&
|
|
1184
|
+
!this.scrubContext.isTouchInput() &&
|
|
1185
|
+
!this.scrubContext.isPointerLockDenied(), ...(ngDevMode ? [{ debugName: "shouldRender" }] : /* istanbul ignore next */ []));
|
|
1186
|
+
this.scrubContext.registerCursor(this.elementRef.nativeElement);
|
|
1187
|
+
inject(DestroyRef).onDestroy(() => this.scrubContext.registerCursor(null));
|
|
1188
|
+
}
|
|
1189
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxNumberFieldScrubAreaCursor, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
1190
|
+
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.2.9", type: RdxNumberFieldScrubAreaCursor, isStandalone: true, selector: "[rdxNumberFieldScrubAreaCursor]", host: { attributes: { "role": "presentation" }, properties: { "style.position": "\"fixed\"", "style.top.px": "0", "style.left.px": "0", "style.pointer-events": "\"none\"", "style.opacity": "shouldRender() ? \"1\" : \"0\"" } }, exportAs: ["rdxNumberFieldScrubAreaCursor"], hostDirectives: [{ directive: i1$1.RdxPortal }], ngImport: i0 }); }
|
|
1191
|
+
}
|
|
1192
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxNumberFieldScrubAreaCursor, decorators: [{
|
|
1193
|
+
type: Directive,
|
|
1194
|
+
args: [{
|
|
1195
|
+
selector: '[rdxNumberFieldScrubAreaCursor]',
|
|
1196
|
+
exportAs: 'rdxNumberFieldScrubAreaCursor',
|
|
1197
|
+
hostDirectives: [RdxPortal],
|
|
1198
|
+
host: {
|
|
1199
|
+
role: 'presentation',
|
|
1200
|
+
'[style.position]': '"fixed"',
|
|
1201
|
+
'[style.top.px]': '0',
|
|
1202
|
+
'[style.left.px]': '0',
|
|
1203
|
+
'[style.pointer-events]': '"none"',
|
|
1204
|
+
'[style.opacity]': 'shouldRender() ? "1" : "0"'
|
|
1205
|
+
}
|
|
1206
|
+
}]
|
|
1207
|
+
}], ctorParameters: () => [] });
|
|
485
1208
|
|
|
486
1209
|
const _imports = [
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
1210
|
+
RdxNumberFieldRoot,
|
|
1211
|
+
RdxNumberFieldGroup,
|
|
1212
|
+
RdxNumberFieldInput,
|
|
1213
|
+
RdxNumberFieldHiddenInput,
|
|
1214
|
+
RdxNumberFieldIncrement,
|
|
1215
|
+
RdxNumberFieldDecrement,
|
|
1216
|
+
RdxNumberFieldScrubArea,
|
|
1217
|
+
RdxNumberFieldScrubAreaCursor
|
|
491
1218
|
];
|
|
492
1219
|
class RdxNumberFieldModule {
|
|
493
1220
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxNumberFieldModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
|
|
494
|
-
static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "21.2.9", ngImport: i0, type: RdxNumberFieldModule, imports: [
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
1221
|
+
static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "21.2.9", ngImport: i0, type: RdxNumberFieldModule, imports: [RdxNumberFieldRoot,
|
|
1222
|
+
RdxNumberFieldGroup,
|
|
1223
|
+
RdxNumberFieldInput,
|
|
1224
|
+
RdxNumberFieldHiddenInput,
|
|
1225
|
+
RdxNumberFieldIncrement,
|
|
1226
|
+
RdxNumberFieldDecrement,
|
|
1227
|
+
RdxNumberFieldScrubArea,
|
|
1228
|
+
RdxNumberFieldScrubAreaCursor], exports: [RdxNumberFieldRoot,
|
|
1229
|
+
RdxNumberFieldGroup,
|
|
1230
|
+
RdxNumberFieldInput,
|
|
1231
|
+
RdxNumberFieldHiddenInput,
|
|
1232
|
+
RdxNumberFieldIncrement,
|
|
1233
|
+
RdxNumberFieldDecrement,
|
|
1234
|
+
RdxNumberFieldScrubArea,
|
|
1235
|
+
RdxNumberFieldScrubAreaCursor] }); }
|
|
501
1236
|
static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxNumberFieldModule }); }
|
|
502
1237
|
}
|
|
503
1238
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxNumberFieldModule, decorators: [{
|
|
@@ -512,5 +1247,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
|
|
|
512
1247
|
* Generated bundle index. Do not edit.
|
|
513
1248
|
*/
|
|
514
1249
|
|
|
515
|
-
export {
|
|
1250
|
+
export { CHANGE_VALUE_TICK_DELAY, DEFAULT_STEP, REASONS, RdxNumberFieldButton, RdxNumberFieldDecrement, RdxNumberFieldGroup, RdxNumberFieldHiddenInput, RdxNumberFieldIncrement, RdxNumberFieldInput, RdxNumberFieldModule, RdxNumberFieldRoot, RdxNumberFieldScrubArea, RdxNumberFieldScrubAreaCursor, SCROLLING_POINTER_MOVE_DISTANCE, START_AUTO_CHANGE_DELAY, createPressAndHold, hasNumberFormatRoundingOptions, injectNumberFieldRootContext, injectNumberFieldScrubAreaContext, numberOrUndefined, provideNumberFieldRootContext, provideNumberFieldScrubAreaContext, removeFloatingPointErrors, toValidatedNumber, useNumberFormatter, useNumberParser };
|
|
516
1251
|
//# sourceMappingURL=radix-ng-primitives-number-field.mjs.map
|