@radix-ng/primitives 0.51.0 → 1.0.0-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (178) hide show
  1. package/fesm2022/radix-ng-primitives-accordion.mjs +105 -38
  2. package/fesm2022/radix-ng-primitives-accordion.mjs.map +1 -1
  3. package/fesm2022/radix-ng-primitives-alert-dialog.mjs +221 -129
  4. package/fesm2022/radix-ng-primitives-alert-dialog.mjs.map +1 -1
  5. package/fesm2022/radix-ng-primitives-arrow.mjs +20 -4
  6. package/fesm2022/radix-ng-primitives-arrow.mjs.map +1 -1
  7. package/fesm2022/radix-ng-primitives-aspect-ratio.mjs.map +1 -1
  8. package/fesm2022/radix-ng-primitives-avatar.mjs +54 -61
  9. package/fesm2022/radix-ng-primitives-avatar.mjs.map +1 -1
  10. package/fesm2022/radix-ng-primitives-button.mjs +123 -0
  11. package/fesm2022/radix-ng-primitives-button.mjs.map +1 -0
  12. package/fesm2022/radix-ng-primitives-calendar.mjs.map +1 -1
  13. package/fesm2022/radix-ng-primitives-checkbox.mjs +378 -54
  14. package/fesm2022/radix-ng-primitives-checkbox.mjs.map +1 -1
  15. package/fesm2022/radix-ng-primitives-collapsible.mjs +182 -81
  16. package/fesm2022/radix-ng-primitives-collapsible.mjs.map +1 -1
  17. package/fesm2022/radix-ng-primitives-collection.mjs +40 -57
  18. package/fesm2022/radix-ng-primitives-collection.mjs.map +1 -1
  19. package/fesm2022/radix-ng-primitives-config.mjs.map +1 -1
  20. package/fesm2022/radix-ng-primitives-context-menu.mjs +140 -424
  21. package/fesm2022/radix-ng-primitives-context-menu.mjs.map +1 -1
  22. package/fesm2022/radix-ng-primitives-core.mjs +735 -744
  23. package/fesm2022/radix-ng-primitives-core.mjs.map +1 -1
  24. package/fesm2022/radix-ng-primitives-cropper.mjs +1 -0
  25. package/fesm2022/radix-ng-primitives-cropper.mjs.map +1 -1
  26. package/fesm2022/radix-ng-primitives-date-field.mjs +51 -45
  27. package/fesm2022/radix-ng-primitives-date-field.mjs.map +1 -1
  28. package/fesm2022/radix-ng-primitives-dialog.mjs +655 -327
  29. package/fesm2022/radix-ng-primitives-dialog.mjs.map +1 -1
  30. package/fesm2022/radix-ng-primitives-dismissable-layer.mjs +70 -46
  31. package/fesm2022/radix-ng-primitives-dismissable-layer.mjs.map +1 -1
  32. package/fesm2022/radix-ng-primitives-drawer.mjs +1059 -0
  33. package/fesm2022/radix-ng-primitives-drawer.mjs.map +1 -0
  34. package/fesm2022/radix-ng-primitives-editable.mjs.map +1 -1
  35. package/fesm2022/radix-ng-primitives-field.mjs +363 -0
  36. package/fesm2022/radix-ng-primitives-field.mjs.map +1 -0
  37. package/fesm2022/radix-ng-primitives-fieldset.mjs +79 -0
  38. package/fesm2022/radix-ng-primitives-fieldset.mjs.map +1 -0
  39. package/fesm2022/radix-ng-primitives-focus-scope.mjs +23 -8
  40. package/fesm2022/radix-ng-primitives-focus-scope.mjs.map +1 -1
  41. package/fesm2022/radix-ng-primitives-input.mjs +172 -0
  42. package/fesm2022/radix-ng-primitives-input.mjs.map +1 -0
  43. package/fesm2022/radix-ng-primitives-label.mjs +6 -6
  44. package/fesm2022/radix-ng-primitives-label.mjs.map +1 -1
  45. package/fesm2022/radix-ng-primitives-menu.mjs +1480 -344
  46. package/fesm2022/radix-ng-primitives-menu.mjs.map +1 -1
  47. package/fesm2022/radix-ng-primitives-menubar.mjs +290 -162
  48. package/fesm2022/radix-ng-primitives-menubar.mjs.map +1 -1
  49. package/fesm2022/radix-ng-primitives-meter.mjs +271 -0
  50. package/fesm2022/radix-ng-primitives-meter.mjs.map +1 -0
  51. package/fesm2022/radix-ng-primitives-navigation-menu.mjs +1052 -1553
  52. package/fesm2022/radix-ng-primitives-navigation-menu.mjs.map +1 -1
  53. package/fesm2022/radix-ng-primitives-number-field.mjs +1102 -367
  54. package/fesm2022/radix-ng-primitives-number-field.mjs.map +1 -1
  55. package/fesm2022/radix-ng-primitives-pagination.mjs.map +1 -1
  56. package/fesm2022/radix-ng-primitives-popover.mjs +978 -989
  57. package/fesm2022/radix-ng-primitives-popover.mjs.map +1 -1
  58. package/fesm2022/radix-ng-primitives-popper.mjs +91 -41
  59. package/fesm2022/radix-ng-primitives-popper.mjs.map +1 -1
  60. package/fesm2022/radix-ng-primitives-portal.mjs +34 -10
  61. package/fesm2022/radix-ng-primitives-portal.mjs.map +1 -1
  62. package/fesm2022/radix-ng-primitives-presence.mjs +134 -246
  63. package/fesm2022/radix-ng-primitives-presence.mjs.map +1 -1
  64. package/fesm2022/radix-ng-primitives-preview-card.mjs +997 -0
  65. package/fesm2022/radix-ng-primitives-preview-card.mjs.map +1 -0
  66. package/fesm2022/radix-ng-primitives-progress.mjs +223 -84
  67. package/fesm2022/radix-ng-primitives-progress.mjs.map +1 -1
  68. package/fesm2022/radix-ng-primitives-radio.mjs +191 -51
  69. package/fesm2022/radix-ng-primitives-radio.mjs.map +1 -1
  70. package/fesm2022/radix-ng-primitives-roving-focus.mjs +96 -50
  71. package/fesm2022/radix-ng-primitives-roving-focus.mjs.map +1 -1
  72. package/fesm2022/radix-ng-primitives-select.mjs +791 -509
  73. package/fesm2022/radix-ng-primitives-select.mjs.map +1 -1
  74. package/fesm2022/radix-ng-primitives-separator.mjs +12 -35
  75. package/fesm2022/radix-ng-primitives-separator.mjs.map +1 -1
  76. package/fesm2022/radix-ng-primitives-slider.mjs +969 -717
  77. package/fesm2022/radix-ng-primitives-slider.mjs.map +1 -1
  78. package/fesm2022/radix-ng-primitives-stepper.mjs +15 -19
  79. package/fesm2022/radix-ng-primitives-stepper.mjs.map +1 -1
  80. package/fesm2022/radix-ng-primitives-switch.mjs +125 -113
  81. package/fesm2022/radix-ng-primitives-switch.mjs.map +1 -1
  82. package/fesm2022/radix-ng-primitives-tabs.mjs +381 -108
  83. package/fesm2022/radix-ng-primitives-tabs.mjs.map +1 -1
  84. package/fesm2022/radix-ng-primitives-time-field.mjs +55 -46
  85. package/fesm2022/radix-ng-primitives-time-field.mjs.map +1 -1
  86. package/fesm2022/radix-ng-primitives-toggle-group.mjs +121 -247
  87. package/fesm2022/radix-ng-primitives-toggle-group.mjs.map +1 -1
  88. package/fesm2022/radix-ng-primitives-toggle.mjs +98 -61
  89. package/fesm2022/radix-ng-primitives-toggle.mjs.map +1 -1
  90. package/fesm2022/radix-ng-primitives-toolbar.mjs +303 -92
  91. package/fesm2022/radix-ng-primitives-toolbar.mjs.map +1 -1
  92. package/fesm2022/radix-ng-primitives-tooltip.mjs +690 -1071
  93. package/fesm2022/radix-ng-primitives-tooltip.mjs.map +1 -1
  94. package/fesm2022/radix-ng-primitives-visually-hidden.mjs +25 -66
  95. package/fesm2022/radix-ng-primitives-visually-hidden.mjs.map +1 -1
  96. package/meter/README.md +3 -0
  97. package/navigation-menu/README.md +2 -1
  98. package/package.json +31 -18
  99. package/portal/README.md +2 -0
  100. package/preview-card/README.md +3 -0
  101. package/schematics/collection.json +1 -0
  102. package/schematics/ng-add/index.d.ts +3 -2
  103. package/schematics/ng-add/index.js +62 -31
  104. package/schematics/ng-add/index.js.map +1 -1
  105. package/schematics/ng-add/package-config.d.ts +4 -2
  106. package/schematics/ng-add/package-config.js +10 -2
  107. package/schematics/ng-add/package-config.js.map +1 -1
  108. package/schematics/ng-add/schema.d.ts +3 -0
  109. package/schematics/ng-add/schema.js +3 -0
  110. package/schematics/ng-add/schema.js.map +1 -0
  111. package/schematics/ng-add/schema.json +14 -0
  112. package/select/README.md +2 -0
  113. package/types/radix-ng-primitives-accordion.d.ts +48 -14
  114. package/types/radix-ng-primitives-alert-dialog.d.ts +95 -38
  115. package/types/radix-ng-primitives-arrow.d.ts +1 -1
  116. package/types/radix-ng-primitives-aspect-ratio.d.ts +1 -1
  117. package/types/radix-ng-primitives-avatar.d.ts +7 -11
  118. package/types/radix-ng-primitives-button.d.ts +73 -0
  119. package/types/radix-ng-primitives-calendar.d.ts +1 -2
  120. package/types/radix-ng-primitives-checkbox.d.ts +201 -32
  121. package/types/radix-ng-primitives-collapsible.d.ts +112 -39
  122. package/types/radix-ng-primitives-collection.d.ts +38 -34
  123. package/types/radix-ng-primitives-config.d.ts +1 -1
  124. package/types/radix-ng-primitives-context-menu.d.ts +60 -116
  125. package/types/radix-ng-primitives-core.d.ts +307 -236
  126. package/types/radix-ng-primitives-cropper.d.ts +2 -2
  127. package/types/radix-ng-primitives-date-field.d.ts +38 -23
  128. package/types/radix-ng-primitives-dialog.d.ts +282 -165
  129. package/types/radix-ng-primitives-dismissable-layer.d.ts +15 -7
  130. package/types/radix-ng-primitives-drawer.d.ts +448 -0
  131. package/types/radix-ng-primitives-editable.d.ts +1 -1
  132. package/types/radix-ng-primitives-field.d.ts +373 -0
  133. package/types/radix-ng-primitives-fieldset.d.ts +48 -0
  134. package/types/radix-ng-primitives-focus-scope.d.ts +13 -5
  135. package/types/radix-ng-primitives-input.d.ts +87 -0
  136. package/types/radix-ng-primitives-label.d.ts +0 -1
  137. package/types/radix-ng-primitives-menu.d.ts +572 -99
  138. package/types/radix-ng-primitives-menubar.d.ts +60 -50
  139. package/types/radix-ng-primitives-meter.d.ts +193 -0
  140. package/types/radix-ng-primitives-navigation-menu.d.ts +422 -340
  141. package/types/radix-ng-primitives-number-field.d.ts +405 -145
  142. package/types/radix-ng-primitives-pagination.d.ts +2 -2
  143. package/types/radix-ng-primitives-popover.d.ts +365 -351
  144. package/types/radix-ng-primitives-popper.d.ts +49 -9
  145. package/types/radix-ng-primitives-portal.d.ts +14 -6
  146. package/types/radix-ng-primitives-presence.d.ts +28 -76
  147. package/types/radix-ng-primitives-preview-card.d.ts +359 -0
  148. package/types/radix-ng-primitives-progress.d.ts +174 -48
  149. package/types/radix-ng-primitives-radio.d.ts +55 -25
  150. package/types/radix-ng-primitives-roving-focus.d.ts +30 -21
  151. package/types/radix-ng-primitives-select.d.ts +475 -177
  152. package/types/radix-ng-primitives-separator.d.ts +7 -32
  153. package/types/radix-ng-primitives-slider.d.ts +315 -201
  154. package/types/radix-ng-primitives-stepper.d.ts +5 -7
  155. package/types/radix-ng-primitives-switch.d.ts +86 -71
  156. package/types/radix-ng-primitives-tabs.d.ts +213 -79
  157. package/types/radix-ng-primitives-time-field.d.ts +42 -27
  158. package/types/radix-ng-primitives-toggle-group.d.ts +85 -164
  159. package/types/radix-ng-primitives-toggle.d.ts +43 -53
  160. package/types/radix-ng-primitives-toolbar.d.ts +163 -38
  161. package/types/radix-ng-primitives-tooltip.d.ts +347 -384
  162. package/types/radix-ng-primitives-visually-hidden.d.ts +19 -19
  163. package/dropdown-menu/README.md +0 -1
  164. package/fesm2022/radix-ng-primitives-dropdown-menu.mjs +0 -581
  165. package/fesm2022/radix-ng-primitives-dropdown-menu.mjs.map +0 -1
  166. package/fesm2022/radix-ng-primitives-hover-card.mjs +0 -1238
  167. package/fesm2022/radix-ng-primitives-hover-card.mjs.map +0 -1
  168. package/fesm2022/radix-ng-primitives-select2.mjs +0 -897
  169. package/fesm2022/radix-ng-primitives-select2.mjs.map +0 -1
  170. package/fesm2022/radix-ng-primitives-tooltip2.mjs +0 -735
  171. package/fesm2022/radix-ng-primitives-tooltip2.mjs.map +0 -1
  172. package/hover-card/README.md +0 -3
  173. package/select2/README.md +0 -3
  174. package/tooltip2/README.md +0 -3
  175. package/types/radix-ng-primitives-dropdown-menu.d.ts +0 -171
  176. package/types/radix-ng-primitives-hover-card.d.ts +0 -471
  177. package/types/radix-ng-primitives-select2.d.ts +0 -511
  178. package/types/radix-ng-primitives-tooltip2.d.ts +0 -325
@@ -1,852 +1,1104 @@
1
1
  import * as i0 from '@angular/core';
2
- import { inject, output, Directive, input, booleanAttribute, viewChild, signal, Input, Component, Injectable, numberAttribute, model, computed, ElementRef, PLATFORM_ID, NgModule } from '@angular/core';
3
- import { NgTemplateOutlet, isPlatformBrowser } from '@angular/common';
2
+ import { inject, ElementRef, DestroyRef, Directive, computed, input, numberAttribute, booleanAttribute, model, output, signal, NgModule } from '@angular/core';
3
+ import { DOCUMENT } from '@angular/common';
4
+ import * as i1 from '@radix-ng/primitives/core';
5
+ import { createContext, clamp, injectControlValueAccessor, injectId, RdxControlValueAccessor } from '@radix-ng/primitives/core';
6
+ export { clamp } from '@radix-ng/primitives/core';
4
7
 
