@spartan-ng/brain 0.0.1-alpha.437 → 0.0.1-alpha.439

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 (45) hide show
  1. package/calendar/index.d.ts +5 -2
  2. package/calendar/lib/brn-calendar-cell-button.directive.d.ts +2 -2
  3. package/calendar/lib/brn-calendar-grid.directive.d.ts +1 -1
  4. package/calendar/lib/brn-calendar.directive.d.ts +4 -1
  5. package/calendar/lib/brn-calendar.token.d.ts +19 -5
  6. package/calendar/lib/mode/brn-calendar-multiple.directive.d.ts +61 -0
  7. package/fesm2022/spartan-ng-brain-calendar.mjs +196 -9
  8. package/fesm2022/spartan-ng-brain-calendar.mjs.map +1 -1
  9. package/fesm2022/spartan-ng-brain-input-otp.mjs +212 -0
  10. package/fesm2022/spartan-ng-brain-input-otp.mjs.map +1 -0
  11. package/fesm2022/spartan-ng-brain-select.mjs +2 -1
  12. package/fesm2022/spartan-ng-brain-select.mjs.map +1 -1
  13. package/fesm2022/spartan-ng-brain-slider.mjs +278 -342
  14. package/fesm2022/spartan-ng-brain-slider.mjs.map +1 -1
  15. package/fesm2022/spartan-ng-brain-toggle-group.mjs +260 -0
  16. package/fesm2022/spartan-ng-brain-toggle-group.mjs.map +1 -0
  17. package/fesm2022/spartan-ng-brain-toggle.mjs +6 -197
  18. package/fesm2022/spartan-ng-brain-toggle.mjs.map +1 -1
  19. package/fesm2022/spartan-ng-brain-tooltip.mjs +18 -8
  20. package/fesm2022/spartan-ng-brain-tooltip.mjs.map +1 -1
  21. package/hlm-tailwind-preset.js +5 -0
  22. package/input-otp/README.md +3 -0
  23. package/input-otp/index.d.ts +10 -0
  24. package/input-otp/lib/brn-input-otp-slot.component.d.ts +14 -0
  25. package/input-otp/lib/brn-input-otp.component.d.ts +57 -0
  26. package/input-otp/lib/brn-input-otp.token.d.ts +5 -0
  27. package/package.json +9 -1
  28. package/slider/index.d.ts +9 -4
  29. package/slider/lib/brn-slider-range.directive.d.ts +7 -0
  30. package/slider/lib/brn-slider-thumb.directive.d.ts +18 -11
  31. package/slider/lib/brn-slider-tick.directive.d.ts +12 -0
  32. package/slider/lib/brn-slider-track.directive.d.ts +8 -136
  33. package/slider/lib/brn-slider-track.token.d.ts +5 -0
  34. package/slider/lib/brn-slider.directive.d.ts +37 -0
  35. package/slider/lib/brn-slider.token.d.ts +4 -0
  36. package/toggle/index.d.ts +0 -7
  37. package/toggle/lib/brn-toggle.directive.d.ts +0 -2
  38. package/toggle-group/README.md +3 -0
  39. package/toggle-group/index.d.ts +15 -0
  40. package/{toggle → toggle-group}/lib/brn-toggle-group.component.d.ts +5 -5
  41. package/toggle-group/lib/brn-toggle-item.directive.d.ts +25 -0
  42. package/tooltip/lib/brn-tooltip-trigger.directive.d.ts +8 -4
  43. package/slider/lib/brn-slider-tick-mark.directive.d.ts +0 -15
  44. package/slider/lib/brn-slider-track-active-fill.directive.d.ts +0 -10
  45. /package/{toggle → toggle-group}/lib/brn-toggle-group.token.d.ts +0 -0
@@ -1,408 +1,344 @@
1
- import { isPlatformBrowser } from '@angular/common';
2
1
  import * as i0 from '@angular/core';
3
- import { InjectionToken, computed, signal, inject, PLATFORM_ID, ElementRef, Renderer2, ChangeDetectorRef, effect, untracked, Directive, input, model, Injector, contentChild, forwardRef } from '@angular/core';
4
- import { Directionality } from '@angular/cdk/bidi';
5
- import { SharedResizeObserver } from '@angular/cdk/observers/private';
6
- import { toObservable } from '@angular/core/rxjs-interop';
2
+ import { InjectionToken, inject, Directive, ElementRef, PLATFORM_ID, computed, HostListener, TemplateRef, ViewContainerRef, effect, ChangeDetectorRef, model, input, numberAttribute, booleanAttribute, linkedSignal, signal } from '@angular/core';
3
+ import { DOCUMENT, isPlatformServer } from '@angular/common';
4
+ import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
5
+ import { fromEvent } from 'rxjs';
6
+ import { switchMap, takeUntil } from 'rxjs/operators';
7
7
  import { NG_VALUE_ACCESSOR } from '@angular/forms';
