1 | import { Directive, TemplateRef, EventEmitter, Component, ChangeDetectionStrategy, ElementRef, Inject, ChangeDetectorRef, HostBinding, Input, Output, ViewChild, HostListener, NgModule } from '@angular/core';
|
2 | import { Directionality, BidiModule } from '@angular/cdk/bidi';
|
3 | import { OverlayModule } from '@angular/cdk/overlay';
|
4 | import { DOCUMENT, CommonModule } from '@angular/common';
|
5 | import { PAGE_DOWN, PAGE_UP, DOWN_ARROW, UP_ARROW, LEFT_ARROW, RIGHT_ARROW } from '@angular/cdk/keycodes';
|
6 | import { map, takeUntil, startWith, share, debounceTime } from 'rxjs/operators';
|
7 | import { fromEvent } from 'rxjs';
|
8 |
|
9 |
|
10 |
|
11 |
|
12 |
|
13 |
|
14 |
|
15 |
|
16 | class BMSliderValueTooltipDirective {
|
17 | |
18 |
|
19 |
|
20 | constructor(templateRef) {
|
21 | this.templateRef = templateRef;
|
22 | }
|
23 | }
|
24 | BMSliderValueTooltipDirective.decorators = [
|
25 | { type: Directive, args: [{
|
26 | selector: '[bmSliderValueTooltip]',
|
27 | },] }
|
28 | ];
|
29 |
|
30 | BMSliderValueTooltipDirective.ctorParameters = () => [
|
31 | { type: TemplateRef }
|
32 | ];
|
33 |
|
34 |
|
35 |
|
36 |
|
37 |
|
38 |
|
39 |
|
40 |
|
41 |
|
42 |
|
43 |
|
44 |
|
45 | function dragObservable(mousedownEvent, doc = document) {
|
46 |
|
47 | let container = ( (mousedownEvent.target));
|
48 | while (container.tagName !== 'BODY' &&
|
49 | getComputedStyle(container).getPropertyValue('position') !== 'static') {
|
50 | container = ( (container.parentElement));
|
51 | }
|
52 | const { left, width } = container.getBoundingClientRect();
|
53 |
|
54 | const offsetX = mousedownEvent.offsetX;
|
55 |
|
56 | const mouseup = fromEvent(doc, 'mouseup');
|
57 |
|
58 | const mousemove = fromEvent(doc, 'mousemove');
|
59 | return mousemove.pipe(map(( |
60 |
|
61 |
|
62 |
|
63 | mousemoveEvent => {
|
64 | mousemoveEvent.preventDefault();
|
65 | return Math.min(1, Math.max(0, (mousemoveEvent.clientX - offsetX - left) / width));
|
66 | })), takeUntil(mouseup));
|
67 | }
|
68 |
|
69 |
|
70 |
|
71 |
|
72 |
|
73 |
|
74 |
|
75 |
|
76 |
|
77 |
|
78 |
|
79 |
|
80 | function mouseoverRelativeCoordinatesObservable(mouseenterEvent, doc = document) {
|
81 | mouseenterEvent.preventDefault();
|
82 |
|
83 | let container = ( (mouseenterEvent.target));
|
84 | while (container.tagName !== 'BODY' &&
|
85 | getComputedStyle(container).getPropertyValue('position') !== 'static') {
|
86 | container = ( (container.parentElement));
|
87 | }
|
88 | const { left, width } = container.getBoundingClientRect();
|
89 |
|
90 | const mouseleave = fromEvent(container, 'mouseleave');
|
91 |
|
92 | const mousemove = fromEvent(doc, 'mousemove');
|
93 | return mousemove.pipe(map(( |
94 |
|
95 |
|
96 |
|
97 | mousemoveEvent => {
|
98 | mousemoveEvent.preventDefault();
|
99 | return Math.min(1, Math.max(0, (mousemoveEvent.clientX - left) / width));
|
100 | })), takeUntil(mouseleave), startWith(Math.min(1, Math.max(0, (mouseenterEvent.clientX - left) / width))));
|
101 | }
|
102 |
|
103 |
|
104 |
|
105 |
|
106 |
|
107 |
|
108 |
|
109 |
|
110 | class BMSliderComponent {
|
111 | |
112 |
|
113 |
|
114 |
|
115 |
|
116 |
|
117 | constructor(_elementRef, _document, _cdr, _dir) {
|
118 | this._elementRef = _elementRef;
|
119 | this._document = _document;
|
120 | this._cdr = _cdr;
|
121 | this._dir = _dir;
|
122 | this.min = 0;
|
123 | this.tabindex = 0;
|
124 | this.role = 'slider';
|
125 | this.change = new EventEmitter();
|
126 | this.dragStart = new EventEmitter();
|
127 | this.dragEnd = new EventEmitter();
|
128 | this._isDragging = false;
|
129 | this._isOver = false;
|
130 | }
|
131 | |
132 |
|
133 |
|
134 |
|
135 | set value(v) {
|
136 | this._value = v;
|
137 | }
|
138 | |
139 |
|
140 |
|
141 | get value() {
|
142 | return this._value;
|
143 | }
|
144 | |
145 |
|
146 |
|
147 |
|
148 | writeValue(v) {
|
149 | this.value = v;
|
150 | }
|
151 | |
152 |
|
153 |
|
154 |
|
155 | registerOnChange(fn) {
|
156 | this._onChangeCB = fn;
|
157 | }
|
158 | |
159 |
|
160 |
|
161 |
|
162 | registerOnTouched(fn) {
|
163 | this._onTouchedCB = fn;
|
164 | }
|
165 | |
166 |
|
167 |
|
168 |
|
169 | setDisabledState(isDisabled) {
|
170 | this.disabled = isDisabled;
|
171 | }
|
172 | |
173 |
|
174 |
|
175 |
|
176 | onMouseenter($event) {
|
177 | if (this.disabled) {
|
178 | return;
|
179 | }
|
180 | this._isOver = true;
|
181 | mouseoverRelativeCoordinatesObservable($event, this._document).subscribe({
|
182 | next: ( |
183 |
|
184 |
|
185 |
|
186 | val => {
|
187 | this.pointerTime = val * (this.max || 0);
|
188 |
|
189 | const overTime = Math.max(0, this.getRelativeLeft(this.pointerTime) -
|
190 | this.getRelativeLeft(this.value));
|
191 | if (overTime > 0 &&
|
192 | this._previewBar &&
|
193 | this._previewBar.nativeElement) {
|
194 |
|
195 | const relativeLeft = this.getRelativeLeft(this.value);
|
196 | this._previewBar.nativeElement.style.left = `${relativeLeft}px`;
|
197 | this._previewBar.nativeElement.style.width = `${overTime}px`;
|
198 | }
|
199 | this._cdr.markForCheck();
|
200 | }),
|
201 | error: ( |
202 |
|
203 |
|
204 | () => {
|
205 | this._isOver = false;
|
206 | }),
|
207 | complete: ( |
208 |
|
209 |
|
210 | () => {
|
211 | this._isOver = false;
|
212 | this._cdr.markForCheck();
|
213 | }),
|
214 | });
|
215 | }
|
216 | |
217 |
|
218 |
|
219 |
|
220 | onClick(e) {
|
221 | if (!this._isDragging) {
|
222 | e.preventDefault();
|
223 | const { offsetX } = e;
|
224 | const { width } = this._elementRef.nativeElement.getBoundingClientRect();
|
225 |
|
226 | const percent = offsetX / width;
|
227 | this.value = percent * (this.max || 0);
|
228 | this.change.emit(this.value);
|
229 | }
|
230 | }
|
231 | |
232 |
|
233 |
|
234 | onFocusin() {
|
235 | if (this._onTouchedCB) {
|
236 | this._onTouchedCB();
|
237 | }
|
238 | }
|
239 |
|
240 | |
241 |
|
242 |
|
243 |
|
244 | onKeydown(e) {
|
245 | if (this.disabled) {
|
246 | return;
|
247 | }
|
248 |
|
249 | let emitChanges = false;
|
250 | if (this.max) {
|
251 |
|
252 | const smallStep = (this.max || 1) / 100;
|
253 | switch (e.keyCode) {
|
254 | case PAGE_DOWN: {
|
255 | e.preventDefault();
|
256 | this._value = Math.max(0, this._value - 10 * smallStep);
|
257 | emitChanges = true;
|
258 | break;
|
259 | }
|
260 | case PAGE_UP: {
|
261 | e.preventDefault();
|
262 | this._value = Math.min(this.max, this._value + 10 * smallStep);
|
263 | emitChanges = true;
|
264 | break;
|
265 | }
|
266 | case DOWN_ARROW: {
|
267 | e.preventDefault();
|
268 | this._value = Math.max(0, this._value - smallStep);
|
269 | emitChanges = true;
|
270 | break;
|
271 | }
|
272 | case UP_ARROW: {
|
273 | e.preventDefault();
|
274 | this._value = Math.min(this.max, this._value + smallStep);
|
275 | emitChanges = true;
|
276 | break;
|
277 | }
|
278 | case LEFT_ARROW: {
|
279 | e.preventDefault();
|
280 | this._value = Math.min(Math.max(0, this._value +
|
281 | (this._dir.value === 'ltr' ? -smallStep : smallStep)), this.max);
|
282 | emitChanges = true;
|
283 | break;
|
284 | }
|
285 | case RIGHT_ARROW: {
|
286 | e.preventDefault();
|
287 | this._value = Math.min(Math.max(0, this._value +
|
288 | (this._dir.value === 'ltr' ? smallStep : -smallStep)), this.max);
|
289 | emitChanges = true;
|
290 | break;
|
291 | }
|
292 | default:
|
293 | }
|
294 | }
|
295 | if (emitChanges) {
|
296 | if (this._onChangeCB) {
|
297 | this._onChangeCB(this.value);
|
298 | }
|
299 | this.change.emit(this.value);
|
300 | }
|
301 | }
|
302 | |
303 |
|
304 |
|
305 |
|
306 | getRelativeLeft(value) {
|
307 | if (this._elementRef.nativeElement && value && this.max) {
|
308 | const { width } = this._elementRef.nativeElement.getBoundingClientRect();
|
309 |
|
310 | const relative = value / (this.max - this.min);
|
311 | return relative * width;
|
312 | }
|
313 | return 0;
|
314 | }
|
315 | |
316 |
|
317 |
|
318 | showThumb() {
|
319 | return this._isDragging || this._isOver;
|
320 | }
|
321 | |
322 |
|
323 |
|
324 | showPreviewBar() {
|
325 | return this._isOver && !this._isDragging;
|
326 | }
|
327 | |
328 |
|
329 |
|
330 |
|
331 | onThumbMousedown($event) {
|
332 | if (this.disabled) {
|
333 | return;
|
334 | }
|
335 | this._cdr.detach();
|
336 | this._isDragging = true;
|
337 | if (this._onTouchedCB) {
|
338 | this._onTouchedCB();
|
339 | }
|
340 | this.dragStart.emit();
|
341 |
|
342 | const dragObservable$ = dragObservable($event, this._document).pipe(share());
|
343 |
|
344 |
|
345 |
|
346 |
|
347 | dragObservable$
|
348 | .pipe(debounceTime(15))
|
349 | .subscribe(( |
350 |
|
351 |
|
352 | () => this._cdr.detectChanges()));
|
353 | dragObservable$.subscribe({
|
354 | next: ( |
355 |
|
356 |
|
357 |
|
358 | val => {
|
359 | this._value = val * (this.max || 0);
|
360 |
|
361 | const relativeLeft = this.getRelativeLeft(this.value);
|
362 | (( ($event.target))).style.left = `${relativeLeft}px`;
|
363 | if (this._doneBar && this._doneBar.nativeElement) {
|
364 | this._doneBar.nativeElement.style.width = `${relativeLeft}px`;
|
365 | }
|
366 | if (this._onChangeCB) {
|
367 | this._onChangeCB(Math.min(Math.max(this.min, this.value), this.max));
|
368 | }
|
369 | this.change.emit(this.value);
|
370 | }),
|
371 | error: ( |
372 |
|
373 |
|
374 | () => {
|
375 | this._cdr.reattach();
|
376 | }),
|
377 | complete: ( |
378 |
|
379 |
|
380 | () => {
|
381 | this._cdr.reattach();
|
382 | this.dragEnd.emit();
|
383 | setTimeout(( |
384 |
|
385 |
|
386 | () => {
|
387 | this._isDragging = false;
|
388 | }), 10);
|
389 | }),
|
390 | });
|
391 | }
|
392 | }
|
393 | BMSliderComponent.decorators = [
|
394 | { type: Component, args: [{
|
395 | selector: 'bm-slider',
|
396 | template: "<span\n *ngIf=\"showPreviewBar()\"\n #previewBar\n class=\"bm-slider__bar bm-slider__bar--preview\"\n></span>\n<span\n #doneBar\n class=\"bm-slider__bar bm-slider__bar--done\"\n [ngStyle]=\"{ 'width.px': getRelativeLeft(value) }\"\n></span>\n<span\n *ngIf=\"showThumb()\"\n #thumb\n class=\"bm-slider-thumb\"\n [ngStyle]=\"{ 'left.px': getRelativeLeft(value), 'margin-left.em': -0.5 }\"\n (mousedown)=\"onThumbMousedown($event)\"\n></span>\n<ng-content></ng-content>\n",
|
397 | changeDetection: ChangeDetectionStrategy.OnPush,
|
398 | styles: [":host{height:.5em;border-radius:.4em;display:inline-block;background-color:var(--secondary-color,#bcbcbc);max-width:100%;min-width:var(--bm-slider-min--width,200px);position:relative}:host:focus{outline:solid var(--anchor-color,#4399fd) 2px}:host(.bm-slider--disabled){opacity:.5}.bm-slider__bar{height:.5em;border-radius:.4em;display:inline-block;position:absolute}.bm-slider__bar--done{background-color:var(--header-background-color,#3e71ad)}.bm-slider__bar--preview{background-color:var(--secondary-color-hover,#959494)}.bm-slider-thumb{position:absolute;background-color:var(--header-background-color,#3e71ad);border-radius:50%;display:inline-block;height:1em;width:1em;top:-.25em}.bm-slider-tooltip{display:inline-block;background-color:rgba(0,0,0,.7);border:1px solid #000;border-radius:3px;padding:.25em .5em;color:#fff;font-size:smaller}"]
|
399 | }] }
|
400 | ];
|
401 |
|
402 | BMSliderComponent.ctorParameters = () => [
|
403 | { type: ElementRef },
|
404 | { type: undefined, decorators: [{ type: Inject, args: [DOCUMENT,] }] },
|
405 | { type: ChangeDetectorRef },
|
406 | { type: Directionality }
|
407 | ];
|
408 | BMSliderComponent.propDecorators = {
|
409 | max: [{ type: HostBinding, args: ['attr.aria-valuemax',] }, { type: Input }],
|
410 | value: [{ type: HostBinding, args: ['attr.aria-valuenow',] }, { type: Input }],
|
411 | min: [{ type: HostBinding, args: ['attr.aria-valuemin',] }, { type: Input }],
|
412 | tabindex: [{ type: HostBinding, args: ['attr.tabindex',] }, { type: Input }],
|
413 | disabled: [{ type: Input }, { type: HostBinding, args: ['attr.aria-disabled',] }, { type: HostBinding, args: ['class.bm-slider--disabled',] }],
|
414 | role: [{ type: HostBinding, args: ['attr.role',] }],
|
415 | change: [{ type: Output }],
|
416 | dragStart: [{ type: Output }],
|
417 | dragEnd: [{ type: Output }],
|
418 | _doneBar: [{ type: ViewChild, args: ['doneBar', { read: ElementRef, static: true },] }],
|
419 | _previewBar: [{ type: ViewChild, args: ['previewBar', { read: ElementRef, static: false },] }],
|
420 | onMouseenter: [{ type: HostListener, args: ['mouseenter', ['$event'],] }],
|
421 | onClick: [{ type: HostListener, args: ['click', ['$event'],] }],
|
422 | onFocusin: [{ type: HostListener, args: ['focusin',] }],
|
423 | onKeydown: [{ type: HostListener, args: ['keydown', ['$event'],] }]
|
424 | };
|
425 |
|
426 |
|
427 |
|
428 |
|
429 |
|
430 |
|
431 |
|
432 |
|
433 | class BMSliderComponentModule {
|
434 | }
|
435 | BMSliderComponentModule.decorators = [
|
436 | { type: NgModule, args: [{
|
437 | imports: [BidiModule, CommonModule, OverlayModule],
|
438 | declarations: [BMSliderValueTooltipDirective, BMSliderComponent],
|
439 | exports: [BMSliderComponent, BMSliderValueTooltipDirective],
|
440 | },] }
|
441 | ];
|
442 |
|
443 | export { BMSliderComponent, BMSliderComponentModule, BMSliderValueTooltipDirective };
|
444 |
|