@radix-ng/primitives 0.50.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/collection/README.md +1 -0
- package/fesm2022/radix-ng-primitives-accordion.mjs +134 -66
- package/fesm2022/radix-ng-primitives-accordion.mjs.map +1 -1
- package/fesm2022/radix-ng-primitives-alert-dialog.mjs +224 -132
- package/fesm2022/radix-ng-primitives-alert-dialog.mjs.map +1 -1
- package/fesm2022/radix-ng-primitives-arrow.mjs +26 -10
- package/fesm2022/radix-ng-primitives-arrow.mjs.map +1 -1
- package/fesm2022/radix-ng-primitives-aspect-ratio.mjs +6 -6
- package/fesm2022/radix-ng-primitives-aspect-ratio.mjs.map +1 -1
- package/fesm2022/radix-ng-primitives-avatar.mjs +68 -75
- 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 +104 -103
- package/fesm2022/radix-ng-primitives-calendar.mjs.map +1 -1
- package/fesm2022/radix-ng-primitives-checkbox.mjs +414 -80
- package/fesm2022/radix-ng-primitives-checkbox.mjs.map +1 -1
- package/fesm2022/radix-ng-primitives-collapsible.mjs +193 -92
- package/fesm2022/radix-ng-primitives-collapsible.mjs.map +1 -1
- package/fesm2022/radix-ng-primitives-collection.mjs +72 -0
- package/fesm2022/radix-ng-primitives-collection.mjs.map +1 -0
- package/fesm2022/radix-ng-primitives-config.mjs +5 -5
- package/fesm2022/radix-ng-primitives-config.mjs.map +1 -1
- package/fesm2022/radix-ng-primitives-context-menu.mjs +143 -427
- package/fesm2022/radix-ng-primitives-context-menu.mjs.map +1 -1
- package/fesm2022/radix-ng-primitives-core.mjs +757 -757
- package/fesm2022/radix-ng-primitives-core.mjs.map +1 -1
- package/fesm2022/radix-ng-primitives-cropper.mjs +55 -53
- package/fesm2022/radix-ng-primitives-cropper.mjs.map +1 -1
- package/fesm2022/radix-ng-primitives-date-field.mjs +93 -86
- package/fesm2022/radix-ng-primitives-date-field.mjs.map +1 -1
- package/fesm2022/radix-ng-primitives-dialog.mjs +658 -330
- package/fesm2022/radix-ng-primitives-dialog.mjs.map +1 -1
- package/fesm2022/radix-ng-primitives-dismissable-layer.mjs +98 -76
- 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 +20 -20
- 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-guards.mjs +3 -3
- package/fesm2022/radix-ng-primitives-focus-guards.mjs.map +1 -1
- package/fesm2022/radix-ng-primitives-focus-scope.mjs +29 -14
- 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 +11 -11
- package/fesm2022/radix-ng-primitives-label.mjs.map +1 -1
- package/fesm2022/radix-ng-primitives-menu.mjs +1484 -353
- 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 +1060 -1553
- package/fesm2022/radix-ng-primitives-navigation-menu.mjs.map +1 -1
- package/fesm2022/radix-ng-primitives-number-field.mjs +1102 -366
- package/fesm2022/radix-ng-primitives-number-field.mjs.map +1 -1
- package/fesm2022/radix-ng-primitives-pagination.mjs +51 -51
- package/fesm2022/radix-ng-primitives-pagination.mjs.map +1 -1
- package/fesm2022/radix-ng-primitives-popover.mjs +980 -995
- package/fesm2022/radix-ng-primitives-popover.mjs.map +1 -1
- package/fesm2022/radix-ng-primitives-popper.mjs +137 -82
- package/fesm2022/radix-ng-primitives-popper.mjs.map +1 -1
- package/fesm2022/radix-ng-primitives-portal.mjs +40 -16
- 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 +231 -92
- package/fesm2022/radix-ng-primitives-progress.mjs.map +1 -1
- package/fesm2022/radix-ng-primitives-radio.mjs +211 -70
- package/fesm2022/radix-ng-primitives-radio.mjs.map +1 -1
- package/fesm2022/radix-ng-primitives-roving-focus.mjs +127 -77
- package/fesm2022/radix-ng-primitives-roving-focus.mjs.map +1 -1
- package/fesm2022/radix-ng-primitives-select.mjs +791 -511
- package/fesm2022/radix-ng-primitives-select.mjs.map +1 -1
- package/fesm2022/radix-ng-primitives-separator.mjs +16 -45
- package/fesm2022/radix-ng-primitives-separator.mjs.map +1 -1
- package/fesm2022/radix-ng-primitives-slider.mjs +976 -720
- package/fesm2022/radix-ng-primitives-slider.mjs.map +1 -1
- package/fesm2022/radix-ng-primitives-stepper.mjs +69 -71
- package/fesm2022/radix-ng-primitives-stepper.mjs.map +1 -1
- package/fesm2022/radix-ng-primitives-switch.mjs +128 -124
- package/fesm2022/radix-ng-primitives-switch.mjs.map +1 -1
- package/fesm2022/radix-ng-primitives-tabs.mjs +388 -115
- package/fesm2022/radix-ng-primitives-tabs.mjs.map +1 -1
- package/fesm2022/radix-ng-primitives-time-field.mjs +111 -117
- package/fesm2022/radix-ng-primitives-time-field.mjs.map +1 -1
- package/fesm2022/radix-ng-primitives-toggle-group.mjs +122 -248
- package/fesm2022/radix-ng-primitives-toggle-group.mjs.map +1 -1
- package/fesm2022/radix-ng-primitives-toggle.mjs +99 -62
- package/fesm2022/radix-ng-primitives-toggle.mjs.map +1 -1
- package/fesm2022/radix-ng-primitives-toolbar.mjs +307 -94
- package/fesm2022/radix-ng-primitives-toolbar.mjs.map +1 -1
- package/fesm2022/radix-ng-primitives-tooltip.mjs +690 -1079
- package/fesm2022/radix-ng-primitives-tooltip.mjs.map +1 -1
- package/fesm2022/radix-ng-primitives-visually-hidden.mjs +46 -87
- package/fesm2022/radix-ng-primitives-visually-hidden.mjs.map +1 -1
- package/fesm2022/radix-ng-primitives.mjs.map +1 -1
- package/meter/README.md +3 -0
- package/navigation-menu/README.md +2 -1
- package/package.json +85 -63
- 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/{accordion/index.d.ts → types/radix-ng-primitives-accordion.d.ts} +102 -67
- package/types/radix-ng-primitives-alert-dialog.d.ts +114 -0
- package/{arrow/index.d.ts → types/radix-ng-primitives-arrow.d.ts} +1 -1
- package/{aspect-ratio/index.d.ts → types/radix-ng-primitives-aspect-ratio.d.ts} +1 -1
- package/{avatar/index.d.ts → types/radix-ng-primitives-avatar.d.ts} +7 -11
- package/types/radix-ng-primitives-button.d.ts +73 -0
- package/{calendar/index.d.ts → types/radix-ng-primitives-calendar.d.ts} +2 -3
- package/types/radix-ng-primitives-checkbox.d.ts +337 -0
- package/types/radix-ng-primitives-collapsible.d.ts +159 -0
- package/types/radix-ng-primitives-collection.d.ts +44 -0
- package/{config/index.d.ts → types/radix-ng-primitives-config.d.ts} +1 -1
- package/types/radix-ng-primitives-context-menu.d.ts +73 -0
- package/{core/index.d.ts → types/radix-ng-primitives-core.d.ts} +311 -236
- package/{cropper/index.d.ts → types/radix-ng-primitives-cropper.d.ts} +6 -5
- package/{date-field/index.d.ts → types/radix-ng-primitives-date-field.d.ts} +42 -27
- package/types/radix-ng-primitives-dialog.d.ts +323 -0
- package/{dismissable-layer/index.d.ts → types/radix-ng-primitives-dismissable-layer.d.ts} +15 -7
- package/types/radix-ng-primitives-drawer.d.ts +448 -0
- package/{editable/index.d.ts → 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/{focus-scope/index.d.ts → types/radix-ng-primitives-focus-scope.d.ts} +13 -5
- package/types/radix-ng-primitives-input.d.ts +87 -0
- package/{label/index.d.ts → types/radix-ng-primitives-label.d.ts} +0 -1
- package/types/radix-ng-primitives-menu.d.ts +612 -0
- package/types/radix-ng-primitives-menubar.d.ts +66 -0
- package/types/radix-ng-primitives-meter.d.ts +193 -0
- package/types/radix-ng-primitives-navigation-menu.d.ts +488 -0
- package/types/radix-ng-primitives-number-field.d.ts +464 -0
- package/{pagination/index.d.ts → types/radix-ng-primitives-pagination.d.ts} +2 -2
- package/types/radix-ng-primitives-popover.d.ts +416 -0
- package/{popper/index.d.ts → types/radix-ng-primitives-popper.d.ts} +50 -9
- package/types/radix-ng-primitives-portal.d.ts +30 -0
- package/types/radix-ng-primitives-presence.d.ts +55 -0
- package/types/radix-ng-primitives-preview-card.d.ts +359 -0
- package/types/radix-ng-primitives-progress.d.ts +206 -0
- package/{radio/index.d.ts → types/radix-ng-primitives-radio.d.ts} +56 -26
- package/{roving-focus/index.d.ts → types/radix-ng-primitives-roving-focus.d.ts} +38 -27
- package/types/radix-ng-primitives-select.d.ts +512 -0
- package/types/radix-ng-primitives-separator.d.ts +38 -0
- package/types/radix-ng-primitives-slider.d.ts +377 -0
- package/{stepper/index.d.ts → types/radix-ng-primitives-stepper.d.ts} +21 -22
- package/types/radix-ng-primitives-switch.d.ts +121 -0
- package/types/radix-ng-primitives-tabs.d.ts +247 -0
- package/{time-field/index.d.ts → types/radix-ng-primitives-time-field.d.ts} +46 -31
- package/types/radix-ng-primitives-toggle-group.d.ts +116 -0
- package/types/radix-ng-primitives-toggle.d.ts +65 -0
- package/types/radix-ng-primitives-toolbar.d.ts +180 -0
- package/types/radix-ng-primitives-tooltip.d.ts +395 -0
- package/{visually-hidden/index.d.ts → types/radix-ng-primitives-visually-hidden.d.ts} +19 -19
- package/alert-dialog/index.d.ts +0 -57
- package/checkbox/index.d.ts +0 -164
- package/collapsible/index.d.ts +0 -85
- package/context-menu/index.d.ts +0 -129
- package/dialog/index.d.ts +0 -205
- package/dropdown-menu/README.md +0 -1
- package/dropdown-menu/index.d.ts +0 -171
- package/fesm2022/radix-ng-primitives-dropdown-menu.mjs +0 -583
- package/fesm2022/radix-ng-primitives-dropdown-menu.mjs.map +0 -1
- package/fesm2022/radix-ng-primitives-hover-card.mjs +0 -1246
- package/fesm2022/radix-ng-primitives-hover-card.mjs.map +0 -1
- package/fesm2022/radix-ng-primitives-tooltip2.mjs +0 -740
- package/fesm2022/radix-ng-primitives-tooltip2.mjs.map +0 -1
- package/hover-card/README.md +0 -3
- package/hover-card/index.d.ts +0 -472
- package/menu/index.d.ts +0 -139
- package/menubar/index.d.ts +0 -56
- package/navigation-menu/index.d.ts +0 -405
- package/number-field/index.d.ts +0 -203
- package/popover/index.d.ts +0 -403
- package/portal/index.d.ts +0 -22
- package/presence/index.d.ts +0 -103
- package/progress/index.d.ts +0 -79
- package/select/index.d.ts +0 -214
- package/separator/index.d.ts +0 -63
- package/slider/index.d.ts +0 -263
- package/switch/index.d.ts +0 -105
- package/tabs/index.d.ts +0 -112
- package/toggle/index.d.ts +0 -75
- package/toggle-group/index.d.ts +0 -194
- package/toolbar/index.d.ts +0 -55
- package/tooltip/index.d.ts +0 -433
- package/tooltip2/README.md +0 -3
- package/tooltip2/index.d.ts +0 -325
- /package/{focus-guards/index.d.ts → types/radix-ng-primitives-focus-guards.d.ts} +0 -0
- /package/{index.d.ts → types/radix-ng-primitives.d.ts} +0 -0
|
@@ -1,505 +1,1241 @@
|
|
|
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;
|
|
86
|
+
}
|
|
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;
|
|
88
118
|
}
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
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", transform: booleanAttribute }] : [{ transform: booleanAttribute }]));
|
|
115
|
-
/**
|
|
116
|
-
* @ignore
|
|
117
|
-
*/
|
|
118
|
-
this.isDisabled = computed(() => this.rootContext.disabled() ||
|
|
119
|
-
this.rootContext.readonly() ||
|
|
120
|
-
this.disabled() ||
|
|
121
|
-
this.rootContext.isDecreaseDisabled(), ...(ngDevMode ? [{ debugName: "isDisabled" }] : []));
|
|
122
216
|
/**
|
|
123
|
-
*
|
|
217
|
+
* When `true`, the button is disabled in addition to inheriting the root's disabled state.
|
|
218
|
+
* @default false
|
|
124
219
|
*/
|
|
125
|
-
this.
|
|
126
|
-
|
|
127
|
-
|
|
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())
|
|
128
242
|
});
|
|
129
243
|
}
|
|
130
|
-
|
|
131
|
-
this.
|
|
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
|
|
255
|
+
});
|
|
256
|
+
const committed = this.rootContext.lastChangedValue ?? this.rootContext.currentValue();
|
|
257
|
+
if (committed !== prev) {
|
|
258
|
+
this.rootContext.commitValue(committed);
|
|
259
|
+
}
|
|
260
|
+
}
|
|
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);
|
|
273
|
+
}
|
|
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
|
+
}
|
|
132
282
|
}
|
|
133
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "
|
|
134
|
-
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "
|
|
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: "
|
|
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
|
}]
|
|
304
|
+
}], ctorParameters: () => [], propDecorators: { disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }] } });
|
|
305
|
+
|
|
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
|
+
}]
|
|
152
328
|
}] });
|
|
153
329
|
|
|
154
|
-
|
|
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", transform: booleanAttribute }] : [{ 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" }] : []));
|
|
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();
|
|
367
|
+
}
|
|
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();
|
|
177
371
|
}
|
|
178
|
-
|
|
179
|
-
|
|
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: "
|
|
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 {
|
|
417
|
+
constructor() {
|
|
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 {
|
|
200
441
|
constructor() {
|
|
201
|
-
this.elementRef = inject(
|
|
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) {
|
|
459
|
+
// Only validate insertions; deletions and composition have `data === null`.
|
|
460
|
+
if (event.data == null) {
|
|
461
|
+
return;
|
|
462
|
+
}
|
|
213
463
|
const target = event.target;
|
|
214
464
|
const nextValue = target.value.slice(0, target.selectionStart ?? undefined) +
|
|
215
|
-
|
|
465
|
+
event.data +
|
|
216
466
|
target.value.slice(target.selectionEnd ?? undefined);
|
|
217
|
-
if (!this.rootContext.
|
|
467
|
+
if (!this.rootContext.isValidPartial(nextValue)) {
|
|
218
468
|
event.preventDefault();
|
|
219
469
|
}
|
|
220
470
|
}
|
|
221
|
-
|
|
222
|
-
|
|
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);
|
|
223
478
|
return;
|
|
224
479
|
}
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
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()) {
|
|
233
489
|
return;
|
|
234
490
|
}
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
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());
|
|
238
504
|
}
|
|
239
|
-
else if (
|
|
240
|
-
|
|
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());
|
|
241
514
|
}
|
|
242
515
|
}
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
event.
|
|
249
|
-
|
|
250
|
-
}
|
|
251
|
-
onKeydownHome(event) {
|
|
252
|
-
event.preventDefault();
|
|
253
|
-
this.rootContext.handleMinMaxValue('min');
|
|
254
|
-
}
|
|
255
|
-
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.
|
|
256
523
|
event.preventDefault();
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
onChange() {
|
|
264
|
-
this.inputValue.set(this.rootContext.textValue());
|
|
265
|
-
}
|
|
266
|
-
onKeydownEnter(event) {
|
|
267
|
-
const target = event.target;
|
|
268
|
-
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
|
+
}
|
|
269
530
|
}
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
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
|
+
}
|
|
273
565
|
}
|
|
274
|
-
|
|
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
|
+
}
|
|
275
575
|
event.preventDefault();
|
|
276
|
-
|
|
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
|
+
});
|
|
277
583
|
}
|
|
278
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "
|
|
279
|
-
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "
|
|
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 }); }
|
|
280
586
|
}
|
|
281
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "
|
|
587
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxNumberFieldInput, decorators: [{
|
|
282
588
|
type: Directive,
|
|
283
589
|
args: [{
|
|
284
590
|
selector: 'input[rdxNumberFieldInput]',
|
|
591
|
+
exportAs: 'rdxNumberFieldInput',
|
|
285
592
|
host: {
|
|
286
|
-
role: 'spinbutton',
|
|
287
593
|
type: 'text',
|
|
288
|
-
tabindex: '0',
|
|
289
594
|
autocomplete: 'off',
|
|
290
595
|
autocorrect: 'off',
|
|
291
596
|
spellcheck: 'false',
|
|
292
597
|
'aria-roledescription': 'Number field',
|
|
293
|
-
'[
|
|
294
|
-
'[
|
|
295
|
-
'[attr.aria-valuemax]': 'rootContext.max()',
|
|
598
|
+
'[id]': 'rootContext.id()',
|
|
599
|
+
'[value]': 'rootContext.inputValue()',
|
|
296
600
|
'[attr.inputmode]': 'rootContext.inputMode()',
|
|
297
|
-
'[attr.
|
|
298
|
-
'[
|
|
601
|
+
'[attr.aria-invalid]': 'rootContext.required() && rootContext.currentValue() === null ? "true" : undefined',
|
|
602
|
+
'[disabled]': 'rootContext.isDisabled()',
|
|
299
603
|
'[attr.readonly]': 'rootContext.readonly() ? "" : undefined',
|
|
604
|
+
'[required]': 'rootContext.required()',
|
|
605
|
+
'[attr.data-disabled]': 'rootContext.isDisabled() ? "" : undefined',
|
|
300
606
|
'[attr.data-readonly]': 'rootContext.readonly() ? "" : undefined',
|
|
301
|
-
'[attr.
|
|
302
|
-
'(
|
|
607
|
+
'[attr.data-required]': 'rootContext.required() ? "" : undefined',
|
|
608
|
+
'(focus)': 'onFocus($event)',
|
|
609
|
+
'(blur)': 'onBlur($event)',
|
|
303
610
|
'(input)': 'onInput($event)',
|
|
304
|
-
'(blur)': 'onKeydownEnter($event)',
|
|
305
611
|
'(beforeinput)': 'onBeforeInput($event)',
|
|
306
|
-
'(keydown
|
|
307
|
-
'(
|
|
308
|
-
'(
|
|
309
|
-
'(keydown.home)': 'onKeydownHome($event)',
|
|
310
|
-
'(keydown.end)': 'onKeydownEnd($event)',
|
|
311
|
-
'(keydown.pageUp)': 'onKeydownPageUp($event)',
|
|
312
|
-
'(keydown.pageDown)': 'onKeydownPageDown($event)',
|
|
313
|
-
'(wheel)': 'onWheelEvent($event)'
|
|
612
|
+
'(keydown)': 'onKeydown($event)',
|
|
613
|
+
'(paste)': 'onPaste($event)',
|
|
614
|
+
'(wheel)': 'onWheel($event)'
|
|
314
615
|
}
|
|
315
616
|
}]
|
|
316
617
|
}], ctorParameters: () => [] });
|
|
317
618
|
|
|
318
|
-
|
|
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 {
|
|
319
647
|
constructor() {
|
|
320
|
-
|
|
321
|
-
this.
|
|
322
|
-
|
|
323
|
-
this.
|
|
324
|
-
|
|
325
|
-
this.
|
|
326
|
-
|
|
327
|
-
this.
|
|
328
|
-
this.formatOptions = input(...(ngDevMode ? [undefined, { debugName: "formatOptions" }] : []));
|
|
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 });
|
|
329
656
|
/**
|
|
330
|
-
*
|
|
657
|
+
* Amount to increment and decrement with the buttons, arrow keys and scrub area.
|
|
658
|
+
* @default 1
|
|
331
659
|
*/
|
|
332
|
-
this.
|
|
660
|
+
this.step = input(1, { ...(ngDevMode ? { debugName: "step" } : /* istanbul ignore next */ {}), transform: numberAttribute });
|
|
333
661
|
/**
|
|
334
|
-
*
|
|
662
|
+
* The step used when incrementing while the Alt key is held. Snaps to multiples of this value.
|
|
663
|
+
* @default 0.1
|
|
335
664
|
*/
|
|
336
|
-
this.
|
|
665
|
+
this.smallStep = input(0.1, { ...(ngDevMode ? { debugName: "smallStep" } : /* istanbul ignore next */ {}), transform: numberAttribute });
|
|
337
666
|
/**
|
|
338
|
-
*
|
|
667
|
+
* The step used when incrementing while the Shift key is held. Snaps to multiples of this value.
|
|
668
|
+
* @default 10
|
|
339
669
|
*/
|
|
340
|
-
this.
|
|
341
|
-
(this.clampInputValue(this.value()) === this.min() || (this.min() && !isNaN(this.value()))
|
|
342
|
-
? handleDecimalOperation('-', this.value(), this.step()) < this.min()
|
|
343
|
-
: false), ...(ngDevMode ? [{ debugName: "isDecreaseDisabled" }] : []));
|
|
670
|
+
this.largeStep = input(10, { ...(ngDevMode ? { debugName: "largeStep" } : /* istanbul ignore next */ {}), transform: numberAttribute });
|
|
344
671
|
/**
|
|
345
|
-
*
|
|
672
|
+
* Whether the value should snap to the nearest step when incrementing or decrementing.
|
|
673
|
+
* @default false
|
|
346
674
|
*/
|
|
347
|
-
this.
|
|
348
|
-
(this.clampInputValue(this.value()) === this.max() || (this.min() && !isNaN(this.value()))
|
|
349
|
-
? handleDecimalOperation('+', this.value(), this.step()) > this.max()
|
|
350
|
-
: false), ...(ngDevMode ? [{ debugName: "isIncreaseDisabled" }] : []));
|
|
675
|
+
this.snapOnStep = input(false, { ...(ngDevMode ? { debugName: "snapOnStep" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
351
676
|
/**
|
|
352
|
-
*
|
|
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
|
|
353
680
|
*/
|
|
354
|
-
this.
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
681
|
+
this.allowOutOfRange = input(false, { ...(ngDevMode ? { debugName: "allowOutOfRange" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
682
|
+
/**
|
|
683
|
+
* Whether the value can be changed with the mouse wheel while the input is focused.
|
|
684
|
+
* @default false
|
|
685
|
+
*/
|
|
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 */ []));
|
|
691
|
+
/**
|
|
692
|
+
* When `true`, the user cannot interact with the field.
|
|
693
|
+
* @default false
|
|
694
|
+
*/
|
|
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();
|
|
362
716
|
/**
|
|
363
|
-
*
|
|
364
|
-
*
|
|
365
|
-
* @ignore
|
|
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.
|
|
366
719
|
*/
|
|
367
|
-
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 */ []));
|
|
368
727
|
/**
|
|
369
|
-
* @ignore
|
|
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.
|
|
370
730
|
*/
|
|
371
|
-
this.
|
|
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
|
+
});
|
|
372
783
|
}
|
|
373
|
-
/**
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
// Clamp to min and max, round to the nearest step, and round to the specified number of digits
|
|
378
|
-
let clampedValue;
|
|
379
|
-
if (this.step() === undefined || isNaN(this.step()) || !this.stepSnapping()) {
|
|
380
|
-
clampedValue = clamp(val, this.min(), this.max());
|
|
381
|
-
}
|
|
382
|
-
else {
|
|
383
|
-
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 '';
|
|
384
788
|
}
|
|
385
|
-
|
|
386
|
-
return clampedValue;
|
|
789
|
+
return this.formatter().format(value);
|
|
387
790
|
}
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
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;
|
|
392
795
|
}
|
|
393
|
-
/**
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
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();
|
|
399
804
|
}
|
|
400
|
-
|
|
401
|
-
this.
|
|
805
|
+
if (event?.shiftKey) {
|
|
806
|
+
return this.largeStep();
|
|
402
807
|
}
|
|
808
|
+
return this.step();
|
|
403
809
|
}
|
|
404
|
-
/**
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
handleDecrease(multiplier = 1) {
|
|
408
|
-
this.handleChangingValue('decrease', multiplier);
|
|
810
|
+
/** @ignore Registers the native input element. */
|
|
811
|
+
registerInput(el) {
|
|
812
|
+
this.inputEl.set(el);
|
|
409
813
|
}
|
|
410
|
-
/**
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
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();
|
|
415
821
|
}
|
|
416
822
|
/**
|
|
417
823
|
* @ignore
|
|
824
|
+
* Validates and applies a candidate value, emitting `onValueChange` when it changes.
|
|
825
|
+
* Returns whether a change was fired.
|
|
418
826
|
*/
|
|
419
|
-
|
|
420
|
-
const
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
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;
|
|
425
850
|
}
|
|
426
|
-
|
|
427
|
-
if (
|
|
428
|
-
|
|
851
|
+
this.lastChangedValue = validatedValue;
|
|
852
|
+
if (this.allowInputSync) {
|
|
853
|
+
this.setInputValue(this.formatNumber(validatedValue));
|
|
429
854
|
}
|
|
430
|
-
return
|
|
855
|
+
return shouldFireChange;
|
|
431
856
|
}
|
|
432
857
|
/**
|
|
433
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.
|
|
434
861
|
*/
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
el.value = val;
|
|
440
|
-
return el;
|
|
441
|
-
});
|
|
442
|
-
}
|
|
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);
|
|
443
866
|
}
|
|
444
|
-
/**
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
867
|
+
/** @ignore Emits the committed value at the end of an interaction. */
|
|
868
|
+
commitValue(value) {
|
|
869
|
+
this.hasPendingCommit = false;
|
|
870
|
+
this.onValueCommitted.emit(value);
|
|
871
|
+
}
|
|
872
|
+
applyValue(value) {
|
|
873
|
+
this.value.set(value);
|
|
874
|
+
this.cva.setValue(value);
|
|
449
875
|
}
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
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()) {
|
|
453
1061
|
return;
|
|
454
1062
|
}
|
|
455
|
-
const
|
|
456
|
-
|
|
457
|
-
|
|
1063
|
+
const isTouch = event.pointerType === 'touch';
|
|
1064
|
+
this.isTouchInput.set(isTouch);
|
|
1065
|
+
if (event.pointerType === 'mouse') {
|
|
1066
|
+
event.preventDefault();
|
|
1067
|
+
this.rootContext.inputEl()?.focus();
|
|
458
1068
|
}
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
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);
|
|
462
1078
|
}
|
|
463
|
-
|
|
464
|
-
this.
|
|
1079
|
+
catch {
|
|
1080
|
+
this.isPointerLockDenied.set(true);
|
|
465
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})`;
|
|
466
1141
|
}
|
|
467
1142
|
}
|
|
468
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "
|
|
469
|
-
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "
|
|
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 }); }
|
|
470
1145
|
}
|
|
471
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "
|
|
1146
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxNumberFieldScrubArea, decorators: [{
|
|
472
1147
|
type: Directive,
|
|
473
1148
|
args: [{
|
|
474
|
-
selector: '[
|
|
475
|
-
exportAs: '
|
|
476
|
-
providers: [
|
|
1149
|
+
selector: '[rdxNumberFieldScrubArea]',
|
|
1150
|
+
exportAs: 'rdxNumberFieldScrubArea',
|
|
1151
|
+
providers: [provideNumberFieldScrubAreaContext(() => inject(RdxNumberFieldScrubArea).context)],
|
|
477
1152
|
host: {
|
|
478
|
-
role: '
|
|
479
|
-
'[
|
|
480
|
-
'[
|
|
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)'
|
|
481
1162
|
}
|
|
482
1163
|
}]
|
|
483
|
-
}] });
|
|
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: () => [] });
|
|
484
1208
|
|
|
485
1209
|
const _imports = [
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
1210
|
+
RdxNumberFieldRoot,
|
|
1211
|
+
RdxNumberFieldGroup,
|
|
1212
|
+
RdxNumberFieldInput,
|
|
1213
|
+
RdxNumberFieldHiddenInput,
|
|
1214
|
+
RdxNumberFieldIncrement,
|
|
1215
|
+
RdxNumberFieldDecrement,
|
|
1216
|
+
RdxNumberFieldScrubArea,
|
|
1217
|
+
RdxNumberFieldScrubAreaCursor
|
|
490
1218
|
];
|
|
491
1219
|
class RdxNumberFieldModule {
|
|
492
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "
|
|
493
|
-
static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
1220
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxNumberFieldModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
|
|
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] }); }
|
|
1236
|
+
static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxNumberFieldModule }); }
|
|
501
1237
|
}
|
|
502
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "
|
|
1238
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxNumberFieldModule, decorators: [{
|
|
503
1239
|
type: NgModule,
|
|
504
1240
|
args: [{
|
|
505
1241
|
imports: [..._imports],
|
|
@@ -511,5 +1247,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.3", ngImpor
|
|
|
511
1247
|
* Generated bundle index. Do not edit.
|
|
512
1248
|
*/
|
|
513
1249
|
|
|
514
|
-
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 };
|
|
515
1251
|
//# sourceMappingURL=radix-ng-primitives-number-field.mjs.map
|