8
- import { Subject, merge } from 'rxjs';
9
- import { takeUntil, tap, debounceTime } from 'rxjs/operators';
10
8
 
11
- const BRN_SLIDER_INPUT = new InjectionToken('BrnSliderInput');
12
- const BRN_SLIDER_TRACK = new InjectionToken('BrnSliderTrack');
13
- const BRN_SLIDER = new InjectionToken('BrnSlider');
14
- /**
15
- * Directive that adds slider-specific behaviors to an input element inside `<brn-slider>`.
16
- */
17
- class BrnSliderInputDirective {
18
- _onChangeFn;
19
- // eslint-disable-next-line @typescript-eslint/no-empty-function
20
- _onTouchedFn = () => { };
21
- isDisabled = computed(() => (this._slider.disabled() === true ? true : undefined));
22
- valueNow = computed(() => this.value() ?? 0);
23
- valueMin = computed(() => this._slider.min());
24
- valueMax = computed(() => this._slider.max());
25
- ariaLabelledby = computed(() => this._slider.label()?.id);
26
- ariaLabel = computed(() => {
27
- if (!this._slider.ariaLabel() && !this.ariaLabelledby()) {
28
- throw new Error("'ariaLabel' input must be provided as fallback accessibility aria label when no aria-labelledby element is provided.");
9
+ const BrnSliderToken = new InjectionToken('BrnSliderToken');
10
+ function provideBrnSlider(slider) {
11
+ return { provide: BrnSliderToken, useExisting: slider };
12
+ }
13
+ function injectBrnSlider() {
14
+ return inject(BrnSliderToken);
15
+ }
16
+
17
+ class BrnSliderRangeDirective {
18
+ /** Access the slider */
19
+ slider = injectBrnSlider();
20
+ /** @nocollapse */ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.1", ngImport: i0, type: BrnSliderRangeDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
21
+ /** @nocollapse */ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "19.2.1", type: BrnSliderRangeDirective, isStandalone: true, selector: "[brnSliderRange]", host: { properties: { "attr.data-disabled": "slider.disabled()", "style.width.%": "slider.percentage()" } }, ngImport: i0 });
22
+ }
23
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.1", ngImport: i0, type: BrnSliderRangeDirective, decorators: [{
24
+ type: Directive,
25
+ args: [{
26
+ selector: '[brnSliderRange]',
27
+ host: {
28
+ '[attr.data-disabled]': 'slider.disabled()',
29
+ '[style.width.%]': 'slider.percentage()',
30
+ },
31
+ }]
32
+ }] });
33
+
34
+ class BrnSliderThumbDirective {
35
+ slider = injectBrnSlider();
36
+ _document = inject(DOCUMENT);
37
+ _elementRef = inject(ElementRef);
38
+ _platform = inject(PLATFORM_ID);
39
+ /**
40
+ * Offsets the thumb centre point while sliding to ensure it remains
41
+ * within the bounds of the slider when reaching the edges.
42
+ * Based on https://github.com/radix-ui/primitives/blob/main/packages/react/slider/src/slider.tsx
43
+ */
44
+ thumbOffset = computed(() => {
45
+ // we can't compute the offset on the server
46
+ if (isPlatformServer(this._platform)) {
47
+ return this.slider.percentage() + '%';
29
48
  }
30
- return this._slider.ariaLabel();
49
+ const halfWidth = this._elementRef.nativeElement.offsetWidth / 2;
50
+ const offset = this.linearScale([0, 50], [0, halfWidth]);
51
+ const thumbInBoundsOffset = halfWidth - offset(this.slider.percentage());
52
+ const percent = this.slider.percentage();
53
+ return `calc(${percent}% + ${thumbInBoundsOffset}px)`;
31
54
  });
32
- value = signal(0);
33
- isFocused = signal(false);
34
- _platformId = inject(PLATFORM_ID);
35
- _elementRef = inject((ElementRef));
36
- _slider = inject(BRN_SLIDER);
37
- _renderer2 = inject(Renderer2);
38
- _changeDetector = inject(ChangeDetectorRef);
39
55
  constructor() {
40
- effect(() => {
41
- if (isPlatformBrowser(this._platformId)) {
42
- const step = this._slider.step();
43
- const min = this._slider.min();
44
- const max = this._slider.max();
45
- const direction = this._slider.direction();
46
- untracked(() => {
47
- this._updateHostElementStep(step);
48
- this._updateMinValue(min);
49
- this._updateMaxValue(max);
50
- this._updateDirection(direction);
51
- });
52
- }
53
- });
54
- }
55
- onFocus() {
56
- this.isFocused.set(true);
57
- }
58
- onBlur() {
59
- this.isFocused.set(false);
60
- this._onTouchedFn();
56
+ const mousedown = fromEvent(this._elementRef.nativeElement, 'pointerdown');
57
+ const mouseup = fromEvent(this._document, 'pointerup');
58
+ const mousemove = fromEvent(this._document, 'pointermove');
59
+ // Listen for mousedown events on the slider thumb
60
+ mousedown
61
+ .pipe(switchMap(() => mousemove.pipe(takeUntil(mouseup))), takeUntilDestroyed())
62
+ .subscribe(this.dragThumb.bind(this));
61
63
  }
62
- onInput() {
63
- this._updateValue();
64
- }
65
- onChange() {
66
- this._updateValue();
67
- }
68
- writeValue(obj) {
69
- this.value.set(obj);
70
- this._updateHostElementValue(obj);
71
- }
72
- registerOnChange(fn) {
73
- this._onChangeFn = fn;
74
- }
75
- registerOnTouched(fn) {
76
- this._onTouchedFn = fn;
64
+ /** @internal */
65
+ dragThumb(event) {
66
+ if (this.slider.disabled()) {
67
+ return;
68
+ }
69
+ const rect = this.slider.track()?.elementRef.nativeElement.getBoundingClientRect();
70
+ if (!rect) {
71
+ return;
72
+ }
73
+ const percentage = (event.clientX - rect.left) / rect.width;
74
+ this.slider.setValue(this.slider.min() + (this.slider.max() - this.slider.min()) * Math.max(0, Math.min(1, percentage)));
77
75
  }
78
76
  /**
79
- * Sets the disabled state of the slider.
80
- * @param isDisabled The new disabled state
77
+ * Handle keyboard events.
78
+ * @param event
81
79
  */
82
- setDisabledState(isDisabled) {
83
- /** Disable slider only when slider component
84
- * is not disabled and isDisabled param is explicitly set to true
85
- */
86
- if (isDisabled && !this._slider.disabled()) {
87
- this._slider.disabled.set(isDisabled);
80
+ handleKeydown(event) {
81
+ const dir = getComputedStyle(this._elementRef.nativeElement).direction;
82
+ let multiplier = event.shiftKey ? 10 : 1;
83
+ const value = this.slider.value();
84
+ // if the slider is RTL, flip the multiplier
85
+ if (dir === 'rtl') {
86
+ multiplier = event.shiftKey ? -10 : -1;
87
+ }
88
+ switch (event.key) {
89
+ case 'ArrowLeft':
90
+ this.slider.setValue(Math.max(value - this.slider.step() * multiplier, this.slider.min()));
91
+ event.preventDefault();
92
+ break;
93
+ case 'ArrowRight':
94
+ this.slider.setValue(Math.min(value + this.slider.step() * multiplier, this.slider.max()));
95
+ event.preventDefault();
96
+ break;
97
+ case 'Home':
98
+ this.slider.setValue(this.slider.min());
99
+ event.preventDefault();
100
+ break;
101
+ case 'End':
102
+ this.slider.setValue(this.slider.max());
103
+ event.preventDefault();
104
+ break;
88
105
  }
89
106
  }
90
- _updateHostElementValue(value) {
91
- this._elementRef.nativeElement.value = value?.toString() ?? '0';
92
- this._changeDetector.detectChanges();
93
- }
94
- _updateValue() {
95
- this.value.set(this._elementRef.nativeElement.valueAsNumber);
96
- this._onChangeFn?.(this.value());
97
- }
98
- _updateHostElementStep(step) {
99
- this._elementRef.nativeElement.step = step.toString();
100
- this._updateValue();
101
- }
102
- _updateMinValue(value) {
103
- this._elementRef.nativeElement.min = value.toString();
104
- this._updateValue();
105
- }
106
- _updateMaxValue(value) {
107
- this._elementRef.nativeElement.max = value.toString();
108
- this._updateValue();
109
- }
110
- _updateDirection(direction) {
111
- this._renderer2.setStyle(this._elementRef.nativeElement, 'direction', direction);
107
+ linearScale(input, output) {
108
+ return (value) => {
109
+ if (input[0] === input[1] || output[0] === output[1])
110
+ return output[0];
111
+ const ratio = (output[1] - output[0]) / (input[1] - input[0]);
112
+ return output[0] + ratio * (value - input[0]);
113
+ };
112
114
  }
113
- /** @nocollapse */ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.1", ngImport: i0, type: BrnSliderInputDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
114
- /** @nocollapse */ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "19.2.1", type: BrnSliderInputDirective, isStandalone: true, selector: "input[brnSliderInput]", host: { attributes: { "type": "range", "role": "slider", "aria-orientation": "horizontal" }, listeners: { "change": "onChange()", "input": "onInput()", "focus": "onFocus()", "blur": "onBlur()" }, properties: { "attr.disabled": "isDisabled()", "attr.aria-valuenow": "valueNow()", "attr.aria-valuemin": "valueMin()", "attr.aria-valuemax": "valueMax()", "attr.aria-labelledby": "ariaLabelledby()", "attr.aria-label": "ariaLabel()" } }, providers: [
115
- {
116
- provide: NG_VALUE_ACCESSOR,
117
- useExisting: BrnSliderInputDirective,
118
- multi: true,
119
- },
120
- {
121
- provide: BRN_SLIDER_INPUT,
122
- useExisting: BrnSliderInputDirective,
123
- },
124
- ], ngImport: i0 });
115
+ /** @nocollapse */ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.1", ngImport: i0, type: BrnSliderThumbDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
116
+ /** @nocollapse */ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "19.2.1", type: BrnSliderThumbDirective, isStandalone: true, selector: "[brnSliderThumb]", host: { attributes: { "role": "slider" }, listeners: { "keydown": "handleKeydown($event)" }, properties: { "attr.aria-valuenow": "slider.value()", "attr.aria-valuemin": "slider.min()", "attr.aria-valuemax": "slider.max()", "attr.tabindex": "slider.disabled() ? -1 : 0", "attr.data-disabled": "slider.disabled()", "style.inset-inline-start": "thumbOffset()" } }, ngImport: i0 });
125
117
  }