5
- // https://github.com/tmcw-up-for-adoption/simple-linear-scale/blob/master/index.js
6
- function linearScale(input, output) {
7
- return (value) => {
8
- if (input[0] === input[1] || output[0] === output[1])
9
- return output[0];
10
- const ratio = (output[1] - output[0]) / (input[1] - input[0]);
11
- return output[0] + ratio * (value - input[0]);
12
- };
13
- }
14
8
  /**
15
- * Verifies the minimum steps between all values is greater than or equal
16
- * to the expected minimum steps.
9
+ * The Slider context exposes the root directive instance to every child part.
10
+ * The root owns all state, value-change logic and thumb registration; parts read
11
+ * signals and call methods off it.
17
12
  *
18
- * @example
19
- * // returns false
20
- * hasMinStepsBetweenValues([1,2,3], 2);
21
- *
22
- * @example
23
- * // returns true
24
- * hasMinStepsBetweenValues([1,2,3], 1);
13
+ * @see https://base-ui.com/react/components/slider
25
14
  */
26
- function hasMinStepsBetweenValues(values, minStepsBetweenValues) {
27
- if (minStepsBetweenValues > 0) {
28
- const stepsBetweenValues = getStepsBetweenValues(values);
29
- const actualMinStepsBetweenValues = Math.min(...stepsBetweenValues);
30
- return actualMinStepsBetweenValues >= minStepsBetweenValues;
15
+ const [injectSliderRootContext, provideSliderRootContext] = createContext('RdxSliderRootContext');
16
+
17
+ /** Ascending comparator. */
18
+ function asc(a, b) {
19
+ return a - b;
20
+ }
21
+ /** Maps a value within `[min, max]` to a 0–100 percentage. */
22
+ function valueToPercent(value, min, max) {
23
+ return ((value - min) * 100) / (max - min);
24
+ }
25
+ /** Replaces the item at `index` then re-sorts the array ascending. */
26
+ function replaceArrayItemAtIndex(array, index, newValue) {
27
+ const output = array.slice();
28
+ output[index] = newValue;
29
+ return output.sort(asc);
30
+ }
31
+ /** The center point of an element in client coordinates. */
32
+ function getMidpoint(element) {
33
+ const rect = element.getBoundingClientRect();
34
+ return { x: (rect.left + rect.right) / 2, y: (rect.top + rect.bottom) / 2 };
35
+ }
36
+ /** Converts an array of values into clamped 0–100 percentages. */
37
+ function valueArrayToPercentages(values, min, max) {
38
+ return values.map((value) => clamp(valueToPercent(value, min, max), 0, 100));
39
+ }
40
+ /** Number of decimal places in `num`, handling exponential notation for tiny values. */
41
+ function getDecimalPrecision(num) {
42
+ if (num === 0) {
43
+ return 0;
31
44
  }
32
- return true;
45
+ if (Math.abs(num) < 1) {
46
+ const parts = num.toExponential().split('e-');
47
+ const mantissaDecimalPart = parts[0].split('.')[1];
48
+ return (mantissaDecimalPart ? mantissaDecimalPart.length : 0) + parseInt(parts[1], 10);
49
+ }
50
+ const decimalPart = num.toString().split('.')[1];
51
+ return decimalPart ? decimalPart.length : 0;
52
+ }
53
+ /** Snaps `value` to the nearest step, using `min` as the origin of the step grid. */
54
+ function roundValueToStep(value, step, min) {
55
+ const nearest = Math.round((value - min) / step) * step + min;
56
+ return Number(nearest.toFixed(Math.max(getDecimalPrecision(step), getDecimalPrecision(min))));
33
57
  }
34
58
  /**
35
- * Given a `values` array and a `nextValue`, determine which value in
36
- * the array is closest to `nextValue` and return its index.
37
- *
38
- * @example
39
- * // returns 1
40
- * getClosestValueIndex([10, 30], 25);
59
+ * Resolves the value(s) for the keyboard / hidden-input path: clamps to the
60
+ * bounds, and for range sliders also clamps to neighbouring thumbs and re-sorts.
61
+ * Returns a `number` for single sliders and a sorted `number[]` for range sliders.
41
62
  */
42
- function getClosestValueIndex(values, nextValue) {
43
- if (values.length === 1)
44
- return 0;
45
- const distances = values.map((value) => Math.abs(value - nextValue));
46
- const closestDistance = Math.min(...distances);
47
- return distances.indexOf(closestDistance);
63
+ function getSliderValue(valueInput, index, min, max, range, values) {
64
+ let newValue = clamp(valueInput, min, max);
65
+ if (range) {
66
+ newValue = clamp(newValue, values[index - 1] ?? Number.NEGATIVE_INFINITY, values[index + 1] ?? Number.POSITIVE_INFINITY);
67
+ return replaceArrayItemAtIndex(values, index, newValue);
68
+ }
69
+ return newValue;
70
+ }
71
+ /** Returns `false` if any adjacent pair of values is closer than the minimum distance. */
72
+ function validateMinimumDistance(values, step, minStepsBetweenValues) {
73
+ if (!Array.isArray(values)) {
74
+ return true;
75
+ }
76
+ const distances = values.reduce((acc, val, index, vals) => {
77
+ if (index === vals.length - 1) {
78
+ return acc;
79
+ }
80
+ acc.push(Math.abs(val - vals[index + 1]));
81
+ return acc;
82
+ }, []);
83
+ return Math.min(...distances) >= step * minStepsBetweenValues;
84
+ }
85
+ /** Keyboard step helper: increments/decrements `thumbValue` and clamps to bounds. */
86
+ function getNewValue(thumbValue, increment, direction, min, max) {
87
+ const value = direction === 1 ? thumbValue + increment : thumbValue - increment;
88
+ const roundedValue = Number(value.toFixed(Math.max(getDecimalPrecision(thumbValue), getDecimalPrecision(increment), getDecimalPrecision(min))));
89
+ return clamp(roundedValue, min, max);
48
90
  }
49
91
  /**
50
- * Gets an array of steps between each value.
51
- *
52
- * @example
53
- * // returns [1, 9]
54
- * getStepsBetweenValues([10, 11, 20]);
92
+ * The "push" collision algorithm: moves the pressed thumb and pushes its
93
+ * neighbours only as far as needed, letting them spring back toward their
94
+ * initial positions as the pressed thumb retreats.
55
95
  */
56
- function getStepsBetweenValues(values) {
57
- return values.slice(0, -1).map((value, index) => values[index + 1] - value);
96
+ function getPushedThumbValues(params) {
97
+ const { values, index, nextValue, min, max, step, minStepsBetweenValues, initialValues } = params;
98
+ if (values.length === 0) {
99
+ return [];
100
+ }
101
+ const nextValues = values.slice();
102
+ const minValueDifference = step * minStepsBetweenValues;
103
+ const lastIndex = nextValues.length - 1;
104
+ const baseInitialValues = initialValues ?? values;
105
+ const indexMin = min + index * minValueDifference;
106
+ const indexMax = max - (lastIndex - index) * minValueDifference;
107
+ nextValues[index] = clamp(nextValue, indexMin, indexMax);
108
+ // push thumbs to the right
109
+ for (let i = index + 1; i <= lastIndex; i += 1) {
110
+ const minAllowed = nextValues[i - 1] + minValueDifference;
111
+ const maxAllowed = max - (lastIndex - i) * minValueDifference;
112
+ const initialValue = baseInitialValues[i] ?? nextValues[i];
113
+ let candidate = Math.max(nextValues[i], minAllowed);
114
+ if (initialValue < candidate) {
115
+ candidate = Math.max(initialValue, minAllowed);
116
+ }
117
+ nextValues[i] = clamp(candidate, minAllowed, maxAllowed);
118
+ }
119
+ // push thumbs to the left
120
+ for (let i = index - 1; i >= 0; i -= 1) {
121
+ const maxAllowed = nextValues[i + 1] - minValueDifference;
122
+ const minAllowed = min + i * minValueDifference;
123
+ const initialValue = baseInitialValues[i] ?? nextValues[i];
124
+ let candidate = Math.min(nextValues[i], maxAllowed);
125
+ if (initialValue > candidate) {
126
+ candidate = Math.min(initialValue, maxAllowed);
127
+ }
128
+ nextValues[i] = clamp(candidate, minAllowed, maxAllowed);
129
+ }
130
+ for (let i = 0; i <= lastIndex; i += 1) {
131
+ nextValues[i] = Number(nextValues[i].toFixed(12));
132
+ }
133
+ return nextValues;
134
+ }
135
+ /** Dispatches the pressed thumb's new value through the configured collision behavior. */
136
+ function resolveThumbCollision(params) {
137
+ const { behavior, values, currentValues, initialValues, pressedIndex, nextValue, min, max, step, minStepsBetweenValues } = params;
138
+ const activeValues = (currentValues ?? values).slice();
139
+ const baselineValues = initialValues ?? values;
140
+ const range = activeValues.length > 1;
141
+ const minValueDifference = step * minStepsBetweenValues;
142
+ if (!range) {
143
+ return { value: nextValue, thumbIndex: 0, didSwap: false };
144
+ }
145
+ if (behavior === 'push') {
146
+ const value = getPushedThumbValues({
147
+ values: activeValues,
148
+ index: pressedIndex,
149
+ nextValue,
150
+ min,
151
+ max,
152
+ step,
153
+ minStepsBetweenValues
154
+ });
155
+ return { value, thumbIndex: pressedIndex, didSwap: false };
156
+ }
157
+ if (behavior === 'swap') {
158
+ const pressedInitialValue = activeValues[pressedIndex];
159
+ const epsilon = 1e-7;
160
+ const candidateValues = activeValues.slice();
161
+ const previousNeighbor = candidateValues[pressedIndex - 1];
162
+ const nextNeighbor = candidateValues[pressedIndex + 1];
163
+ const lowerBound = previousNeighbor != null ? previousNeighbor + minValueDifference : min;
164
+ const upperBound = nextNeighbor != null ? nextNeighbor - minValueDifference : max;
165
+ const constrainedValue = clamp(nextValue, lowerBound, upperBound);
166
+ const pressedValueAfterClamp = Number(constrainedValue.toFixed(12));
167
+ candidateValues[pressedIndex] = pressedValueAfterClamp;
168
+ const movingForward = nextValue > pressedInitialValue;
169
+ const movingBackward = nextValue < pressedInitialValue;
170
+ const shouldSwapForward = movingForward && nextNeighbor != null && nextValue >= nextNeighbor - epsilon;
171
+ const shouldSwapBackward = movingBackward && previousNeighbor != null && nextValue <= previousNeighbor + epsilon;
172
+ if (!shouldSwapForward && !shouldSwapBackward) {
173
+ return { value: candidateValues, thumbIndex: pressedIndex, didSwap: false };
174
+ }
175
+ const targetIndex = shouldSwapForward ? pressedIndex + 1 : pressedIndex - 1;
176
+ const initialValuesForPush = candidateValues.map((_, idx) => {
177
+ if (idx === pressedIndex) {
178
+ return pressedValueAfterClamp;
179
+ }
180
+ const baseline = baselineValues[idx];
181
+ return baseline != null ? baseline : activeValues[idx];
182
+ });
183
+ const nextValueForTarget = shouldSwapForward
184
+ ? Math.max(nextValue, candidateValues[targetIndex])
185
+ : Math.min(nextValue, candidateValues[targetIndex]);
186
+ const adjustedValues = getPushedThumbValues({
187
+ values: candidateValues,
188
+ index: targetIndex,
189
+ nextValue: nextValueForTarget,
190
+ min,
191
+ max,
192
+ step,
193
+ minStepsBetweenValues,
194
+ initialValues: initialValuesForPush
195
+ });
196
+ const neighborIndex = shouldSwapForward ? targetIndex - 1 : targetIndex + 1;
197
+ if (neighborIndex >= 0 && neighborIndex < adjustedValues.length) {
198
+ const previousValue = adjustedValues[neighborIndex - 1];
199
+ const nextValueAfter = adjustedValues[neighborIndex + 1];
200
+ let neighborLowerBound = previousValue != null ? previousValue + minValueDifference : min;
201
+ neighborLowerBound = Math.max(neighborLowerBound, min + neighborIndex * minValueDifference);
202
+ let neighborUpperBound = nextValueAfter != null ? nextValueAfter - minValueDifference : max;
203
+ neighborUpperBound = Math.min(neighborUpperBound, max - (adjustedValues.length - 1 - neighborIndex) * minValueDifference);
204
+ const restoredValue = clamp(pressedValueAfterClamp, neighborLowerBound, neighborUpperBound);
205
+ adjustedValues[neighborIndex] = Number(restoredValue.toFixed(12));
206
+ }
207
+ return { value: adjustedValues, thumbIndex: targetIndex, didSwap: true };
208
+ }
209
+ // behavior === 'none' — clamp the pressed thumb between its neighbours; thumbs never cross.
210
+ const previousNeighbor = activeValues[pressedIndex - 1];
211
+ const nextNeighbor = activeValues[pressedIndex + 1];
212
+ const lowerBound = previousNeighbor != null ? previousNeighbor + minValueDifference : min;
213
+ const upperBound = nextNeighbor != null ? nextNeighbor - minValueDifference : max;
214
+ const constrained = Number(clamp(nextValue, lowerBound, upperBound).toFixed(12));
215
+ const value = activeValues.slice();
216
+ value[pressedIndex] = constrained;
217
+ return { value, thumbIndex: pressedIndex, didSwap: false };
58
218
  }
59
219
  /**
60
- * Offsets the thumb centre point while sliding to ensure it remains
61
- * within the bounds of the slider when reaching the edges
220
+ * Border + padding on the leading/trailing edge of the control along the active
221
+ * axis. Uses physical longhands (`left`/`right`/`top`/`bottom`) rather than
222
+ * logical ones (`inline-start`/…) because `getComputedStyle` resolves the
223
+ * physical properties in every browser, whereas logical longhands return an
224
+ * empty string in some engines.
62
225
  */
63
- function getThumbInBoundsOffset(width, left, direction) {
64
- const halfWidth = width / 2;
65
- const halfPercent = 50;
66
- const offset = linearScale([0, halfPercent], [0, halfWidth]);
67
- return (halfWidth - offset(left) * direction) * direction;
68
- }
69
- function convertValueToPercentage(value, min, max) {
70
- const maxSteps = max - min;
71
- const percentPerStep = 100 / maxSteps;
72
- const percentage = percentPerStep * (value - min);
73
- return clamp(percentage, 0, 100);
226
+ function getControlOffset(styles, vertical, rtl) {
227
+ if (!styles) {
228
+ return { start: 0, end: 0 };
229
+ }
230
+ const parseSize = (v) => {
231
+ const p = parseFloat(v);
232
+ return Number.isNaN(p) ? 0 : p;
233
+ };
234
+ const sideOffset = (side) => parseSize(styles.getPropertyValue(`border-${side}-width`)) +
235
+ parseSize(styles.getPropertyValue(`padding-${side}`));
236
+ let startSide;
237
+ let endSide;
238
+ if (vertical) {
239
+ startSide = 'top';
240
+ endSide = 'bottom';
241
+ }
242
+ else if (rtl) {
243
+ startSide = 'right';
244
+ endSide = 'left';
245
+ }
246
+ else {
247
+ startSide = 'left';
248
+ endSide = 'right';
249
+ }
250
+ return { start: sideOffset(startSide), end: sideOffset(endSide) };
74
251
  }
75
- function getDecimalCount(value) {
76
- return (String(value).split('.')[1] || '').length;
252
+ const formatterCache = new Map();
253
+ function getFormatter(locale, options) {
254
+ const key = JSON.stringify({ locale: locale ?? null, options: options ?? null });
255
+ let formatter = formatterCache.get(key);
256
+ if (!formatter) {
257
+ formatter = new Intl.NumberFormat(locale, options);
258
+ formatterCache.set(key, formatter);
259
+ }
260
+ return formatter;
77
261
  }
78
- function roundValue(value, decimalCount) {
79
- const rounder = 10 ** decimalCount;
80
- return Math.round(value * rounder) / rounder;
262
+ /** Formats a number with a cached `Intl.NumberFormat` instance. */
263
+ function formatNumber(value, locale, options) {
264
+ if (value == null) {
265
+ return '';
266
+ }
267
+ return getFormatter(locale, options).format(value);
81
268
  }
82
- function getNextSortedValues(prevValues = [], nextValue, atIndex) {
83
- const nextValues = [...prevValues];
84
- nextValues[atIndex] = nextValue;
85
- return nextValues.sort((a, b) => a - b);
269
+ /** Default `aria-valuetext` for a two-thumb range, or a formatted value when `format` is set. */
270
+ function getDefaultAriaValueText(values, index, format, locale) {
271
+ if (index < 0) {
272
+ return undefined;
273
+ }
274
+ if (values.length === 2) {
275
+ return index === 0
276
+ ? `${formatNumber(values[index], locale, format)} start range`
277
+ : `${formatNumber(values[index], locale, format)} end range`;
278
+ }
279
+ return format ? formatNumber(values[index], locale, format) : undefined;
86
280
  }
87
- const PAGE_KEYS = ['PageUp', 'PageDown'];
88
281
  const ARROW_KEYS = ['ArrowUp', 'ArrowDown', 'ArrowLeft', 'ArrowRight'];
89
- const BACK_KEYS = {
90
- 'from-left': ['Home', 'PageDown', 'ArrowDown', 'ArrowLeft'],
91
- 'from-right': ['Home', 'PageDown', 'ArrowDown', 'ArrowRight'],
92
- 'from-bottom': ['Home', 'PageDown', 'ArrowDown', 'ArrowLeft'],
93
- 'from-top': ['Home', 'PageUp', 'ArrowUp', 'ArrowLeft']
94
- };
95
- function clamp(value, min = Number.NEGATIVE_INFINITY, max = Number.POSITIVE_INFINITY) {
96
- return Math.min(Math.max(value, min), max);
282
+ const COMPOSITE_KEYS = new Set([...ARROW_KEYS, 'Home', 'End']);
283
+ const ALL_KEYS = new Set([...COMPOSITE_KEYS, 'PageUp', 'PageDown']);
284
+ function areValuesEqual(a, b) {
285
+ if (Array.isArray(a) && Array.isArray(b)) {
286
+ return a.length === b.length && a.every((v, i) => v === b[i]);
287
+ }
288
+ return a === b;
97
289
  }
98
290
 
99
- class RdxSliderImplDirective {
291
+ const INTENTIONAL_DRAG_COUNT_THRESHOLD = 2;
292
+ /**
293
+ * The interactive area of the slider. Handles pointer presses and drags on the
294
+ * track, mapping pointer position to a value and moving the closest thumb.
295
+ *
296
+ * @see https://base-ui.com/react/components/slider
297
+ */
298
+ class RdxSliderControl {
100
299
  constructor() {
101
- this.rootContext = inject(RdxSliderRootComponent);
102
- this.slideStart = output();
103
- this.slideMove = output();
104
- this.slideEnd = output();
105
- this.homeKeyDown = output();
106
- this.endKeyDown = output();
107
- this.stepKeyDown = output();
300
+ this.root = injectSliderRootContext();
301
+ this.elementRef = inject(ElementRef);
302
+ this.document = inject(DOCUMENT);
303
+ this.styles = null;
304
+ this.moveCount = 0;
305
+ this.currentInteractionValue = null;
306
+ this.onMove = (event) => this.handleMove(event);
307
+ this.onUp = (event) => this.handleUp(event);
308
+ this.onCancel = (event) => this.handleUp(event);
309
+ this.root.controlRef.set(this.elementRef.nativeElement);
310
+ inject(DestroyRef).onDestroy(() => this.stopListening());
108
311
  }
109
- onKeyDown(event) {
110
- const keyEvent = event;
111
- if (keyEvent.key === 'Home') {
112
- this.homeKeyDown.emit(keyEvent);
113
- // Prevent scrolling to page start
114
- event.preventDefault();
312
+ onPointerDown(event) {
313
+ const control = this.elementRef.nativeElement;
314
+ if (this.root.isDisabled() || event.defaultPrevented || event.button !== 0) {
315
+ return;
115
316
  }
116
- else if (keyEvent.key === 'End') {
117
- this.endKeyDown.emit(keyEvent);
118
- // Prevent scrolling to page end
119
- event.preventDefault();
317
+ const target = event.target;
318
+ if (!target) {
319
+ return;
120
320
  }
121
- else if (PAGE_KEYS.concat(ARROW_KEYS).includes(keyEvent.key)) {
122
- this.stepKeyDown.emit(keyEvent);
123
- // Prevent scrolling for directional key presses
124
- event.preventDefault();
321
+ // Suppress the nested range input's native click-to-set and drag so the
322
+ // control fully owns pointer interaction (otherwise releasing on a thumb
323
+ // fires a native change that snaps the value to the press point inside
324
+ // the thumb-sized input). Focus is restored manually via focusThumb.
325
+ event.preventDefault();
326
+ this.styles = this.document.defaultView?.getComputedStyle(control) ?? null;
327
+ this.startPressing({ x: event.clientX, y: event.clientY });
328
+ const finger = this.getFingerState({ x: event.clientX, y: event.clientY });
329
+ if (finger == null) {
330
+ return;
125
331
  }
332
+ this.root.setDragging(true);
333
+ // Pressing directly on a thumb sets a center offset; only a rail press changes value on down.
334
+ if (this.root.pressedThumbCenterOffset == null) {
335
+ this.setValueFromPointer(finger, 'track-press');
336
+ }
337
+ this.root.focusThumb(finger.thumbIndex);
338
+ control.setPointerCapture(event.pointerId);
339
+ this.moveCount = 0;
340
+ this.document.addEventListener('pointermove', this.onMove);
341
+ this.document.addEventListener('pointerup', this.onUp, { once: true });
342
+ this.document.addEventListener('pointercancel', this.onCancel, { once: true });
126
343
  }
127
- onPointerDown(event) {
128
- const pointerEvent = event;
129
- const target = event.target;
130
- target.setPointerCapture(pointerEvent.pointerId);
131
- // Prevent browser focus behaviour because we focus a thumb manually when values change.
132
- event.preventDefault();
133
- // Touch devices have a delay before focusing so won't focus if touch immediately moves
134
- // away from target (sliding). We want thumb to focus regardless.
135
- if (this.rootContext.thumbElements.includes(target)) {
136
- target.focus();
344
+ handleMove(event) {
345
+ this.moveCount += 1;
346
+ if (event.buttons === 0) {
347
+ this.handleUp(event);
348
+ return;
349
+ }
350
+ const finger = this.getFingerState({ x: event.clientX, y: event.clientY });
351
+ if (finger == null) {
352
+ return;
353
+ }
354
+ if (validateMinimumDistance(finger.value, this.root.step(), this.root.minStepsBetweenValues())) {
355
+ if (!this.root.dragging() && this.moveCount > INTENTIONAL_DRAG_COUNT_THRESHOLD) {
356
+ this.root.setDragging(true);
357
+ }
358
+ const applied = this.setValueFromPointer(finger, 'drag');
359
+ if (applied && finger.didSwap) {
360
+ this.root.focusThumb(finger.thumbIndex);
361
+ }
362
+ }
363
+ }
364
+ handleUp(event) {
365
+ this.root.setActive(-1);
366
+ this.root.setDragging(false);
367
+ this.root.pressedThumbCenterOffset = null;
368
+ this.root.pressedInput = null;
369
+ if (this.currentInteractionValue != null) {
370
+ this.root.commitValue();
371
+ }
372
+ const control = this.elementRef.nativeElement;
373
+ if (control.hasPointerCapture?.(event.pointerId)) {
374
+ control.releasePointerCapture(event.pointerId);
375
+ }
376
+ this.root.resetPressedThumb();
377
+ this.root.pressedValues = null;
378
+ this.currentInteractionValue = null;
379
+ this.stopListening();
380
+ }
381
+ stopListening() {
382
+ this.document.removeEventListener('pointermove', this.onMove);
383
+ this.document.removeEventListener('pointerup', this.onUp);
384
+ this.document.removeEventListener('pointercancel', this.onCancel);
385
+ }
386
+ startPressing(finger) {
387
+ const values = this.root.values();
388
+ const range = this.root.range();
389
+ this.root.pressedValues = range ? values.slice() : null;
390
+ this.currentInteractionValue = null;
391
+ const pressedThumbIndex = this.root.pressedThumbIndex;
392
+ let closestThumbIndex = pressedThumbIndex;
393
+ if (pressedThumbIndex > -1 && pressedThumbIndex < values.length) {
394
+ // Pressed directly on a thumb sitting on max — walk left over stacked max thumbs.
395
+ if (values[pressedThumbIndex] === this.root.max()) {
396
+ let c = pressedThumbIndex;
397
+ while (c > 0 && values[c - 1] === this.root.max()) {
398
+ c -= 1;
399
+ }
400
+ closestThumbIndex = c;
401
+ }
137
402
  }
138
403
  else {
139
- this.slideStart.emit(pointerEvent);
404
+ // Pressed on the rail — find the nearest enabled thumb by midpoint distance.
405
+ const axis = this.root.orientation() === 'horizontal' ? 'x' : 'y';
406
+ const thumbs = this.root.thumbList();
407
+ let minDistance;
408
+ closestThumbIndex = -1;
409
+ for (let i = 0; i < thumbs.length; i += 1) {
410
+ const thumb = thumbs[i];
411
+ if (thumb.disabled()) {
412
+ continue;
413
+ }
414
+ const midpoint = getMidpoint(thumb.element);
415
+ const distance = Math.abs(finger[axis] - midpoint[axis]);
416
+ if (minDistance === undefined || distance <= minDistance) {
417
+ closestThumbIndex = i;
418
+ minDistance = distance;
419
+ }
420
+ }
421
+ }
422
+ if (closestThumbIndex > -1 && closestThumbIndex !== pressedThumbIndex) {
423
+ this.root.pressedThumbIndex = closestThumbIndex;
424
+ this.root.pressedInput = this.root.thumbList()[closestThumbIndex]?.inputElement ?? null;
140
425
  }
141
426
  }
142
- onPointerMove(event) {
143
- const pointerEvent = event;
144
- const target = event.target;
145
- if (target.hasPointerCapture(pointerEvent.pointerId)) {
146
- this.slideMove.emit(pointerEvent);
427
+ setValueFromPointer(finger, reason) {
428
+ const nextValues = Array.isArray(finger.value) ? finger.value : [finger.value];
429
+ const applied = this.root.setValue(nextValues, reason);
430
+ if (applied) {
431
+ this.currentInteractionValue = finger.value;
432
+ if (finger.didSwap) {
433
+ this.root.pressedThumbIndex = finger.thumbIndex;
434
+ this.root.pressedInput = this.root.thumbList()[finger.thumbIndex]?.inputElement ?? null;
435
+ }
147
436
  }
437
+ return applied;
148
438
  }
149
- onPointerUp(event) {
150
- const pointerEvent = event;
151
- const target = event.target;
152
- if (target.hasPointerCapture(pointerEvent.pointerId)) {
153
- target.releasePointerCapture(pointerEvent.pointerId);
154
- this.slideEnd.emit(pointerEvent);
439
+ /** Projects a pointer position onto the track and resolves it to a value (+ collision). */
440
+ getFingerState(finger) {
441
+ const control = this.root.controlRef();
442
+ const values = this.root.values();
443
+ const range = this.root.range();
444
+ const thumbIndex = this.root.pressedThumbIndex;
445
+ const vertical = this.root.orientation() === 'vertical';
446
+ const rtl = this.root.dir() === 'rtl';
447
+ const min = this.root.min();
448
+ const max = this.root.max();
449
+ const step = this.root.step();
450
+ if (!control || (!range && (thumbIndex < 0 || thumbIndex >= values.length))) {
451
+ return null;
452
+ }
453
+ const { width, height, bottom, left, right } = control.getBoundingClientRect();
454
+ const controlOffset = getControlOffset(this.styles, vertical, rtl);
455
+ const controlSize = (vertical ? height : width) - controlOffset.start - controlOffset.end;
456
+ // A collapsed/unmeasurable track would divide by zero and yield NaN values.
457
+ if (!(controlSize > 0)) {
458
+ return null;
459
+ }
460
+ const thumbCenterOffset = this.root.pressedThumbCenterOffset ?? 0;
461
+ const fingerX = finger.x - thumbCenterOffset;
462
+ const fingerY = finger.y - thumbCenterOffset;
463
+ const valueSize = vertical
464
+ ? bottom - fingerY - controlOffset.end
465
+ : (rtl ? right - fingerX : fingerX - left) - controlOffset.start;
466
+ const valueRescaled = clamp(valueSize / controlSize, 0, 1);
467
+ let newValue = (max - min) * valueRescaled + min;
468
+ newValue = roundValueToStep(newValue, step, min);
469
+ newValue = clamp(newValue, min, max);
470
+ if (!range) {
471
+ return { value: newValue, thumbIndex, didSwap: false };
155
472
  }
473
+ if (thumbIndex < 0) {
474
+ return null;
475
+ }
476
+ return resolveThumbCollision({
477
+ behavior: this.root.thumbCollisionBehavior(),
478
+ values,
479
+ currentValues: values,
480
+ initialValues: this.root.pressedValues,
481
+ pressedIndex: thumbIndex,
482
+ nextValue: newValue,
483
+ min,
484
+ max,
485
+ step,
486
+ minStepsBetweenValues: this.root.minStepsBetweenValues()
487
+ });
156
488
  }
157
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxSliderImplDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
158
- static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.2.9", type: RdxSliderImplDirective, isStandalone: true, selector: "[rdxSliderImpl]", outputs: { slideStart: "slideStart", slideMove: "slideMove", slideEnd: "slideEnd", homeKeyDown: "homeKeyDown", endKeyDown: "endKeyDown", stepKeyDown: "stepKeyDown" }, host: { attributes: { "role": "slider", "tabindex": "0" }, listeners: { "keydown": "onKeyDown($event)", "pointerdown": "onPointerDown($event)", "pointermove": "onPointerMove($event)", "pointerup": "onPointerUp($event)" } }, ngImport: i0 }); }
489
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxSliderControl, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
490
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.2.9", type: RdxSliderControl, isStandalone: true, selector: "div[rdxSliderControl]", host: { listeners: { "pointerdown": "onPointerDown($event)" }, properties: { "attr.data-orientation": "root.orientation()", "attr.data-disabled": "root.isDisabled() ? \"\" : undefined", "attr.data-dragging": "root.dragging() ? \"\" : undefined" } }, exportAs: ["rdxSliderControl"], ngImport: i0 }); }
159
491
  }
160
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxSliderImplDirective, decorators: [{
492
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxSliderControl, decorators: [{
161
493
  type: Directive,
162
494
  args: [{
163
- selector: '[rdxSliderImpl]',
495
+ selector: 'div[rdxSliderControl]',
496
+ exportAs: 'rdxSliderControl',
164
497
  host: {
165
- role: 'slider',
166
- tabindex: '0',
167
- '(keydown)': 'onKeyDown($event)',
168
- '(pointerdown)': 'onPointerDown($event)',
169
- '(pointermove)': 'onPointerMove($event)',
170
- '(pointerup)': 'onPointerUp($event)'
498
+ '[attr.data-orientation]': 'root.orientation()',
499
+ '[attr.data-disabled]': 'root.isDisabled() ? "" : undefined',
500
+ '[attr.data-dragging]': 'root.dragging() ? "" : undefined',
501
+ '(pointerdown)': 'onPointerDown($event)'
171
502
  }
172
503
  }]
173
- }], propDecorators: { slideStart: [{ type: i0.Output, args: ["slideStart"] }], slideMove: [{ type: i0.Output, args: ["slideMove"] }], slideEnd: [{ type: i0.Output, args: ["slideEnd"] }], homeKeyDown: [{ type: i0.Output, args: ["homeKeyDown"] }], endKeyDown: [{ type: i0.Output, args: ["endKeyDown"] }], stepKeyDown: [{ type: i0.Output, args: ["stepKeyDown"] }] } });
504
+ }], ctorParameters: () => [] });
174
505
 
175
- class RdxSliderHorizontalComponent {
506
+ /**
507
+ * Visualises the portion of the track between the slider's minimum (or the first
508
+ * thumb in a range) and the active value.
509
+ *
510
+ * @see https://base-ui.com/react/components/slider
511
+ */
512
+ class RdxSliderIndicator {
176
513
  constructor() {
177
- this.rootContext = inject(RdxSliderRootComponent);
178
- this.dir = 'ltr';
179
- this.inverted = input(false, { ...(ngDevMode ? { debugName: "inverted" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
180
- this.min = 0;
181
- this.max = 100;
182
- this.className = '';
183
- this.slideStart = output();
184
- this.slideMove = output();
185
- this.slideEnd = output();
186
- this.stepKeyDown = output();
187
- this.endKeyDown = output();
188
- this.homeKeyDown = output();
189
- this.sliderElement = viewChild('sliderElement', ...(ngDevMode ? [{ debugName: "sliderElement" }] : /* istanbul ignore next */ []));
190
- this.rect = signal(undefined, ...(ngDevMode ? [{ debugName: "rect" }] : /* istanbul ignore next */ []));
191
- }
192
- onSlideStart(event) {
193
- const value = this.getValueFromPointer(event.clientX);
194
- this.slideStart.emit(value);
195
- }
196
- onSlideMove(event) {
197
- const value = this.getValueFromPointer(event.clientX);
198
- this.slideMove.emit(value);
199
- }
200
- onSlideEnd() {
201
- this.rect.set(undefined);
202
- this.slideEnd.emit();
203
- }
204
- onStepKeyDown(event) {
205
- const slideDirection = this.rootContext.isSlidingFromLeft() ? 'from-left' : 'from-right';
206
- const isBackKey = BACK_KEYS[slideDirection].includes(event.key);
207
- this.stepKeyDown.emit({ event, direction: isBackKey ? -1 : 1 });
208
- }
209
- getValueFromPointer(pointerPosition) {
210
- this.rect.set(this.sliderElement()?.nativeElement.getBoundingClientRect());
211
- const rect = this.rect();
212
- if (!rect)
213
- return 0;
214
- const input = [0, rect.width];
215
- const output = this.rootContext.isSlidingFromLeft()
216
- ? [this.min, this.max]
217
- : [this.max, this.min];
218
- const value = linearScale(input, output);
219
- this.rect.set(rect);
220
- return value(pointerPosition - rect.left);
221
- }
222
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxSliderHorizontalComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
223
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "21.2.9", type: RdxSliderHorizontalComponent, isStandalone: true, selector: "rdx-slider-horizontal", inputs: { dir: { classPropertyName: "dir", publicName: "dir", isSignal: false, isRequired: false, transformFunction: null }, inverted: { classPropertyName: "inverted", publicName: "inverted", isSignal: true, isRequired: false, transformFunction: null }, min: { classPropertyName: "min", publicName: "min", isSignal: false, isRequired: false, transformFunction: null }, max: { classPropertyName: "max", publicName: "max", isSignal: false, isRequired: false, transformFunction: null }, className: { classPropertyName: "className", publicName: "className", isSignal: false, isRequired: false, transformFunction: null } }, outputs: { slideStart: "slideStart", slideMove: "slideMove", slideEnd: "slideEnd", stepKeyDown: "stepKeyDown", endKeyDown: "endKeyDown", homeKeyDown: "homeKeyDown" }, viewQueries: [{ propertyName: "sliderElement", first: true, predicate: ["sliderElement"], descendants: true, isSignal: true }], ngImport: i0, template: `
224
- <span
225
- #sliderElement
226
- [class]="className"
227
- [attr.data-orientation]="'horizontal'"
228
- [style]="{ '--rdx-slider-thumb-transform': 'translateX(-50%)' }"
229
- (slideStart)="onSlideStart($event)"
230
- (slideMove)="onSlideMove($event)"
231
- (slideEnd)="onSlideEnd()"
232
- (stepKeyDown)="onStepKeyDown($event)"
233
- (endKeyDown)="endKeyDown.emit($event)"
234
- (homeKeyDown)="homeKeyDown.emit($event)"
235
- rdxSliderImpl
236
- >
237
- <ng-content />
238
- </span>
239
- `, isInline: true, dependencies: [{ kind: "directive", type: RdxSliderImplDirective, selector: "[rdxSliderImpl]", outputs: ["slideStart", "slideMove", "slideEnd", "homeKeyDown", "endKeyDown", "stepKeyDown"] }] }); }
514
+ this.root = injectSliderRootContext();
515
+ this.indicatorStyle = computed(() => {
516
+ const vertical = this.root.orientation() === 'vertical';
517
+ const range = this.root.range();
518
+ const values = this.root.values();
519
+ const min = this.root.min();
520
+ const max = this.root.max();
521
+ const startEdge = vertical ? 'bottom' : 'inset-inline-start';
522
+ const mainSide = vertical ? 'height' : 'width';
523
+ const crossSide = vertical ? 'width' : 'height';
524
+ const start = valueToPercent(values[0], min, max);
525
+ const end = valueToPercent(values[values.length - 1], min, max);
526
+ const styles = {
527
+ position: vertical ? 'absolute' : 'relative',
528
+ [crossSide]: 'inherit'
529
+ };
530
+ if (!range) {
531
+ styles[startEdge] = 0;
532
+ styles[mainSide] = `${start}%`;
533
+ return styles;
534
+ }
535
+ styles[startEdge] = `${start}%`;
536
+ styles[mainSide] = `${end - start}%`;
537
+ return styles;
538
+ }, ...(ngDevMode ? [{ debugName: "indicatorStyle" }] : /* istanbul ignore next */ []));
539
+ }
540
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxSliderIndicator, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
541
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.2.9", type: RdxSliderIndicator, isStandalone: true, selector: "div[rdxSliderIndicator]", host: { properties: { "style": "indicatorStyle()", "attr.data-orientation": "root.orientation()", "attr.data-disabled": "root.isDisabled() ? \"\" : undefined", "attr.data-dragging": "root.dragging() ? \"\" : undefined" } }, exportAs: ["rdxSliderIndicator"], ngImport: i0 }); }
240
542
  }
241
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxSliderHorizontalComponent, decorators: [{
242
- type: Component,
543
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxSliderIndicator, decorators: [{
544
+ type: Directive,
243
545
  args: [{
244
- selector: 'rdx-slider-horizontal',
245
- imports: [RdxSliderImplDirective],
246
- template: `
247
- <span
248
- #sliderElement
249
- [class]="className"
250
- [attr.data-orientation]="'horizontal'"
251
- [style]="{ '--rdx-slider-thumb-transform': 'translateX(-50%)' }"
252
- (slideStart)="onSlideStart($event)"
253
- (slideMove)="onSlideMove($event)"
254
- (slideEnd)="onSlideEnd()"
255
- (stepKeyDown)="onStepKeyDown($event)"
256
- (endKeyDown)="endKeyDown.emit($event)"
257
- (homeKeyDown)="homeKeyDown.emit($event)"
258
- rdxSliderImpl
259
- >
260
- <ng-content />
261
- </span>
262
- `
546
+ selector: 'div[rdxSliderIndicator]',
547
+ exportAs: 'rdxSliderIndicator',
548
+ host: {
549
+ '[style]': 'indicatorStyle()',
550
+ '[attr.data-orientation]': 'root.orientation()',
551
+ '[attr.data-disabled]': 'root.isDisabled() ? "" : undefined',
552
+ '[attr.data-dragging]': 'root.dragging() ? "" : undefined'
553
+ }
263
554
  }]
264
- }], propDecorators: { dir: [{
265
- type: Input
266
- }], inverted: [{ type: i0.Input, args: [{ isSignal: true, alias: "inverted", required: false }] }], min: [{
267
- type: Input
268
- }], max: [{
269
- type: Input
270
- }], className: [{
271
- type: Input
272
- }], slideStart: [{ type: i0.Output, args: ["slideStart"] }], slideMove: [{ type: i0.Output, args: ["slideMove"] }], slideEnd: [{ type: i0.Output, args: ["slideEnd"] }], stepKeyDown: [{ type: i0.Output, args: ["stepKeyDown"] }], endKeyDown: [{ type: i0.Output, args: ["endKeyDown"] }], homeKeyDown: [{ type: i0.Output, args: ["homeKeyDown"] }], sliderElement: [{ type: i0.ViewChild, args: ['sliderElement', { isSignal: true }] }] } });
273
-
274
- class RdxSliderOrientationContextService {
275
- constructor() {
276
- this.contextSignal = signal({
277
- startEdge: 'left',
278
- endEdge: 'right',
279
- direction: 1,
280
- size: 'width'
281
- }, ...(ngDevMode ? [{ debugName: "contextSignal" }] : /* istanbul ignore next */ []));
282
- }
283
- get context() {
284
- return this.contextSignal();
285
- }
286
- updateContext(context) {
287
- this.contextSignal.update((current) => ({
288
- ...current,
289
- ...context
290
- }));
291
- }
292
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxSliderOrientationContextService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
293
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxSliderOrientationContextService }); }
294
- }
295
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxSliderOrientationContextService, decorators: [{
296
- type: Injectable
297
555
  }] });
298
556
 
299
- class RdxSliderVerticalComponent {
300
- constructor() {
301
- this.rootContext = inject(RdxSliderRootComponent);
302
- this.dir = 'ltr';
303
- this.inverted = input(false, { ...(ngDevMode ? { debugName: "inverted" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
304
- this.min = 0;
305
- this.max = 100;
306
- this.className = '';
307
- this.slideStart = output();
308
- this.slideMove = output();
309
- this.slideEnd = output();
310
- this.stepKeyDown = output();
311
- this.endKeyDown = output();
312
- this.homeKeyDown = output();
313
- this.sliderElement = viewChild('sliderElement', ...(ngDevMode ? [{ debugName: "sliderElement" }] : /* istanbul ignore next */ []));
314
- this.rect = signal(undefined, ...(ngDevMode ? [{ debugName: "rect" }] : /* istanbul ignore next */ []));
315
- }
316
- onSlideStart(event) {
317
- const value = this.getValueFromPointer(event.clientY);
318
- this.slideStart.emit(value);
319
- }
320
- onSlideMove(event) {
321
- const value = this.getValueFromPointer(event.clientY);
322
- this.slideMove.emit(value);
323
- }
324
- onSlideEnd() {
325
- this.rect.set(undefined);
326
- this.slideEnd.emit();
327
- }
328
- onStepKeyDown(event) {
329
- const slideDirection = this.rootContext.isSlidingFromBottom() ? 'from-bottom' : 'from-top';
330
- const isBackKey = BACK_KEYS[slideDirection].includes(event.key);
331
- this.stepKeyDown.emit({ event, direction: isBackKey ? -1 : 1 });
332
- }
333
- getValueFromPointer(pointerPosition) {
334
- this.rect.set(this.sliderElement()?.nativeElement.getBoundingClientRect());
335
- const rect = this.rect();
336
- if (!rect)
337
- return 0;
338
- const input = [0, rect.height];
339
- const output = this.rootContext.isSlidingFromBottom()
340
- ? [this.max, this.min]
341
- : [this.min, this.max];
342
- const value = linearScale(input, output);
343
- this.rect.set(rect);
344
- return value(pointerPosition - rect.top);
345
- }
346
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxSliderVerticalComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
347
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "21.2.9", type: RdxSliderVerticalComponent, isStandalone: true, selector: "rdx-slider-vertical", inputs: { dir: { classPropertyName: "dir", publicName: "dir", isSignal: false, isRequired: false, transformFunction: null }, inverted: { classPropertyName: "inverted", publicName: "inverted", isSignal: true, isRequired: false, transformFunction: null }, min: { classPropertyName: "min", publicName: "min", isSignal: false, isRequired: false, transformFunction: null }, max: { classPropertyName: "max", publicName: "max", isSignal: false, isRequired: false, transformFunction: null }, className: { classPropertyName: "className", publicName: "className", isSignal: false, isRequired: false, transformFunction: null } }, outputs: { slideStart: "slideStart", slideMove: "slideMove", slideEnd: "slideEnd", stepKeyDown: "stepKeyDown", endKeyDown: "endKeyDown", homeKeyDown: "homeKeyDown" }, viewQueries: [{ propertyName: "sliderElement", first: true, predicate: ["sliderElement"], descendants: true, isSignal: true }], ngImport: i0, template: `
348
- <span
349
- #sliderElement
350
- [class]="className"
351
- [attr.data-orientation]="'vertical'"
352
- [style]="{ '--rdx-slider-thumb-transform': 'translateY(-50%)' }"
353
- (slideStart)="onSlideStart($event)"
354
- (slideMove)="onSlideMove($event)"
355
- (slideEnd)="onSlideEnd()"
356
- (stepKeyDown)="onStepKeyDown($event)"
357
- (endKeyDown)="endKeyDown.emit($event)"
358
- (homeKeyDown)="homeKeyDown.emit($event)"
359
- rdxSliderImpl
360
- >
361
- <ng-content />
362
- </span>
363
- `, isInline: true, dependencies: [{ kind: "directive", type: RdxSliderImplDirective, selector: "[rdxSliderImpl]", outputs: ["slideStart", "slideMove", "slideEnd", "homeKeyDown", "endKeyDown", "stepKeyDown"] }] }); }
557
+ function sortByDomOrder(list) {
558
+ return list.slice().sort((a, b) => {
559
+ const position = a.element.compareDocumentPosition(b.element);
560
+ if (position & Node.DOCUMENT_POSITION_FOLLOWING) {
561
+ return -1;
562
+ }
563
+ if (position & Node.DOCUMENT_POSITION_PRECEDING) {
564
+ return 1;
565
+ }
566
+ return 0;
567
+ });
364
568
  }
365
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxSliderVerticalComponent, decorators: [{
366
- type: Component,
367
- args: [{
368
- selector: 'rdx-slider-vertical',
369
- imports: [RdxSliderImplDirective],
370
- template: `
371
- <span
372
- #sliderElement
373
- [class]="className"
374
- [attr.data-orientation]="'vertical'"
375
- [style]="{ '--rdx-slider-thumb-transform': 'translateY(-50%)' }"
376
- (slideStart)="onSlideStart($event)"
377
- (slideMove)="onSlideMove($event)"
378
- (slideEnd)="onSlideEnd()"
379
- (stepKeyDown)="onStepKeyDown($event)"
380
- (endKeyDown)="endKeyDown.emit($event)"
381
- (homeKeyDown)="homeKeyDown.emit($event)"
382
- rdxSliderImpl
383
- >
384
- <ng-content />
385
- </span>
386
- `
387
- }]
388
- }], propDecorators: { dir: [{
389
- type: Input
390
- }], inverted: [{ type: i0.Input, args: [{ isSignal: true, alias: "inverted", required: false }] }], min: [{
391
- type: Input
392
- }], max: [{
393
- type: Input
394
- }], className: [{
395
- type: Input
396
- }], slideStart: [{ type: i0.Output, args: ["slideStart"] }], slideMove: [{ type: i0.Output, args: ["slideMove"] }], slideEnd: [{ type: i0.Output, args: ["slideEnd"] }], stepKeyDown: [{ type: i0.Output, args: ["stepKeyDown"] }], endKeyDown: [{ type: i0.Output, args: ["endKeyDown"] }], homeKeyDown: [{ type: i0.Output, args: ["homeKeyDown"] }], sliderElement: [{ type: i0.ViewChild, args: ['sliderElement', { isSignal: true }] }] } });
397
-
398
569
  /**
399
- * @group Components
570
+ * Groups all parts of the slider and owns its state, value-change logic and
571
+ * thumb registration. A single directive drives both orientations — there are no
572
+ * separate horizontal/vertical components.
573
+ *
574
+ * @see https://base-ui.com/react/components/slider
400
575
  */
401
- class RdxSliderRootComponent {
576
+ class RdxSliderRoot {
402
577
  constructor() {
403
578
  /** @ignore */
404
- this.orientationContext = inject(RdxSliderOrientationContextService);
579
+ this.cva = injectControlValueAccessor();
580
+ this.id = input(injectId('rdx-slider-'), ...(ngDevMode ? [{ debugName: "id" }] : /* istanbul ignore next */ []));
405
581
  /**
406
- * The minimum value for the range.
407
- *
408
- * @group Props
409
- * @defaultValue 0
582
+ * The minimum value of the slider.
583
+ * @default 0
410
584
  */
411
585
  this.min = input(0, { ...(ngDevMode ? { debugName: "min" } : /* istanbul ignore next */ {}), transform: numberAttribute });
412
586
  /**
413
- * The maximum value for the range.
414
- *
415
- * @group Props
416
- * @defaultValue 100
587
+ * The maximum value of the slider.
588
+ * @default 100
417
589
  */
418
590
  this.max = input(100, { ...(ngDevMode ? { debugName: "max" } : /* istanbul ignore next */ {}), transform: numberAttribute });
419
591
  /**
420
- * The stepping interval.
421
- *
422
- * @group Props
423
- * @defaultValue 1
592
+ * The granularity with which the value can change through user interaction.
593
+ * @default 1
424
594
  */
425
595
  this.step = input(1, { ...(ngDevMode ? { debugName: "step" } : /* istanbul ignore next */ {}), transform: numberAttribute });
426
596
  /**
427
- * The minimum permitted steps between multiple thumbs.
428
- *
429
- * @group Props
430
- * @defaultValue 0
597
+ * The granularity with which the value changes on Page Up / Page Down keys and Shift + Arrow keys.
598
+ * @default 10
431
599
  */
432
- this.minStepsBetweenThumbs = input(0, { ...(ngDevMode ? { debugName: "minStepsBetweenThumbs" } : /* istanbul ignore next */ {}), transform: numberAttribute });
600
+ this.largeStep = input(10, { ...(ngDevMode ? { debugName: "largeStep" } : /* istanbul ignore next */ {}), transform: numberAttribute });
433
601
  /**
434
- * The orientation of the slider.
435
- *
436
- * @group Props
437
- * @defaultValue 'horizontal'
602
+ * The minimum number of steps that must separate two thumbs in a range slider.
603
+ * @default 0
438
604
  */
439
- this.orientation = input('horizontal', ...(ngDevMode ? [{ debugName: "orientation" }] : /* istanbul ignore next */ []));
605
+ this.minStepsBetweenValues = input(0, { ...(ngDevMode ? { debugName: "minStepsBetweenValues" } : /* istanbul ignore next */ {}), transform: numberAttribute });
440
606
  /**
441
- * When true, prevents the user from interacting with the slider.
442
- *
443
- * @group Props
444
- * @defaultValue false
445
- */
446
- this.disabled = input(false, { ...(ngDevMode ? { debugName: "disabled" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
447
- /**
448
- * Whether the slider is visually inverted.
449
- *
450
- * @group Props
451
- * @defaultValue false
607
+ * The orientation of the slider.
608
+ * @default 'horizontal'
452
609
  */
453
- this.inverted = input(false, { ...(ngDevMode ? { debugName: "inverted" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
610
+ this.orientation = input('horizontal', ...(ngDevMode ? [{ debugName: "orientation" }] : /* istanbul ignore next */ []));
454
611
  /**
455
- * The reading direction of the combobox when applicable.
456
- *
457
- * @group Props
458
- * @defaultValue 'ltr'
612
+ * The reading direction. Mirrors the horizontal axis when set to `'rtl'`.
613
+ * @default 'ltr'
459
614
  */
460
615
  this.dir = input('ltr', ...(ngDevMode ? [{ debugName: "dir" }] : /* istanbul ignore next */ []));
461
- this.className = '';
462
- /**
463
- * Style class of the component.
464
- *
465
- * @group Props
466
- */
467
- this.styleClass = input(...(ngDevMode ? [undefined, { debugName: "styleClass" }] : /* istanbul ignore next */ []));
468
616
  /**
469
- * The controlled value of the slider.
470
- *
471
- * @group Props
617
+ * How thumbs behave when they meet in a range slider.
618
+ * @default 'push'
472
619
  */
473
- this.modelValue = model([0], ...(ngDevMode ? [{ debugName: "modelValue" }] : /* istanbul ignore next */ []));
620
+ this.thumbCollisionBehavior = input('push', ...(ngDevMode ? [{ debugName: "thumbCollisionBehavior" }] : /* istanbul ignore next */ []));
621
+ /** Options forwarded to `Intl.NumberFormat` when displaying and announcing values. */
622
+ this.format = input(...(ngDevMode ? [undefined, { debugName: "format" }] : /* istanbul ignore next */ []));
623
+ /** Locale used for value formatting. */
624
+ this.locale = input(...(ngDevMode ? [undefined, { debugName: "locale" }] : /* istanbul ignore next */ []));
625
+ /** Name of the hidden inputs rendered by each thumb, for form submission. */
626
+ this.name = input(...(ngDevMode ? [undefined, { debugName: "name" }] : /* istanbul ignore next */ []));
627
+ /** Id of the form the slider belongs to. */
628
+ this.form = input(...(ngDevMode ? [undefined, { debugName: "form" }] : /* istanbul ignore next */ []));
474
629
  /**
475
- * Event handler called when the slider value changes.
476
- *
477
- * @group Emits
630
+ * When `true`, the user cannot interact with the slider.
631
+ * @default false
478
632
  */
479
- this.valueChange = output();
480
- /**
481
- * Event handler called when the value changes at the end of an interaction.
482
- *
483
- * Useful when you only need to capture a final value e.g. to update a backend service.
484
- *
485
- * @group Emits
486
- */
487
- this.valueCommit = output();
633
+ this.disabled = input(false, { ...(ngDevMode ? { debugName: "disabled" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
634
+ /** The uncontrolled value of the slider when it is initially rendered. */
635
+ this.defaultValue = input(...(ngDevMode ? [undefined, { debugName: "defaultValue" }] : /* istanbul ignore next */ []));
636
+ /** The controlled value of the slider. Use with `(onValueChange)` or two-way `[(value)]`. */
637
+ this.value = model(...(ngDevMode ? [undefined, { debugName: "value" }] : /* istanbul ignore next */ []));
638
+ this.ariaLabelledBy = input(undefined, { ...(ngDevMode ? { debugName: "ariaLabelledBy" } : /* istanbul ignore next */ {}), alias: 'aria-labelledby' });
639
+ /** Emitted when the value changes (during interaction). */
640
+ this.onValueChange = output();
641
+ /** Emitted when interaction ends, with the final value — useful for committing to a backend. */
642
+ this.onValueCommitted = output();
488
643
  /** @ignore */
489
- this.valueIndexToChange = model(0, ...(ngDevMode ? [{ debugName: "valueIndexToChange" }] : /* istanbul ignore next */ []));
644
+ this.controlRef = signal(null, ...(ngDevMode ? [{ debugName: "controlRef" }] : /* istanbul ignore next */ []));
645
+ /** @ignore Active thumb index (-1 when none). */
646
+ this.active = signal(-1, ...(ngDevMode ? [{ debugName: "active" }] : /* istanbul ignore next */ []));
647
+ /** @ignore Last thumb index that was focused/used, drives z-index stacking. */
648
+ this.lastUsedThumbIndex = signal(-1, ...(ngDevMode ? [{ debugName: "lastUsedThumbIndex" }] : /* istanbul ignore next */ []));
649
+ /** @ignore Whether a pointer drag is in progress. */
650
+ this.dragging = signal(false, ...(ngDevMode ? [{ debugName: "dragging" }] : /* istanbul ignore next */ []));
651
+ /** @ignore Pointer-drag scratch state (not reactive). */
652
+ this.pressedThumbIndex = -1;
490
653
  /** @ignore */
491
- this.valuesBeforeSlideStart = model([], ...(ngDevMode ? [{ debugName: "valuesBeforeSlideStart" }] : /* istanbul ignore next */ []));
654
+ this.pressedThumbCenterOffset = null;
492
655
  /** @ignore */
493
- this.isSlidingFromLeft = computed(() => (this.dir() === 'ltr' && !this.inverted()) || (this.dir() !== 'ltr' && this.inverted()), ...(ngDevMode ? [{ debugName: "isSlidingFromLeft" }] : /* istanbul ignore next */ []));
656
+ this.pressedInput = null;
657
+ /** @ignore Snapshot of values at drag start, the baseline for push/swap. */
658
+ this.pressedValues = null;
494
659
  /** @ignore */
495
- this.isSlidingFromBottom = computed(() => !this.inverted(), ...(ngDevMode ? [{ debugName: "isSlidingFromBottom" }] : /* istanbul ignore next */ []));
660
+ this.lastChangeReason = 'none';
496
661
  /** @ignore */
497
- this.thumbElements = [];
662
+ this.isDisabled = computed(() => !!this.cva.disabled(), ...(ngDevMode ? [{ debugName: "isDisabled" }] : /* istanbul ignore next */ []));
663
+ /** @ignore The current value source (controlled value, else default, else min). */
664
+ this.currentRaw = computed(() => this.cva.value() ?? this.defaultValue() ?? this.min(), ...(ngDevMode ? [{ debugName: "currentRaw" }] : /* istanbul ignore next */ []));
665
+ /** Whether the slider has multiple thumbs (the value is an array). */
666
+ this.range = computed(() => Array.isArray(this.cva.value() ?? this.defaultValue()), ...(ngDevMode ? [{ debugName: "range" }] : /* istanbul ignore next */ []));
667
+ /** The clamped values rendered to the user, sorted ascending for range sliders. */
668
+ this.values = computed(() => {
669
+ const raw = this.currentRaw();
670
+ const min = this.min();
671
+ const max = this.max();
672
+ const arr = (Array.isArray(raw) ? raw.slice() : [raw]).map((v) => clamp(v, min, max));
673
+ return this.range() ? arr.sort(asc) : arr;
674
+ }, ...(ngDevMode ? [{ debugName: "values" }] : /* istanbul ignore next */ []));
675
+ this.thumbs = signal([], ...(ngDevMode ? [{ debugName: "thumbs" }] : /* istanbul ignore next */ []));
676
+ /** Registered thumbs in DOM order. */
677
+ this.thumbList = this.thumbs.asReadonly();
498
678
  }
499
679
  /** @ignore */
500
- ngOnInit() {
501
- const isHorizontal = this.orientation() === 'horizontal';
502
- if (isHorizontal) {
503
- this.orientationContext.updateContext({
504
- direction: this.isSlidingFromLeft() ? 1 : -1,
505
- size: 'width',
506
- startEdge: this.isSlidingFromLeft() ? 'left' : 'right',
507
- endEdge: this.isSlidingFromLeft() ? 'right' : 'left'
508
- });
509
- }
510
- else {
511
- this.orientationContext.updateContext({
512
- direction: this.isSlidingFromBottom() ? -1 : 1,
513
- size: 'height',
514
- startEdge: this.isSlidingFromBottom() ? 'bottom' : 'top',
515
- endEdge: this.isSlidingFromBottom() ? 'top' : 'bottom'
516
- });
517
- }
680
+ registerThumb(thumb) {
681
+ this.thumbs.update((list) => sortByDomOrder([...list, thumb]));
518
682
  }
519
683
  /** @ignore */
520
- onPointerDown() {
521
- this.valuesBeforeSlideStart.set([...this.modelValue()]);
684
+ unregisterThumb(thumb) {
685
+ this.thumbs.update((list) => list.filter((t) => t !== thumb));
522
686
  }
523
687
  /** @ignore */
524
- handleSlideStart(value) {
525
- const closestIndex = getClosestValueIndex(this.modelValue(), value);
526
- this.updateValues(value, closestIndex);
688
+ thumbIndexOf(thumb) {
689
+ return this.thumbList().indexOf(thumb);
527
690
  }
528
691
  /** @ignore */
529
- handleSlideMove(value) {
530
- this.updateValues(value, this.valueIndexToChange());
531
- }
532
- /** @ignore */
533
- handleSlideEnd() {
534
- const prevValue = this.valuesBeforeSlideStart()[this.valueIndexToChange()];
535
- const nextValue = this.modelValue()[this.valueIndexToChange()];
536
- const hasChanged = nextValue !== prevValue;
537
- if (hasChanged) {
538
- this.valueCommit.emit([...this.modelValue()]);
692
+ setActive(index) {
693
+ this.active.set(index);
694
+ if (index !== -1) {
695
+ this.lastUsedThumbIndex.set(index);
539
696
  }
540
697
  }
541
698
  /** @ignore */
542
- handleStepKeyDown(event) {
543
- const stepInDirection = this.step() * event.direction;
544
- const atIndex = this.valueIndexToChange();
545
- const currentValue = this.modelValue()[atIndex];
546
- this.updateValues(currentValue + stepInDirection, atIndex, true);
699
+ focusThumb(index) {
700
+ this.thumbList()[index]?.inputElement?.focus({ preventScroll: true });
547
701
  }
548
702
  /** @ignore */
549
- updateValues(value, atIndex, commit = false) {
550
- const decimalCount = getDecimalCount(this.step());
551
- const snapToStep = roundValue(Math.round((value - this.min()) / this.step()) * this.step() + this.min(), decimalCount);
552
- const nextValue = clamp(snapToStep, this.min(), this.max());
553
- const nextValues = getNextSortedValues(this.modelValue(), nextValue, atIndex);
554
- if (hasMinStepsBetweenValues(nextValues, this.minStepsBetweenThumbs() * this.step())) {
555
- this.valueIndexToChange.set(nextValues.indexOf(nextValue));
556
- const hasChanged = String(nextValues) !== String(this.modelValue());
557
- if (hasChanged) {
558
- this.modelValue.set(nextValues);
559
- this.valueChange.emit([...this.modelValue()]);
560
- this.thumbElements[this.valueIndexToChange()]?.focus();
561
- if (commit) {
562
- this.valueCommit.emit([...this.modelValue()]);
563
- }
564
- }
703
+ formatValue(value) {
704
+ return formatNumber(value, this.locale(), this.format());
705
+ }
706
+ /** @ignore Output value matching the original value shape (number vs array). */
707
+ outputValue() {
708
+ const raw = this.cva.value();
709
+ if (raw !== undefined) {
710
+ return raw;
565
711
  }
712
+ return this.range() ? this.values() : this.values()[0];
566
713
  }
567
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxSliderRootComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
568
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.9", type: RdxSliderRootComponent, isStandalone: true, selector: "rdx-slider", inputs: { 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 }, minStepsBetweenThumbs: { classPropertyName: "minStepsBetweenThumbs", publicName: "minStepsBetweenThumbs", isSignal: true, isRequired: false, transformFunction: null }, orientation: { classPropertyName: "orientation", publicName: "orientation", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, inverted: { classPropertyName: "inverted", publicName: "inverted", isSignal: true, isRequired: false, transformFunction: null }, dir: { classPropertyName: "dir", publicName: "dir", isSignal: true, isRequired: false, transformFunction: null }, className: { classPropertyName: "className", publicName: "className", isSignal: false, isRequired: false, transformFunction: null }, styleClass: { classPropertyName: "styleClass", publicName: "styleClass", isSignal: true, isRequired: false, transformFunction: null }, modelValue: { classPropertyName: "modelValue", publicName: "modelValue", isSignal: true, isRequired: false, transformFunction: null }, valueIndexToChange: { classPropertyName: "valueIndexToChange", publicName: "valueIndexToChange", isSignal: true, isRequired: false, transformFunction: null }, valuesBeforeSlideStart: { classPropertyName: "valuesBeforeSlideStart", publicName: "valuesBeforeSlideStart", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { modelValue: "modelValueChange", valueChange: "valueChange", valueCommit: "valueCommit", valueIndexToChange: "valueIndexToChangeChange", valuesBeforeSlideStart: "valuesBeforeSlideStartChange" }, providers: [RdxSliderOrientationContextService], ngImport: i0, template: `
569
- <ng-template #transclude><ng-content /></ng-template>
570
-
571
- @if (orientation() === 'horizontal') {
572
- <rdx-slider-horizontal
573
- [className]="styleClass() || className"
574
- [min]="min()"
575
- [max]="max()"
576
- [dir]="dir()"
577
- [inverted]="inverted()"
578
- [attr.aria-disabled]="disabled()"
579
- [attr.data-disabled]="disabled() ? '' : undefined"
580
- (pointerdown)="onPointerDown()"
581
- (slideStart)="handleSlideStart($event)"
582
- (slideMove)="handleSlideMove($event)"
583
- (slideEnd)="handleSlideEnd()"
584
- (homeKeyDown)="updateValues(min(), 0, true)"
585
- (endKeyDown)="updateValues(max(), modelValue().length - 1, true)"
586
- (stepKeyDown)="handleStepKeyDown($event)"
587
- >
588
- <ng-container *ngTemplateOutlet="transclude" />
589
- </rdx-slider-horizontal>
714
+ /**
715
+ * @ignore
716
+ * Applies a new full set of values, preserving the single/range value shape.
717
+ * Returns `false` when the value did not change.
718
+ */
719
+ setValue(nextValues, reason) {
720
+ const next = this.range() ? nextValues : nextValues[0];
721
+ const current = this.outputValue();
722
+ const hasNaN = Array.isArray(next) ? next.some((v) => Number.isNaN(v)) : Number.isNaN(next);
723
+ if (hasNaN || areValuesEqual(next, current)) {
724
+ return false;
590
725
  }
591
-
592
- @if (orientation() === 'vertical') {
593
- <rdx-slider-vertical
594
- [className]="styleClass() || className"
595
- [min]="min()"
596
- [max]="max()"
597
- [dir]="dir()"
598
- [inverted]="inverted()"
599
- [attr.aria-disabled]="disabled()"
600
- [attr.data-disabled]="disabled() ? '' : undefined"
601
- (pointerdown)="onPointerDown()"
602
- (slideStart)="handleSlideStart($event)"
603
- (slideMove)="handleSlideMove($event)"
604
- (slideEnd)="handleSlideEnd()"
605
- (homeKeyDown)="updateValues(min(), 0, true)"
606
- (endKeyDown)="updateValues(max(), modelValue().length - 1, true)"
607
- (stepKeyDown)="handleStepKeyDown($event)"
608
- >
609
- <ng-container *ngTemplateOutlet="transclude" />
610
- </rdx-slider-vertical>
611
- }
612
- `, isInline: true, dependencies: [{ kind: "component", type: RdxSliderHorizontalComponent, selector: "rdx-slider-horizontal", inputs: ["dir", "inverted", "min", "max", "className"], outputs: ["slideStart", "slideMove", "slideEnd", "stepKeyDown", "endKeyDown", "homeKeyDown"] }, { kind: "component", type: RdxSliderVerticalComponent, selector: "rdx-slider-vertical", inputs: ["dir", "inverted", "min", "max", "className"], outputs: ["slideStart", "slideMove", "slideEnd", "stepKeyDown", "endKeyDown", "homeKeyDown"] }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }] }); }
613
- }
614
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxSliderRootComponent, decorators: [{
615
- type: Component,
616
- args: [{
617
- selector: 'rdx-slider',
618
- imports: [RdxSliderHorizontalComponent, RdxSliderVerticalComponent, NgTemplateOutlet],
619
- providers: [RdxSliderOrientationContextService],
620
- template: `
621
- <ng-template #transclude><ng-content /></ng-template>
622
-
623
- @if (orientation() === 'horizontal') {
624
- <rdx-slider-horizontal
625
- [className]="styleClass() || className"
626
- [min]="min()"
627
- [max]="max()"
628
- [dir]="dir()"
629
- [inverted]="inverted()"
630
- [attr.aria-disabled]="disabled()"
631
- [attr.data-disabled]="disabled() ? '' : undefined"
632
- (pointerdown)="onPointerDown()"
633
- (slideStart)="handleSlideStart($event)"
634
- (slideMove)="handleSlideMove($event)"
635
- (slideEnd)="handleSlideEnd()"
636
- (homeKeyDown)="updateValues(min(), 0, true)"
637
- (endKeyDown)="updateValues(max(), modelValue().length - 1, true)"
638
- (stepKeyDown)="handleStepKeyDown($event)"
639
- >
640
- <ng-container *ngTemplateOutlet="transclude" />
641
- </rdx-slider-horizontal>
726
+ this.lastChangeReason = reason;
727
+ this.value.set(next);
728
+ this.cva.setValue(next);
729
+ this.onValueChange.emit(next);
730
+ return true;
731
+ }
732
+ /** @ignore Keyboard / native input path: clamps to neighbours, commits immediately. */
733
+ handleInputChange(valueInput, index, reason = 'keyboard') {
734
+ if (this.isDisabled()) {
735
+ return;
642
736
  }
643
-
644
- @if (orientation() === 'vertical') {
645
- <rdx-slider-vertical
646
- [className]="styleClass() || className"
647
- [min]="min()"
648
- [max]="max()"
649
- [dir]="dir()"
650
- [inverted]="inverted()"
651
- [attr.aria-disabled]="disabled()"
652
- [attr.data-disabled]="disabled() ? '' : undefined"
653
- (pointerdown)="onPointerDown()"
654
- (slideStart)="handleSlideStart($event)"
655
- (slideMove)="handleSlideMove($event)"
656
- (slideEnd)="handleSlideEnd()"
657
- (homeKeyDown)="updateValues(min(), 0, true)"
658
- (endKeyDown)="updateValues(max(), modelValue().length - 1, true)"
659
- (stepKeyDown)="handleStepKeyDown($event)"
660
- >
661
- <ng-container *ngTemplateOutlet="transclude" />
662
- </rdx-slider-vertical>
663
- }
664
- `
665
- }]
666
- }], propDecorators: { 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 }] }], minStepsBetweenThumbs: [{ type: i0.Input, args: [{ isSignal: true, alias: "minStepsBetweenThumbs", required: false }] }], orientation: [{ type: i0.Input, args: [{ isSignal: true, alias: "orientation", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], inverted: [{ type: i0.Input, args: [{ isSignal: true, alias: "inverted", required: false }] }], dir: [{ type: i0.Input, args: [{ isSignal: true, alias: "dir", required: false }] }], className: [{
667
- type: Input
668
- }], styleClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "styleClass", required: false }] }], modelValue: [{ type: i0.Input, args: [{ isSignal: true, alias: "modelValue", required: false }] }, { type: i0.Output, args: ["modelValueChange"] }], valueChange: [{ type: i0.Output, args: ["valueChange"] }], valueCommit: [{ type: i0.Output, args: ["valueCommit"] }], valueIndexToChange: [{ type: i0.Input, args: [{ isSignal: true, alias: "valueIndexToChange", required: false }] }, { type: i0.Output, args: ["valueIndexToChangeChange"] }], valuesBeforeSlideStart: [{ type: i0.Input, args: [{ isSignal: true, alias: "valuesBeforeSlideStart", required: false }] }, { type: i0.Output, args: ["valuesBeforeSlideStartChange"] }] } });
669
-
670
- class RdxSliderRangeComponent {
671
- constructor() {
672
- this.rootContext = inject(RdxSliderRootComponent);
673
- this.percentages = computed(() => this.rootContext
674
- .modelValue()
675
- ?.map((value) => convertValueToPercentage(value, this.rootContext.min(), this.rootContext.max())), ...(ngDevMode ? [{ debugName: "percentages" }] : /* istanbul ignore next */ []));
676
- this.offsetStart = computed(() => (this.rootContext.modelValue().length > 1 ? Math.min(...this.percentages()) : 0), ...(ngDevMode ? [{ debugName: "offsetStart" }] : /* istanbul ignore next */ []));
677
- this.offsetEnd = computed(() => 100 - Math.max(...this.percentages()), ...(ngDevMode ? [{ debugName: "offsetEnd" }] : /* istanbul ignore next */ []));
678
- this.rangeStyles = computed(() => {
679
- const context = this.rootContext.orientationContext.context;
680
- return {
681
- [context.startEdge]: `${this.offsetStart()}%`,
682
- [context.endEdge]: `${this.offsetEnd()}%`
683
- };
684
- }, ...(ngDevMode ? [{ debugName: "rangeStyles" }] : /* istanbul ignore next */ []));
737
+ const result = getSliderValue(valueInput, index, this.min(), this.max(), this.range(), this.values());
738
+ if (!validateMinimumDistance(result, this.step(), this.minStepsBetweenValues())) {
739
+ return;
740
+ }
741
+ const arr = Array.isArray(result) ? result : [result];
742
+ const applied = this.setValue(arr, reason);
743
+ this.cva.markAsTouched();
744
+ if (applied) {
745
+ this.onValueCommitted.emit(this.outputValue());
746
+ }
747
+ }
748
+ /** @ignore Emits the committed value at the end of a pointer drag. */
749
+ commitValue() {
750
+ this.onValueCommitted.emit(this.outputValue());
685
751
  }
686
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxSliderRangeComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
687
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.2.9", type: RdxSliderRangeComponent, isStandalone: true, selector: "rdx-slider-range", host: { properties: { "attr.data-disabled": "rootContext.disabled() ? \"\" : undefined", "attr.data-orientation": "rootContext.orientation()", "style": "rangeStyles()" } }, ngImport: i0, template: `
688
- <ng-content />
689
- `, isInline: true }); }
752
+ /** @ignore */
753
+ markAsTouched() {
754
+ this.cva.markAsTouched();
755
+ }
756
+ /** @ignore */
757
+ setDragging(dragging) {
758
+ this.dragging.set(dragging);
759
+ }
760
+ /** @ignore */
761
+ resetPressedThumb() {
762
+ this.pressedThumbIndex = -1;
763
+ this.pressedThumbCenterOffset = null;
764
+ this.pressedInput = null;
765
+ }
766
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxSliderRoot, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
767
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.2.9", type: RdxSliderRoot, isStandalone: true, selector: "div[rdxSliderRoot]", 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 }, largeStep: { classPropertyName: "largeStep", publicName: "largeStep", isSignal: true, isRequired: false, transformFunction: null }, minStepsBetweenValues: { classPropertyName: "minStepsBetweenValues", publicName: "minStepsBetweenValues", isSignal: true, isRequired: false, transformFunction: null }, orientation: { classPropertyName: "orientation", publicName: "orientation", isSignal: true, isRequired: false, transformFunction: null }, dir: { classPropertyName: "dir", publicName: "dir", isSignal: true, isRequired: false, transformFunction: null }, thumbCollisionBehavior: { classPropertyName: "thumbCollisionBehavior", publicName: "thumbCollisionBehavior", 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 }, name: { classPropertyName: "name", publicName: "name", isSignal: true, isRequired: false, transformFunction: null }, form: { classPropertyName: "form", publicName: "form", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", 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 }, ariaLabelledBy: { classPropertyName: "ariaLabelledBy", publicName: "aria-labelledby", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { value: "valueChange", onValueChange: "onValueChange", onValueCommitted: "onValueCommitted" }, host: { attributes: { "role": "group" }, properties: { "id": "id()", "attr.aria-labelledby": "ariaLabelledBy()", "attr.dir": "dir()", "attr.data-orientation": "orientation()", "attr.data-disabled": "isDisabled() ? \"\" : undefined", "attr.data-dragging": "dragging() ? \"\" : undefined" } }, providers: [provideSliderRootContext(() => inject(RdxSliderRoot))], exportAs: ["rdxSliderRoot"], hostDirectives: [{ directive: i1.RdxControlValueAccessor, inputs: ["value", "value", "disabled", "disabled"] }], ngImport: i0 }); }
690
768
  }
691
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxSliderRangeComponent, decorators: [{
692
- type: Component,
769
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxSliderRoot, decorators: [{
770
+ type: Directive,
693
771
  args: [{
694
- selector: 'rdx-slider-range',
772
+ selector: 'div[rdxSliderRoot]',
773
+ exportAs: 'rdxSliderRoot',
774
+ providers: [provideSliderRootContext(() => inject(RdxSliderRoot))],
775
+ hostDirectives: [
776
+ {
777
+ directive: RdxControlValueAccessor,
778
+ inputs: ['value: value', 'disabled']
779
+ }
780
+ ],
695
781
  host: {
696
- '[attr.data-disabled]': 'rootContext.disabled() ? "" : undefined',
697
- '[attr.data-orientation]': 'rootContext.orientation()',
698
- '[style]': 'rangeStyles()'
699
- },
700
- template: `
701
- <ng-content />
702
- `
782
+ role: 'group',
783
+ '[id]': 'id()',
784
+ '[attr.aria-labelledby]': 'ariaLabelledBy()',
785
+ '[attr.dir]': 'dir()',
786
+ '[attr.data-orientation]': 'orientation()',
787
+ '[attr.data-disabled]': 'isDisabled() ? "" : undefined',
788
+ '[attr.data-dragging]': 'dragging() ? "" : undefined'
789
+ }
703
790
  }]
704
- }] });
791
+ }], 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 }] }], largeStep: [{ type: i0.Input, args: [{ isSignal: true, alias: "largeStep", required: false }] }], minStepsBetweenValues: [{ type: i0.Input, args: [{ isSignal: true, alias: "minStepsBetweenValues", required: false }] }], orientation: [{ type: i0.Input, args: [{ isSignal: true, alias: "orientation", required: false }] }], dir: [{ type: i0.Input, args: [{ isSignal: true, alias: "dir", required: false }] }], thumbCollisionBehavior: [{ type: i0.Input, args: [{ isSignal: true, alias: "thumbCollisionBehavior", required: false }] }], format: [{ type: i0.Input, args: [{ isSignal: true, alias: "format", required: false }] }], locale: [{ type: i0.Input, args: [{ isSignal: true, alias: "locale", required: false }] }], name: [{ type: i0.Input, args: [{ isSignal: true, alias: "name", required: false }] }], form: [{ type: i0.Input, args: [{ isSignal: true, alias: "form", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", 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"] }], ariaLabelledBy: [{ type: i0.Input, args: [{ isSignal: true, alias: "aria-labelledby", required: false }] }], onValueChange: [{ type: i0.Output, args: ["onValueChange"] }], onValueCommitted: [{ type: i0.Output, args: ["onValueCommitted"] }] } });
705
792
 
706
- class RdxSliderThumbImplDirective {
793
+ /**
794
+ * A draggable handle. Render one per value; place an `input[rdxSliderThumbInput]`
795
+ * inside it for keyboard, accessibility and form submission.
796
+ *
797
+ * @see https://base-ui.com/react/components/slider
798
+ */
799
+ class RdxSliderThumb {
707
800
  constructor() {
708
- this.rootContext = inject(RdxSliderRootComponent);
709
- this.elementRef = inject(ElementRef);
710
- this.platformId = inject(PLATFORM_ID);
711
- this.isMounted = signal(false, ...(ngDevMode ? [{ debugName: "isMounted" }] : /* istanbul ignore next */ []));
712
- this.thumbIndex = computed(() => {
713
- const thumbElement = this.elementRef.nativeElement;
714
- const index = this.rootContext.thumbElements.indexOf(thumbElement);
715
- return index >= 0 ? index : null;
716
- }, ...(ngDevMode ? [{ debugName: "thumbIndex" }] : /* istanbul ignore next */ []));
717
- this.value = computed(() => {
718
- const index = this.thumbIndex();
719
- if (index === null)
720
- return undefined;
721
- return this.rootContext.modelValue()?.[index];
722
- }, ...(ngDevMode ? [{ debugName: "value" }] : /* istanbul ignore next */ []));
801
+ this.root = injectSliderRootContext();
802
+ this.element = inject(ElementRef).nativeElement;
803
+ /** The nested range input, set by `[rdxSliderThumbInput]`. */
804
+ this.inputElement = null;
805
+ /** Explicit index for this thumb (required for SSR range sliders). */
806
+ this.indexInput = input(undefined, { ...(ngDevMode ? { debugName: "indexInput" } : /* istanbul ignore next */ {}), alias: 'index',
807
+ transform: (v) => (v == null ? undefined : numberAttribute(v)) });
808
+ /** Disables this individual thumb. */
809
+ this.thumbDisabled = input(false, { ...(ngDevMode ? { debugName: "thumbDisabled" } : /* istanbul ignore next */ {}), alias: 'disabled', transform: booleanAttribute });
810
+ /** The position of this thumb among its siblings. */
811
+ this.index = computed(() => this.indexInput() ?? this.root.thumbIndexOf(this), ...(ngDevMode ? [{ debugName: "index" }] : /* istanbul ignore next */ []));
812
+ /** Whether this thumb is disabled (own state OR root disabled). */
813
+ this.disabled = computed(() => this.thumbDisabled() || this.root.isDisabled(), ...(ngDevMode ? [{ debugName: "disabled" }] : /* istanbul ignore next */ []));
814
+ /** The value represented by this thumb. */
815
+ this.value = computed(() => this.root.values()[this.index()], ...(ngDevMode ? [{ debugName: "value" }] : /* istanbul ignore next */ []));
723
816
  this.percent = computed(() => {
724
- const val = this.value();
725
- if (val === undefined)
726
- return 0;
727
- return convertValueToPercentage(val, this.rootContext.min(), this.rootContext.max());
817
+ const value = this.value();
818
+ return value === undefined ? NaN : valueToPercent(value, this.root.min(), this.root.max());
728
819
  }, ...(ngDevMode ? [{ debugName: "percent" }] : /* istanbul ignore next */ []));
729
- this.transform = computed(() => {
730
- const percent = this.percent();
731
- const offset = this.thumbInBoundsOffset();
732
- return `calc(${percent}% + ${offset}px)`;
733
- }, ...(ngDevMode ? [{ debugName: "transform" }] : /* istanbul ignore next */ []));
734
- this.orientationSize = signal(0, ...(ngDevMode ? [{ debugName: "orientationSize" }] : /* istanbul ignore next */ []));
735
- this.thumbInBoundsOffset = computed(() => {
736
- const context = this.rootContext.orientationContext.context;
737
- const size = this.orientationSize();
738
- const percent = this.percent();
739
- const direction = context.direction;
740
- return size ? getThumbInBoundsOffset(size, percent, direction) : 0;
741
- }, ...(ngDevMode ? [{ debugName: "thumbInBoundsOffset" }] : /* istanbul ignore next */ []));
742
- this.combinedStyles = computed(() => {
743
- const context = this.rootContext.orientationContext.context;
744
- const startEdge = context.startEdge;
820
+ this.thumbStyle = computed(() => {
821
+ const vertical = this.root.orientation() === 'vertical';
822
+ const rtl = this.root.dir() === 'rtl';
823
+ const startEdge = vertical ? 'bottom' : 'inset-inline-start';
824
+ const crossOffset = vertical ? 'left' : 'top';
745
825
  const percent = this.percent();
746
- const offset = this.thumbInBoundsOffset();
747
- return {
826
+ if (!Number.isFinite(percent)) {
827
+ return { position: 'absolute', visibility: 'hidden' };
828
+ }
829
+ const index = this.index();
830
+ let zIndex;
831
+ if (this.root.range()) {
832
+ if (this.root.active() === index) {
833
+ zIndex = 2;
834
+ }
835
+ else if (this.root.lastUsedThumbIndex() === index) {
836
+ zIndex = 1;
837
+ }
838
+ }
839
+ else if (this.root.active() === index) {
840
+ zIndex = 1;
841
+ }
842
+ const style = {
748
843
  position: 'absolute',
749
- transform: 'var(--rdx-slider-thumb-transform)',
750
- display: (this.isMounted() && this.value()) === false ? 'none' : undefined,
751
- [startEdge]: `calc(${percent}% + ${offset}px)`
844
+ [startEdge]: `${percent}%`,
845
+ [crossOffset]: '50%',
846
+ translate: `${(vertical || !rtl ? -1 : 1) * 50}% ${(vertical ? 1 : -1) * 50}%`
752
847
  };
753
- }, ...(ngDevMode ? [{ debugName: "combinedStyles" }] : /* istanbul ignore next */ []));
848
+ if (zIndex !== undefined) {
849
+ style['z-index'] = zIndex;
850
+ }
851
+ return style;
852
+ }, ...(ngDevMode ? [{ debugName: "thumbStyle" }] : /* istanbul ignore next */ []));
853
+ // Registration is DOM-order sorted on the root and reads no inputs, so the constructor
854
+ // (where the host element already exists) is the right place; cleanup goes via DestroyRef.
855
+ this.root.registerThumb(this);
856
+ inject(DestroyRef).onDestroy(() => this.root.unregisterThumb(this));
857
+ }
858
+ onPointerDown(event) {
859
+ if (this.disabled()) {
860
+ return;
861
+ }
862
+ const index = this.index();
863
+ this.root.pressedThumbIndex = index;
864
+ const axis = this.root.orientation() === 'horizontal' ? 'x' : 'y';
865
+ const rect = this.element.getBoundingClientRect();
866
+ const midpoint = axis === 'x' ? (rect.left + rect.right) / 2 : (rect.top + rect.bottom) / 2;
867
+ const pointer = axis === 'x' ? event.clientX : event.clientY;
868
+ this.root.pressedThumbCenterOffset = pointer - midpoint;
869
+ if (this.inputElement) {
870
+ this.root.pressedInput = this.inputElement;
871
+ }
872
+ }
873
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxSliderThumb, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
874
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.2.9", type: RdxSliderThumb, isStandalone: true, selector: "div[rdxSliderThumb]", inputs: { indexInput: { classPropertyName: "indexInput", publicName: "index", isSignal: true, isRequired: false, transformFunction: null }, thumbDisabled: { classPropertyName: "thumbDisabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null } }, host: { listeners: { "pointerdown": "onPointerDown($event)" }, properties: { "style": "thumbStyle()", "attr.data-index": "index()", "attr.data-orientation": "root.orientation()", "attr.data-disabled": "disabled() ? \"\" : undefined", "attr.data-dragging": "root.dragging() ? \"\" : undefined" } }, exportAs: ["rdxSliderThumb"], ngImport: i0 }); }
875
+ }
876
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxSliderThumb, decorators: [{
877
+ type: Directive,
878
+ args: [{
879
+ selector: 'div[rdxSliderThumb]',
880
+ exportAs: 'rdxSliderThumb',
881
+ host: {
882
+ '[style]': 'thumbStyle()',
883
+ '[attr.data-index]': 'index()',
884
+ '[attr.data-orientation]': 'root.orientation()',
885
+ '[attr.data-disabled]': 'disabled() ? "" : undefined',
886
+ '[attr.data-dragging]': 'root.dragging() ? "" : undefined',
887
+ '(pointerdown)': 'onPointerDown($event)'
888
+ }
889
+ }]
890
+ }], ctorParameters: () => [], propDecorators: { indexInput: [{ type: i0.Input, args: [{ isSignal: true, alias: "index", required: false }] }], thumbDisabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }] } });
891
+
892
+ /**
893
+ * The native `input[type=range]` nested inside a thumb. It is visually hidden but
894
+ * remains the focusable element that drives keyboard interaction, accessibility
895
+ * and form submission.
896
+ *
897
+ * @see https://base-ui.com/react/components/slider
898
+ */
899
+ class RdxSliderThumbInput {
900
+ constructor() {
901
+ this.root = injectSliderRootContext();
902
+ this.thumb = inject(RdxSliderThumb);
903
+ this.element = inject(ElementRef).nativeElement;
904
+ this.ariaLabel = input(undefined, { ...(ngDevMode ? { debugName: "ariaLabel" } : /* istanbul ignore next */ {}), alias: 'aria-label' });
905
+ this.ariaValueTextInput = input(undefined, { ...(ngDevMode ? { debugName: "ariaValueTextInput" } : /* istanbul ignore next */ {}), alias: 'aria-valuetext' });
906
+ this.writingMode = computed(() => this.root.orientation() === 'vertical' ? (this.root.dir() === 'rtl' ? 'vertical-rl' : 'vertical-lr') : undefined, ...(ngDevMode ? [{ debugName: "writingMode" }] : /* istanbul ignore next */ []));
907
+ this.ariaLabelledBy = computed(() => this.ariaLabel() == null ? this.root.ariaLabelledBy() : undefined, ...(ngDevMode ? [{ debugName: "ariaLabelledBy" }] : /* istanbul ignore next */ []));
908
+ this.valueText = computed(() => this.ariaValueTextInput() ??
909
+ getDefaultAriaValueText(this.root.values(), this.thumb.index(), this.root.format(), this.root.locale()), ...(ngDevMode ? [{ debugName: "valueText" }] : /* istanbul ignore next */ []));
910
+ // Host element exists in the constructor and the registration has no input dependency.
911
+ this.thumb.inputElement = this.element;
912
+ inject(DestroyRef).onDestroy(() => {
913
+ if (this.thumb.inputElement === this.element) {
914
+ this.thumb.inputElement = null;
915
+ }
916
+ });
917
+ }
918
+ onChange(event) {
919
+ const value = event.target.valueAsNumber;
920
+ if (!Number.isNaN(value)) {
921
+ this.root.handleInputChange(value, this.thumb.index(), 'input');
922
+ }
754
923
  }
755
924
  onFocus() {
756
- if (this.thumbIndex() !== null) {
757
- this.rootContext.valueIndexToChange.set(this.thumbIndex());
758
- }
759
- }
760
- ngOnInit() {
761
- if (isPlatformBrowser(this.platformId)) {
762
- const thumbElement = this.elementRef.nativeElement;
763
- this.rootContext.thumbElements.push(thumbElement);
764
- this.resizeObserver = new ResizeObserver(() => {
765
- const rect = thumbElement.getBoundingClientRect();
766
- const context = this.rootContext.orientationContext.context;
767
- const size = context.size === 'width' ? rect.width : rect.height;
768
- this.orientationSize.set(size);
769
- });
770
- this.resizeObserver.observe(thumbElement);
771
- this.isMounted.set(true);
772
- }
773
- }
774
- ngOnDestroy() {
775
- const thumbElement = this.elementRef.nativeElement;
776
- const index = this.rootContext.thumbElements.indexOf(thumbElement);
777
- if (index >= 0)
778
- this.rootContext.thumbElements.splice(index, 1);
779
- if (this.resizeObserver) {
780
- this.resizeObserver.unobserve(thumbElement);
781
- }
782
- this.isMounted.set(false);
783
- }
784
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxSliderThumbImplDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
785
- static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.2.9", type: RdxSliderThumbImplDirective, isStandalone: true, selector: "[rdxSliderThumbImpl]", host: { attributes: { "role": "slider" }, listeners: { "focus": "onFocus()" }, properties: { "tabindex": "rootContext.disabled() ? undefined : 0", "attr.aria-valuenow": "rootContext.modelValue()", "attr.aria-valuemin": "rootContext.min()", "attr.aria-valuemax": "rootContext.max()", "attr.aria-orientation": "rootContext.orientation()", "attr.data-orientation": "rootContext.orientation()", "attr.data-disabled": "rootContext.disabled() ? \"\" : undefined", "style": "combinedStyles()" } }, ngImport: i0 }); }
925
+ this.root.setActive(this.thumb.index());
926
+ }
927
+ onBlur() {
928
+ this.root.setActive(-1);
929
+ this.root.markAsTouched();
930
+ }
931
+ onKeyDown(event) {
932
+ if (event.defaultPrevented || !ALL_KEYS.has(event.key)) {
933
+ return;
934
+ }
935
+ if (COMPOSITE_KEYS.has(event.key)) {
936
+ event.stopPropagation();
937
+ }
938
+ const min = this.root.min();
939
+ const max = this.root.max();
940
+ const step = this.root.step();
941
+ const largeStep = this.root.largeStep();
942
+ const rtl = this.root.dir() === 'rtl';
943
+ const range = this.root.range();
944
+ const values = this.root.values();
945
+ const index = this.thumb.index();
946
+ const thumbValue = values[index];
947
+ const rounded = roundValueToStep(thumbValue, step, min);
948
+ let newValue = null;
949
+ switch (event.key) {
950
+ case 'ArrowUp':
951
+ newValue = getNewValue(rounded, event.shiftKey ? largeStep : step, 1, min, max);
952
+ break;
953
+ case 'ArrowRight':
954
+ newValue = getNewValue(rounded, event.shiftKey ? largeStep : step, rtl ? -1 : 1, min, max);
955
+ break;
956
+ case 'ArrowDown':
957
+ newValue = getNewValue(rounded, event.shiftKey ? largeStep : step, -1, min, max);
958
+ break;
959
+ case 'ArrowLeft':
960
+ newValue = getNewValue(rounded, event.shiftKey ? largeStep : step, rtl ? 1 : -1, min, max);
961
+ break;
962
+ case 'PageUp':
963
+ newValue = getNewValue(rounded, largeStep, 1, min, max);
964
+ break;
965
+ case 'PageDown':
966
+ newValue = getNewValue(rounded, largeStep, -1, min, max);
967
+ break;
968
+ case 'End':
969
+ newValue =
970
+ range && Number.isFinite(values[index + 1])
971
+ ? values[index + 1] - step * this.root.minStepsBetweenValues()
972
+ : max;
973
+ break;
974
+ case 'Home':
975
+ newValue =
976
+ range && Number.isFinite(values[index - 1])
977
+ ? values[index - 1] + step * this.root.minStepsBetweenValues()
978
+ : min;
979
+ break;
980
+ default:
981
+ break;
982
+ }
983
+ if (newValue !== null) {
984
+ this.root.handleInputChange(newValue, index, 'keyboard');
985
+ event.preventDefault();
986
+ }
987
+ }
988
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxSliderThumbInput, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
989
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.2.9", type: RdxSliderThumbInput, isStandalone: true, selector: "input[rdxSliderThumbInput]", inputs: { ariaLabel: { classPropertyName: "ariaLabel", publicName: "aria-label", isSignal: true, isRequired: false, transformFunction: null }, ariaValueTextInput: { classPropertyName: "ariaValueTextInput", publicName: "aria-valuetext", isSignal: true, isRequired: false, transformFunction: null } }, host: { attributes: { "type": "range" }, listeners: { "keydown": "onKeyDown($event)", "change": "onChange($event)", "focus": "onFocus()", "blur": "onBlur()" }, properties: { "style.writing-mode": "writingMode()", "attr.min": "root.min()", "attr.max": "root.max()", "attr.step": "root.step()", "value": "thumb.value() ?? \"\"", "disabled": "thumb.disabled()", "attr.name": "root.name()", "attr.form": "root.form()", "attr.aria-orientation": "root.orientation()", "attr.aria-valuenow": "thumb.value()", "attr.aria-valuetext": "valueText()", "attr.aria-label": "ariaLabel()", "attr.aria-labelledby": "ariaLabelledBy()", "attr.data-index": "thumb.index()" }, styleAttribute: "position: absolute; inset: 0; width: 100%; height: 100%; margin: 0; padding: 0; opacity: 0; cursor: inherit;" }, exportAs: ["rdxSliderThumbInput"], ngImport: i0 }); }
786
990
  }
787
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxSliderThumbImplDirective, decorators: [{
991
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxSliderThumbInput, decorators: [{
788
992
  type: Directive,
789
993
  args: [{
790
- selector: '[rdxSliderThumbImpl]',
994
+ selector: 'input[rdxSliderThumbInput]',
995
+ exportAs: 'rdxSliderThumbInput',
791
996
  host: {
792
- role: 'slider',
793
- '[tabindex]': 'rootContext.disabled() ? undefined : 0',
794
- '[attr.aria-valuenow]': 'rootContext.modelValue()',
795
- '[attr.aria-valuemin]': 'rootContext.min()',
796
- '[attr.aria-valuemax]': 'rootContext.max()',
797
- '[attr.aria-orientation]': 'rootContext.orientation()',
798
- '[attr.data-orientation]': 'rootContext.orientation()',
799
- '[attr.data-disabled]': 'rootContext.disabled() ? "" : undefined',
800
- '[style]': 'combinedStyles()',
801
- '(focus)': 'onFocus()'
997
+ type: 'range',
998
+ style: 'position: absolute; inset: 0; width: 100%; height: 100%; margin: 0; padding: 0; opacity: 0; cursor: inherit;',
999
+ '[style.writing-mode]': 'writingMode()',
1000
+ '[attr.min]': 'root.min()',
1001
+ '[attr.max]': 'root.max()',
1002
+ '[attr.step]': 'root.step()',
1003
+ '[value]': 'thumb.value() ?? ""',
1004
+ '[disabled]': 'thumb.disabled()',
1005
+ '[attr.name]': 'root.name()',
1006
+ '[attr.form]': 'root.form()',
1007
+ '[attr.aria-orientation]': 'root.orientation()',
1008
+ '[attr.aria-valuenow]': 'thumb.value()',
1009
+ '[attr.aria-valuetext]': 'valueText()',
1010
+ '[attr.aria-label]': 'ariaLabel()',
1011
+ '[attr.aria-labelledby]': 'ariaLabelledBy()',
1012
+ '[attr.data-index]': 'thumb.index()',
1013
+ '(keydown)': 'onKeyDown($event)',
1014
+ '(change)': 'onChange($event)',
1015
+ '(focus)': 'onFocus()',
1016
+ '(blur)': 'onBlur()'
802
1017
  }
803
1018
  }]
804
- }] });
1019
+ }], ctorParameters: () => [], propDecorators: { ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "aria-label", required: false }] }], ariaValueTextInput: [{ type: i0.Input, args: [{ isSignal: true, alias: "aria-valuetext", required: false }] }] } });
805
1020
 
806
- class RdxSliderThumbComponent {
807
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxSliderThumbComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
808
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.2.9", type: RdxSliderThumbComponent, isStandalone: true, selector: "rdx-slider-thumb", hostDirectives: [{ directive: RdxSliderThumbImplDirective }], ngImport: i0, template: `
809
- <ng-content />
810
- `, isInline: true }); }
1021
+ /**
1022
+ * The track of the slider the positioning context for the indicator and thumbs.
1023
+ *
1024
+ * @see https://base-ui.com/react/components/slider
1025
+ */
1026
+ class RdxSliderTrack {
1027
+ constructor() {
1028
+ this.root = injectSliderRootContext();
1029
+ }
1030
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxSliderTrack, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
1031
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.2.9", type: RdxSliderTrack, isStandalone: true, selector: "div[rdxSliderTrack]", host: { properties: { "attr.data-orientation": "root.orientation()", "attr.data-disabled": "root.isDisabled() ? \"\" : undefined", "attr.data-dragging": "root.dragging() ? \"\" : undefined" }, styleAttribute: "position: relative;" }, exportAs: ["rdxSliderTrack"], ngImport: i0 }); }
811
1032
  }
812
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxSliderThumbComponent, decorators: [{
813
- type: Component,
1033
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxSliderTrack, decorators: [{
1034
+ type: Directive,
814
1035
  args: [{
815
- selector: 'rdx-slider-thumb',
816
- hostDirectives: [RdxSliderThumbImplDirective],
817
- template: `
818
- <ng-content />
819
- `
1036
+ selector: 'div[rdxSliderTrack]',
1037
+ exportAs: 'rdxSliderTrack',
1038
+ host: {
1039
+ style: 'position: relative;',
1040
+ '[attr.data-orientation]': 'root.orientation()',
1041
+ '[attr.data-disabled]': 'root.isDisabled() ? "" : undefined',
1042
+ '[attr.data-dragging]': 'root.dragging() ? "" : undefined'
1043
+ }
820
1044
  }]
821
1045
  }] });
822
1046
 
823
- class RdxSliderTrackComponent {
1047
+ /**
1048
+ * Displays the slider's current value(s) as formatted text. Renders into an
1049
+ * `output` element; the displayed value honours the root `format` and `locale`.
1050
+ *
1051
+ * @see https://base-ui.com/react/components/slider
1052
+ */
1053
+ class RdxSliderValue {
824
1054
  constructor() {
825
- this.rootContext = inject(RdxSliderRootComponent);
1055
+ this.root = injectSliderRootContext();
1056
+ /** The separator placed between values of a range slider. */
1057
+ this.separator = input(' – ', ...(ngDevMode ? [{ debugName: "separator" }] : /* istanbul ignore next */ []));
1058
+ this.display = computed(() => this.root
1059
+ .values()
1060
+ .map((value) => this.root.formatValue(value) || `${value}`)
1061
+ .join(this.separator()), ...(ngDevMode ? [{ debugName: "display" }] : /* istanbul ignore next */ []));
826
1062
  }
827
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxSliderTrackComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
828
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.2.9", type: RdxSliderTrackComponent, isStandalone: true, selector: "rdx-slider-track", host: { properties: { "attr.data-disabled": "rootContext.disabled() ? '' : undefined", "attr.data-orientation": "rootContext.orientation()" } }, ngImport: i0, template: `
829
- <ng-content />
830
- `, isInline: true }); }
1063
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxSliderValue, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
1064
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.2.9", type: RdxSliderValue, isStandalone: true, selector: "output[rdxSliderValue]", inputs: { separator: { classPropertyName: "separator", publicName: "separator", isSignal: true, isRequired: false, transformFunction: null } }, host: { attributes: { "aria-live": "off" }, properties: { "textContent": "display()" } }, exportAs: ["rdxSliderValue"], ngImport: i0 }); }
831
1065
  }
832
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxSliderTrackComponent, decorators: [{
833
- type: Component,
1066
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxSliderValue, decorators: [{
1067
+ type: Directive,
834
1068
  args: [{
835
- selector: 'rdx-slider-track',
1069
+ selector: 'output[rdxSliderValue]',
1070
+ exportAs: 'rdxSliderValue',
836
1071
  host: {
837
- '[attr.data-disabled]': "rootContext.disabled() ? '' : undefined",
838
- '[attr.data-orientation]': 'rootContext.orientation()'
839
- },
840
- template: `
841
- <ng-content />
842
- `
1072
+ 'aria-live': 'off',
1073
+ '[textContent]': 'display()'
1074
+ }
843
1075
  }]
844
- }] });
1076
+ }], propDecorators: { separator: [{ type: i0.Input, args: [{ isSignal: true, alias: "separator", required: false }] }] } });
845
1077
 
846
- const _imports = [RdxSliderRootComponent, RdxSliderTrackComponent, RdxSliderRangeComponent, RdxSliderThumbComponent];
1078
+ const _imports = [
1079
+ RdxSliderRoot,
1080
+ RdxSliderControl,
1081
+ RdxSliderTrack,
1082
+ RdxSliderIndicator,
1083
+ RdxSliderThumb,
1084
+ RdxSliderThumbInput,
1085
+ RdxSliderValue
1086
+ ];
847
1087
  class RdxSliderModule {
848
1088
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxSliderModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
849
- static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "21.2.9", ngImport: i0, type: RdxSliderModule, imports: [RdxSliderRootComponent, RdxSliderTrackComponent, RdxSliderRangeComponent, RdxSliderThumbComponent], exports: [RdxSliderRootComponent, RdxSliderTrackComponent, RdxSliderRangeComponent, RdxSliderThumbComponent] }); }
1089
+ static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "21.2.9", ngImport: i0, type: RdxSliderModule, imports: [RdxSliderRoot,
1090
+ RdxSliderControl,
1091
+ RdxSliderTrack,
1092
+ RdxSliderIndicator,
1093
+ RdxSliderThumb,
1094
+ RdxSliderThumbInput,
1095
+ RdxSliderValue], exports: [RdxSliderRoot,
1096
+ RdxSliderControl,
1097
+ RdxSliderTrack,
1098
+ RdxSliderIndicator,
1099
+ RdxSliderThumb,
1100
+ RdxSliderThumbInput,
1101
+ RdxSliderValue] }); }
850
1102
  static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxSliderModule }); }
851
1103
  }
852
1104
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxSliderModule, decorators: [{
@@ -861,5 +1113,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
861
1113
  * Generated bundle index. Do not edit.
862
1114
  */
863
1115
 
864
- export { RdxSliderHorizontalComponent, RdxSliderImplDirective, RdxSliderModule, RdxSliderRangeComponent, RdxSliderRootComponent, RdxSliderThumbComponent, RdxSliderThumbImplDirective, RdxSliderTrackComponent, RdxSliderVerticalComponent };
1116
+ export { ALL_KEYS, ARROW_KEYS, COMPOSITE_KEYS, RdxSliderControl, RdxSliderIndicator, RdxSliderModule, RdxSliderRoot, RdxSliderThumb, RdxSliderThumbInput, RdxSliderTrack, RdxSliderValue, areValuesEqual, asc, formatNumber, getControlOffset, getDecimalPrecision, getDefaultAriaValueText, getMidpoint, getNewValue, getPushedThumbValues, getSliderValue, injectSliderRootContext, provideSliderRootContext, replaceArrayItemAtIndex, resolveThumbCollision, roundValueToStep, validateMinimumDistance, valueArrayToPercentages, valueToPercent };
865
1117
  //# sourceMappingURL=radix-ng-primitives-slider.mjs.map