126
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.1", ngImport: i0, type: BrnSliderInputDirective, decorators: [{
118
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.1", ngImport: i0, type: BrnSliderThumbDirective, decorators: [{
127
119
  type: Directive,
128
120
  args: [{
129
- selector: 'input[brnSliderInput]',
121
+ selector: '[brnSliderThumb]',
130
122
  host: {
131
- type: 'range',
132
123
  role: 'slider',
133
- '(change)': 'onChange()',
134
- '(input)': 'onInput()',
135
- '(focus)': 'onFocus()',
136
- '(blur)': 'onBlur()',
137
- '[attr.disabled]': 'isDisabled()',
138
- '[attr.aria-valuenow]': 'valueNow()',
139
- '[attr.aria-valuemin]': 'valueMin()',
140
- '[attr.aria-valuemax]': 'valueMax()',
141
- '[attr.aria-labelledby]': 'ariaLabelledby()',
142
- '[attr.aria-label]': 'ariaLabel()',
143
- 'aria-orientation': 'horizontal',
124
+ '[attr.aria-valuenow]': 'slider.value()',
125
+ '[attr.aria-valuemin]': 'slider.min()',
126
+ '[attr.aria-valuemax]': 'slider.max()',
127
+ '[attr.tabindex]': 'slider.disabled() ? -1 : 0',
128
+ '[attr.data-disabled]': 'slider.disabled()',
129
+ '[style.inset-inline-start]': 'thumbOffset()',
144
130
  },
145
- providers: [
146
- {
147
- provide: NG_VALUE_ACCESSOR,
148
- useExisting: BrnSliderInputDirective,
149
- multi: true,
150
- },
151
- {
152
- provide: BRN_SLIDER_INPUT,
153
- useExisting: BrnSliderInputDirective,
154
- },
155
- ],
156
- standalone: true,
157
131
  }]
158
- }], ctorParameters: () => [] });
159
- class BrnSliderDirective {
160
- label = input(null);
161
- ariaLabel = input(null);
162
- /** Used only as an input. */
163
- dir = input('ltr');
164
- disabled = model(false, { alias: 'brnSliderDisabled' });
165
- min = model(0);
166
- max = model(100);
167
- step = input(1);
168
- showTickMarks = input(false);
169
- direction = signal('ltr');
170
- _destroyed = new Subject();
171
- _injector = inject(Injector);
172
- _dir = inject(Directionality);
173
- _platformId = inject(PLATFORM_ID);
174
- brnSliderTrack = contentChild(BRN_SLIDER_TRACK);
175
- ngAfterViewInit() {
176
- if (isPlatformBrowser(this._platformId)) {
177
- this._updateDirectionality();
178
- }
132
+ }], ctorParameters: () => [], propDecorators: { handleKeydown: [{
133
+ type: HostListener,
134
+ args: ['keydown', ['$event']]
135
+ }] } });
136
+
137
+ class BrnSliderTickDirective {
138
+ _slider = injectBrnSlider();
139
+ _templateRef = inject(TemplateRef);
140
+ _viewContainer = inject(ViewContainerRef);
141
+ _ticks = [];
142
+ constructor() {
143
+ effect(() => {
144
+ const ticks = this._slider.ticks();
145
+ // remove any existing ticks
146
+ this._ticks.forEach((tick) => this._viewContainer.remove(this._viewContainer.indexOf(tick)));
147
+ // create new ticks
148
+ this._ticks = [];
149
+ ticks.forEach((tick, index) => {
150
+ const view = this._viewContainer.createEmbeddedView(this._templateRef, {
151
+ $implicit: tick,
152
+ index,
153
+ position: (index / (ticks.length - 1)) * 100,
154
+ });
155
+ this._ticks.push(view);
156
+ });
157
+ });
179
158
  }
180
159
  ngOnDestroy() {
181
- this._destroyed.next();
182
- this._destroyed.complete();
183
- }
184
- /**
185
- * The method is responsible of setting the current direction state
186
- * based on the latest 'dir' input or bidi state change. The only
187
- * source of truth for slider direction state is the 'direction' signal
188
- * and all interested consumers of it, will consume this interface exposed signal.
189
- */
190
- _updateDirectionality() {
191
- merge(toObservable(this.dir, { injector: this._injector }), this._dir.change)
192
- .pipe(takeUntil(this._destroyed), tap((dir) => this.direction.set(dir)))
193
- .subscribe();
160
+ this._ticks.forEach((tick) => this._viewContainer.remove(this._viewContainer.indexOf(tick)));
194
161
  }
195
- /** @nocollapse */ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.1", ngImport: i0, type: BrnSliderDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
196
- /** @nocollapse */ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.2.0", version: "19.2.1", type: BrnSliderDirective, isStandalone: true, selector: "[brnSlider]", inputs: { label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null }, dir: { classPropertyName: "dir", publicName: "dir", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "brnSliderDisabled", 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 }, showTickMarks: { classPropertyName: "showTickMarks", publicName: "showTickMarks", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { disabled: "brnSliderDisabledChange", min: "minChange", max: "maxChange" }, providers: [
197
- {
198
- provide: BRN_SLIDER,
199
- useExisting: BrnSliderDirective,
200
- },
201
- ], queries: [{ propertyName: "brnSliderTrack", first: true, predicate: BRN_SLIDER_TRACK, descendants: true, isSignal: true }], exportAs: ["brnSlider"], ngImport: i0 });
162
+ /** @nocollapse */ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.1", ngImport: i0, type: BrnSliderTickDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
163
+ /** @nocollapse */ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "19.2.1", type: BrnSliderTickDirective, isStandalone: true, selector: "[brnSliderTick]", ngImport: i0 });
202
164
  }
203
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.1", ngImport: i0, type: BrnSliderDirective, decorators: [{
165
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.1", ngImport: i0, type: BrnSliderTickDirective, decorators: [{
204
166
  type: Directive,
205
167
  args: [{
206
- selector: '[brnSlider]',
207
- standalone: true,
208
- providers: [
209
- {
210
- provide: BRN_SLIDER,
211
- useExisting: BrnSliderDirective,
212
- },
213
- ],
214
- exportAs: 'brnSlider',
168
+ selector: '[brnSliderTick]',
215
169
  }]
216
- }] });
170
+ }], ctorParameters: () => [] });
171
+
172
+ const BrnSliderTrackToken = new InjectionToken('BrnSliderTrackToken');
173
+ function provideBrnSliderTrack(slider) {
174
+ return { provide: BrnSliderTrackToken, useExisting: slider };
175
+ }
176
+ function injectBrnSliderTrack() {
177
+ return inject(BrnSliderTrackToken);
178
+ }
179
+
217
180
  class BrnSliderTrackDirective {
218
- hostElementWidth = signal(0);
219
- _destroyed = new Subject();
220
- _slider = inject(BRN_SLIDER);
221
- _platformId = inject(PLATFORM_ID);
222
- _elementRef = inject((ElementRef));
223
- _sharedResizeObserver = inject(SharedResizeObserver);
224
- brnSliderInput = contentChild(forwardRef(() => BRN_SLIDER_INPUT));
225
- activeTrackPercentage = computed(() => {
226
- const inputValue = this.brnSliderInput()?.value();
227
- if (this._slider.min() >= this._slider.max() || !inputValue || inputValue < this._slider.min()) {
228
- return 0;
229
- }
230
- if (inputValue > this._slider.max()) {
231
- return 1;
181
+ /** Access the slider */
182
+ slider = injectBrnSlider();
183
+ /** @internal Access the slider track */
184
+ elementRef = inject(ElementRef);
185
+ constructor() {
186
+ this.slider.track.set(this);
187
+ }
188
+ moveThumbToPoint(event) {
189
+ if (this.slider.disabled()) {
190
+ return;
232
191
  }
233
- return (inputValue - this._slider.min()) / (this._slider.max() - this._slider.min());
192
+ const position = event.clientX;
193
+ const rect = this.elementRef.nativeElement.getBoundingClientRect();
194
+ const percentage = (position - rect.left) / rect.width;
195
+ // update the value based on the position
196
+ this.slider.setValue(this.slider.min() + (this.slider.max() - this.slider.min()) * percentage);
197
+ }
198
+ /** @nocollapse */ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.1", ngImport: i0, type: BrnSliderTrackDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
199
+ /** @nocollapse */ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "19.2.1", type: BrnSliderTrackDirective, isStandalone: true, selector: "[brnSliderTrack]", host: { listeners: { "mousedown": "moveThumbToPoint($event)" }, properties: { "attr.data-disabled": "slider.disabled()" } }, providers: [provideBrnSliderTrack(BrnSliderTrackDirective)], ngImport: i0 });
200
+ }
201
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.1", ngImport: i0, type: BrnSliderTrackDirective, decorators: [{
202
+ type: Directive,
203
+ args: [{
204
+ selector: '[brnSliderTrack]',
205
+ providers: [provideBrnSliderTrack(BrnSliderTrackDirective)],
206
+ host: {
207
+ '[attr.data-disabled]': 'slider.disabled()',
208
+ },
209
+ }]
210
+ }], ctorParameters: () => [], propDecorators: { moveThumbToPoint: [{
211
+ type: HostListener,
212
+ args: ['mousedown', ['$event']]
213
+ }] } });
214
+
215
+ class BrnSliderDirective {
216
+ _changeDetectorRef = inject(ChangeDetectorRef);
217
+ _elementRef = inject(ElementRef);
218
+ value = model(0);
219
+ min = input(0, {
220
+ transform: numberAttribute,
234
221
  });
235
- tickMarkTrackWidth = computed(() => {
236
- if (!this._slider.showTickMarks()) {
237
- return 0;
238
- }
239
- const sliderStep = this._slider.step();
240
- const sliderMax = this._slider.max();
241
- const sliderMin = this._slider.min();
242
- const step = sliderStep && sliderStep > 0 ? sliderStep : 1;
243
- const maxValue = Math.floor((sliderMax - sliderMin) / step) * step + sliderMin;
244
- const percentage = (maxValue - sliderMin) / (sliderMax - sliderMin);
245
- return this.hostElementWidth() * percentage;
222
+ max = input(100, {
223
+ transform: numberAttribute,
224
+ });
225
+ step = input(1, {
226
+ transform: numberAttribute,
227
+ });
228
+ _disabled = input(false, {
229
+ alias: 'disabled',
230
+ transform: booleanAttribute,
231
+ });
232
+ /** Whether we should show tick marks */
233
+ showTicks = input(false, {
234
+ transform: booleanAttribute,
246
235
  });
247
- tickMarks = computed(() => {
248
- const inputValue = this.brnSliderInput()?.value();
249
- if (!this._slider.showTickMarks() || inputValue === null || inputValue === undefined) {
236
+ /** @internal */
237
+ ticks = computed(() => {
238
+ const value = this.value();
239
+ if (!this.showTicks()) {
250
240
  return [];
251
241
  }
252
- let numActive = Math.max(Math.floor((inputValue - this._slider.min()) / this._slider.step()), 0);
253
- let numInactive = Math.max(Math.floor((this._slider.max() - inputValue) / this._slider.step()), 0);
254
- this._slider.direction() === 'rtl' ? numInactive++ : numActive++;
242
+ let numActive = Math.max(Math.floor((value - this.min()) / this.step()), 0);
243
+ let numInactive = Math.max(Math.floor((this.max() - value) / this.step()), 0);
244
+ const direction = getComputedStyle(this._elementRef.nativeElement).direction;
245
+ direction === 'rtl' ? numInactive++ : numActive++;
255
246
  return Array(numActive).fill(true).concat(Array(numInactive).fill(false));
256
247
  });
257
- ngAfterViewInit() {
258
- if (isPlatformBrowser(this._platformId)) {
259
- this._storeDimensions();
260
- this._onResize();
248
+ /** @internal */
249
+ disabled = linkedSignal(() => this._disabled());
250
+ /** @internal */
251
+ percentage = computed(() => ((this.value() - this.min()) / (this.max() - this.min())) * 100);
252
+ /** @internal Store the on change callback */
253
+ _onChange;
254
+ /** @internal Store the on touched callback */
255
+ _onTouched;
256
+ /** @internal Store the track */
257
+ track = signal(null);
258
+ ngOnInit() {
259
+ // ensure the value is within the min and max range
260
+ if (this.value() < this.min()) {
261
+ this.value.set(this.min());
262
+ }
263
+ if (this.value() > this.max()) {
264
+ this.value.set(this.max());
261
265
  }
262
266
  }
263
- ngOnDestroy() {
264
- this._destroyed.next();
265
- this._destroyed.complete();
267
+ registerOnChange(fn) {
268
+ this._onChange = fn;
266
269
  }
267
- _onResize() {
268
- this._sharedResizeObserver
269
- .observe(this._elementRef.nativeElement)
270
- .pipe(debounceTime(32), takeUntil(this._destroyed), tap(() => this._storeDimensions()))
271
- .subscribe();
270
+ registerOnTouched(fn) {
271
+ this._onTouched = fn;
272
272
  }
273
- _storeDimensions() {
274
- const computedStyle = getComputedStyle(this._elementRef.nativeElement);
275
- this.hostElementWidth.set(this._elementRef.nativeElement.offsetWidth -
276
- Number.parseFloat(computedStyle.paddingLeft) -
277
- Number.parseFloat(computedStyle.paddingRight));
273
+ setDisabledState(isDisabled) {
274
+ this.disabled.set(isDisabled);
278
275
  }
279
- /** @nocollapse */ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.1", ngImport: i0, type: BrnSliderTrackDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
280
- /** @nocollapse */ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.2.0", version: "19.2.1", type: BrnSliderTrackDirective, isStandalone: true, selector: "[brnSliderTrack]", providers: [
276
+ writeValue(value) {
277
+ const clampedValue = clamp(value, [this.min(), this.max()]);
278
+ this.value.set(clampedValue);
279
+ if (value !== clampedValue) {
280
+ this._onChange?.(clampedValue);
281
+ }
282
+ this._changeDetectorRef.detectChanges();
283
+ }
284
+ setValue(value) {
285
+ const decimalCount = getDecimalCount(this.step());
286
+ const snapToStep = roundValue(Math.round((value - this.min()) / this.step()) * this.step() + this.min(), decimalCount);
287
+ value = clamp(snapToStep, [this.min(), this.max()]);
288
+ this.value.set(value);
289
+ this._onChange?.(value);
290
+ }
291
+ /** @nocollapse */ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.1", ngImport: i0, type: BrnSliderDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
292
+ /** @nocollapse */ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "19.2.1", type: BrnSliderDirective, isStandalone: true, selector: "[brnSlider]", inputs: { value: { classPropertyName: "value", publicName: "value", 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 }, _disabled: { classPropertyName: "_disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, showTicks: { classPropertyName: "showTicks", publicName: "showTicks", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { value: "valueChange" }, host: { attributes: { "aria-orientation": "horizontal" }, listeners: { "focusout": "_onTouched?.()" } }, providers: [
293
+ provideBrnSlider(BrnSliderDirective),
281
294
  {
282
- provide: BRN_SLIDER_TRACK,
283
- useExisting: BrnSliderTrackDirective,
295
+ provide: NG_VALUE_ACCESSOR,
296
+ useExisting: BrnSliderDirective,
297
+ multi: true,
284
298
  },
285
- ], queries: [{ propertyName: "brnSliderInput", first: true, predicate: i0.forwardRef(() => BRN_SLIDER_INPUT), descendants: true, isSignal: true }], ngImport: i0 });
299
+ ], exportAs: ["brnSlider"], ngImport: i0 });
286
300
  }
287
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.1", ngImport: i0, type: BrnSliderTrackDirective, decorators: [{
301
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.1", ngImport: i0, type: BrnSliderDirective, decorators: [{
288
302
  type: Directive,
289
303
  args: [{
290
- selector: '[brnSliderTrack]',
291
- standalone: true,
304
+ selector: '[brnSlider]',
305
+ exportAs: 'brnSlider',
292
306
  providers: [
307
+ provideBrnSlider(BrnSliderDirective),
293
308
  {
294
- provide: BRN_SLIDER_TRACK,
295
- useExisting: BrnSliderTrackDirective,
309
+ provide: NG_VALUE_ACCESSOR,
310
+ useExisting: BrnSliderDirective,
311
+ multi: true,
296
312
  },
297
313
  ],
314
+ host: {
315
+ 'aria-orientation': 'horizontal',
316
+ '(focusout)': '_onTouched?.()',
317
+ },
298
318
  }]
299
319
  }] });
300
-
301
- class BrnSliderThumbDirective {
302
- _platformId = inject(PLATFORM_ID);
303
- _elementRef = inject((ElementRef));
304
- _slider = inject(BRN_SLIDER);
305
- translateX = computed(() => this._calcTranslateX());
306
- constructor() {
307
- effect(() => {
308
- if (isPlatformBrowser(this._platformId)) {
309
- this._updateTranslateX(this.translateX());
310
- }
311
- });
312
- }
313
- _calcTranslateX() {
314
- const activeTrackPercentage = this._slider.brnSliderTrack()?.activeTrackPercentage();
315
- const trackHostElementWidth = this._slider.brnSliderTrack()?.hostElementWidth();
316
- if (!activeTrackPercentage || !trackHostElementWidth) {
317
- return 0;
318
- }
319
- if (this._slider.direction() === 'rtl') {
320
- return -(activeTrackPercentage * trackHostElementWidth);
321
- }
322
- return activeTrackPercentage * trackHostElementWidth;
323
- }
324
- _updateTranslateX(value) {
325
- this._elementRef.nativeElement.style.transform = `translate(${value}px, -50%)`;
326
- }
327
- /** @nocollapse */ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.1", ngImport: i0, type: BrnSliderThumbDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
328
- /** @nocollapse */ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "19.2.1", type: BrnSliderThumbDirective, isStandalone: true, selector: "[brnSliderThumb]", ngImport: i0 });
320
+ function roundValue(value, decimalCount) {
321
+ const rounder = Math.pow(10, decimalCount);
322
+ return Math.round(value * rounder) / rounder;
329
323
  }
330
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.1", ngImport: i0, type: BrnSliderThumbDirective, decorators: [{
331
- type: Directive,
332
- args: [{
333
- selector: '[brnSliderThumb]',
334
- standalone: true,
335
- }]
336
- }], ctorParameters: () => [] });
337
-
338
- class BrnSliderTickMarkDirective {
339
- _platformId = inject(PLATFORM_ID);
340
- data = input();
341
- _sliderTrack = inject(BRN_SLIDER_TRACK);
342
- _elementRef = inject((ElementRef));
343
- _slider = inject(BRN_SLIDER);
344
- constructor() {
345
- effect(() => {
346
- if (isPlatformBrowser(this._platformId)) {
347
- this._updateTranslateX();
348
- }
349
- });
350
- }
351
- _updateTranslateX() {
352
- const data = this.data();
353
- if (!data) {
354
- return;
355
- }
356
- const translateX = data.tickMarkIndex * (this._sliderTrack.tickMarkTrackWidth() / (data.totalTickMarks - 1));
357
- this._elementRef.nativeElement.style.transform =
358
- this._slider.direction() === 'rtl' ? `translateX(${-translateX}px)` : `translateX(${translateX}px)`;
359
- }
360
- /** @nocollapse */ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.1", ngImport: i0, type: BrnSliderTickMarkDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
361
- /** @nocollapse */ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "19.2.1", type: BrnSliderTickMarkDirective, isStandalone: true, selector: "[brnSliderTickMark]", inputs: { data: { classPropertyName: "data", publicName: "data", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0 });
324
+ function getDecimalCount(value) {
325
+ return (String(value).split('.')[1] || '').length;
362
326
  }
363
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.1", ngImport: i0, type: BrnSliderTickMarkDirective, decorators: [{
364
- type: Directive,
365
- args: [{
366
- selector: '[brnSliderTickMark]',
367
- standalone: true,
368
- }]
369
- }], ctorParameters: () => [] });
370
-
371
- class BrnSliderTrackActiveFillDirective {
372
- _platformId = inject(PLATFORM_ID);
373
- _elementRef = inject((ElementRef));
374
- _sliderTrack = inject(BRN_SLIDER_TRACK);
375
- constructor() {
376
- effect(() => {
377
- if (isPlatformBrowser(this._platformId)) {
378
- this._updateActiveTrackPercentage(this._sliderTrack.activeTrackPercentage());
379
- }
380
- });
381
- }
382
- _updateActiveTrackPercentage(percentage) {
383
- this._elementRef.nativeElement.style.transform = `scaleX(${percentage})`;
384
- }
385
- /** @nocollapse */ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.1", ngImport: i0, type: BrnSliderTrackActiveFillDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
386
- /** @nocollapse */ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "19.2.1", type: BrnSliderTrackActiveFillDirective, isStandalone: true, selector: "[brnSliderTrackActiveFill]", ngImport: i0 });
327
+ function clamp(value, [min, max]) {
328
+ return Math.min(max, Math.max(min, value));
387
329
  }
388
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.1", ngImport: i0, type: BrnSliderTrackActiveFillDirective, decorators: [{
389
- type: Directive,
390
- args: [{
391
- selector: '[brnSliderTrackActiveFill]',
392
- standalone: true,
393
- }]
394
- }], ctorParameters: () => [] });
395
330
 
396
331
  const BrnSliderImports = [
397
332
  BrnSliderDirective,
398
333
  BrnSliderTrackDirective,
399
- BrnSliderInputDirective,
400
334
  BrnSliderThumbDirective,
335
+ BrnSliderRangeDirective,
336
+ BrnSliderTickDirective,
401
337
  ];
402
338
 
403
339
  /**
404
340
  * Generated bundle index. Do not edit.
405
341
  */
406
342
 
407
- export { BRN_SLIDER, BRN_SLIDER_INPUT, BRN_SLIDER_TRACK, BrnSliderDirective, BrnSliderImports, BrnSliderInputDirective, BrnSliderThumbDirective, BrnSliderTickMarkDirective, BrnSliderTrackActiveFillDirective, BrnSliderTrackDirective };
343
+ export { BrnSliderDirective, BrnSliderImports, BrnSliderRangeDirective, BrnSliderThumbDirective, BrnSliderTickDirective, BrnSliderTrackDirective, injectBrnSlider, provideBrnSlider };
408
344
  //# sourceMappingURL=spartan-ng-brain-slider.mjs.map