UNPKG

ng-devui

Version:

DevUI components based on Angular

307 lines (302 loc) 18.2 kB
import * as i0 from '@angular/core'; import { forwardRef, EventEmitter, Component, ChangeDetectionStrategy, ViewChild, Input, Output, NgModule } from '@angular/core'; import { NG_VALUE_ACCESSOR, FormsModule } from '@angular/forms'; import { fromEvent } from 'rxjs'; import { tap, pluck, map, distinctUntilChanged, takeUntil } from 'rxjs/operators'; import * as i1 from '@angular/common'; import { CommonModule } from '@angular/common'; import * as i2 from 'ng-devui/popover'; import { PopoverModule } from 'ng-devui/popover'; const SLIDER_CONTROL_VALUE_ACCESSOR = { provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => SliderComponent), multi: true }; class SliderComponent { constructor(cdr) { this.cdr = cdr; this.isDragging = false; this.isHovering = false; this.showValuePopover = false; this.max = 100; this.min = 0; this.step = 1; this.disabled = false; this.afterChange = new EventEmitter(); this.tipsRenderer = (value) => `${value}`; this.onTouchedCallback = () => { }; this.onChangeCallback = (v) => { }; } ngOnInit() { this.checkRangeValues(this.min, this.max); this.checkStepValue(); this.toggleDisabled(this.disabled); if (this.value === null) { this.setValue(this.ensureValueInRange(null)); } } ngOnChanges(changes) { if (Object.prototype.hasOwnProperty.call(changes, 'min') || Object.prototype.hasOwnProperty.call(changes, 'max') || Object.prototype.hasOwnProperty.call(changes, 'step')) { this.checkRangeValues(this.min, this.max); this.checkStepValue(); } } ngAfterViewInit() { this.registerMouseEventsListeners(); this.registerHandleHoverPopoverListener(); this.toggleDisabled(this.disabled); } registerOnChange(fn) { this.onChangeCallback = fn; } registerOnTouched(fn) { this.onTouchedCallback = fn; } setDisabledState(isDisabled) { this.disabled = isDisabled; this.toggleDisabled(isDisabled); } writeValue(newValue) { this.setValue(this.ensureValueInRange(newValue), false); } checkRangeValues(minValue, maxValue) { if (maxValue <= minValue) { throw new Error(`max value must be greater than min value`); } } checkStepValue() { if (this.step < 0 || !this.step) { throw new Error('step value must be greater than 0.'); } else if ((this.max - this.min) % this.step) { throw new Error('(max - min) must be divisible by step.'); } } ratioToValue(ratio, min, max, step) { let value = (max - min) * ratio + min; if (step > 0) { value = Math.round(value / step) * step; } return this.clamp(min, value, max); } convertHandlePositionToRatio(handleX, startX, totalLength) { return this.clamp(0, (handleX - startX) / totalLength, 1); } clamp(min, n, max) { return Math.max(min, Math.min(n, max)); } updateStyle(percentage) { percentage = Math.min(1, Math.max(0, percentage)); this.sliderTrack.nativeElement.style.width = `${percentage * 100}%`; this.sliderHandle.nativeElement.style.left = `calc(${percentage * 100}% - 7px)`; } registerMouseEventsListeners() { this.dragStartListener = fromEvent(this.slider.nativeElement, 'mousedown').pipe(tap((e) => { e.stopPropagation(); e.preventDefault(); }), pluck('pageX'), map((position) => this.mousePositionToAdaptiveValue(position))); this.dragEndListener = fromEvent(document, 'mouseup'); this.dragMoveListener = fromEvent(document, 'mousemove').pipe(tap((e) => { e.stopPropagation(); e.preventDefault(); }), pluck('pageX'), distinctUntilChanged(), map((position) => this.mousePositionToAdaptiveValue(position)), distinctUntilChanged(), takeUntil(this.dragEndListener)); } getSliderPagePosition() { const rect = this.slider.nativeElement.getBoundingClientRect(); const window = this.slider.nativeElement.ownerDocument.defaultView; return rect.left + window.pageXOffset; } getRailLength() { return this.sliderRail.nativeElement.clientWidth; } mousePositionToAdaptiveValue(handleX) { const sliderStartX = this.getSliderPagePosition(); const sliderLength = this.getRailLength(); const ratio = this.convertHandlePositionToRatio(handleX, sliderStartX, sliderLength); const value = this.ratioToValue(ratio, this.min, this.max, this.step); return parseFloat(value.toFixed(this.getDecimals(this.step))); } getDecimals(value) { const valueString = value.toString(); const integerLength = valueString.indexOf('.') + 1; return integerLength >= 0 ? valueString.length - integerLength : 0; } mouseStartMoving(value) { this.handleController(true); this.setValue(value); } mouseMoving(value) { this.setValue(value); this.cdr.markForCheck(); } mouseStopMoving() { this.afterChange.emit(this.value); this.handleController(false); this.onTouchedCallback(); this.cdr.markForCheck(); } subscribeMouseActions(mouseActions = ['start', 'move', 'end']) { if (mouseActions.indexOf('start') !== -1 && this.dragStartListener && !this.dragStartHandler) { this.dragStartHandler = this.dragStartListener.subscribe(this.mouseStartMoving.bind(this)); } if (mouseActions.indexOf('move') !== -1 && this.dragMoveListener && !this.dragMoveHandler) { this.dragMoveHandler = this.dragMoveListener.subscribe(this.mouseMoving.bind(this)); } if (mouseActions.indexOf('end') !== -1 && this.dragEndListener && !this.dragEndHandler) { this.dragEndHandler = this.dragEndListener.subscribe(this.mouseStopMoving.bind(this)); } } unsubscribeMouseActions(dragStages = ['start', 'move', 'end']) { if (dragStages.indexOf('start') !== -1 && this.dragStartHandler) { this.dragStartHandler.unsubscribe(); this.dragStartHandler = null; } if (dragStages.indexOf('move') !== -1 && this.dragMoveHandler) { this.dragMoveHandler.unsubscribe(); this.dragMoveHandler = null; } if (dragStages.indexOf('end') !== -1 && this.dragEndHandler) { this.dragEndHandler.unsubscribe(); this.dragEndHandler = null; } } handleController(movable) { if (movable) { this.isDragging = true; this.showValuePopover = this.showValuePopoverController(); this.subscribeMouseActions(['move', 'end']); } else { this.isDragging = false; this.showValuePopover = this.showValuePopoverController(); this.unsubscribeMouseActions(['move', 'end']); } } toggleDisabled(disabled) { if (disabled) { this.unsubscribeMouseActions(); } else { this.subscribeMouseActions(['start']); } } setValue(value, triggerOnChanges = true) { if (this.value !== value) { this.value = value; this.updateTrackAndHandle(); } if (triggerOnChanges) { this.onChangeCallback(this.value); } } ensureValueInRange(value) { let safeValue; if (!this.valueMustBeValid(value)) { safeValue = this.min; } else { safeValue = this.clamp(this.min, value, this.max); } return safeValue; } updateTrackAndHandle() { const value = this.value; const offset = this.valueToOffset(value); this.updateStyle(offset / 100); this.cdr.markForCheck(); } valueMustBeValid(value) { return !isNaN(typeof value !== 'number' ? parseFloat(value) : value); } valueToOffset(value) { return ((value - this.min) / (this.max - this.min)) * 100; } registerHandleHoverPopoverListener() { const mouseOverListener = fromEvent(this.sliderHandle.nativeElement, 'mouseover'); const mouseLeaveListener = fromEvent(this.sliderHandle.nativeElement, 'mouseout'); this.mouseOverHandler = mouseOverListener.subscribe(this.sliderHandlePopoverOnMouseHover.bind(this)); this.mouseLeaveHandler = mouseLeaveListener.subscribe(this.sliderHandlePopoverOnMouseLeave.bind(this)); } sliderHandlePopoverOnMouseHover() { this.isHovering = true; this.showValuePopover = this.showValuePopoverController(); this.cdr.markForCheck(); } sliderHandlePopoverOnMouseLeave() { this.isHovering = false; this.showValuePopover = this.showValuePopoverController(); this.cdr.markForCheck(); } unregisterHandleHoverTooltip() { this.mouseOverHandler.unsubscribe(); this.mouseLeaveHandler.unsubscribe(); } showValuePopoverController() { return this.isDragging || this.isHovering; } ngOnDestroy() { this.unsubscribeMouseActions(); this.unregisterHandleHoverTooltip(); } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: SliderComponent, deps: [{ token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Component }); } static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.13", type: SliderComponent, selector: "d-slider", inputs: { max: "max", min: "min", step: "step", disabled: "disabled", tipsRenderer: "tipsRenderer" }, outputs: { afterChange: "afterChange" }, providers: [SLIDER_CONTROL_VALUE_ACCESSOR], viewQueries: [{ propertyName: "slider", first: true, predicate: ["slider"], descendants: true, static: true }, { propertyName: "sliderHandle", first: true, predicate: ["sliderHandle"], descendants: true, static: true }, { propertyName: "sliderTrack", first: true, predicate: ["sliderTrack"], descendants: true, static: true }, { propertyName: "sliderRail", first: true, predicate: ["sliderRail"], descendants: true, static: true }], usesOnChanges: true, ngImport: i0, template: "<div class=\"devui-slider\" #slider [ngClass]=\"{ disabled: disabled }\">\n <div class=\"devui-slider-rail\" #sliderRail [ngClass]=\"{ disabled: disabled }\"></div>\n <div class=\"devui-slider-track\" #sliderTrack [ngClass]=\"{ disabled: disabled }\"></div>\n <div\n class=\"devui-slider-handle\"\n #sliderHandle\n [ngClass]=\"{ disabled: disabled }\"\n dPopover\n position=\"top\"\n [content]=\"showValuePopover && tipsRenderer ? tipsRenderer(value) : ''\"\n [showAnimation]=\"false\"\n [visible]=\"tipsRenderer && showValuePopover\"\n ></div>\n</div>\n", styles: [":host{display:block}.devui-slider{position:relative;width:100%;padding:4px 0;margin:4px 0;cursor:pointer;box-sizing:border-box;height:14px;display:flex;align-items:center}.devui-slider .devui-slider-rail{position:absolute;background-color:var(--devui-area, #f3f3f3);height:5px;width:100%;transition:background-color var(--devui-animation-duration-base, .2s) var(--devui-animation-ease-in-out-smooth, cubic-bezier(.645, .045, .355, 1));border-radius:var(--devui-border-radius-full, 100px)}.devui-slider .devui-slider-rail.disabled{background-color:var(--devui-area, #f3f3f3)}.devui-slider:hover .devui-slider-rail:not(.disabled){background-color:var(--devui-area, #f3f3f3)}.devui-slider:hover .devui-slider-track:not(.disabled){background-color:var(--devui-brand-hover, #3979f9)}.devui-slider:hover .devui-slider-handle:not(.disabled){border-color:var(--devui-brand-hover, #3979f9)}.devui-slider .devui-slider-track{position:absolute;background-color:var(--devui-brand, #0a59f7);height:5px;width:0;border-radius:var(--devui-border-radius-full, 100px)}.devui-slider .devui-slider-handle{position:absolute;background-color:var(--devui-base-bg, #ffffff);width:14px;height:14px;border-radius:50%;left:0;transition:transform .2s ease-in-out;border:2px solid var(--devui-brand, #0a59f7)}.devui-slider .devui-slider-handle:not(.disabled):hover,.devui-slider .devui-slider-handle:not(.disabled):active{transform:scale(1.3)}.devui-slider.disabled{cursor:not-allowed}.devui-slider.disabled .devui-slider-rail{background-color:var(--devui-disabled-bg, #f3f3f3)}.devui-slider.disabled .devui-slider-track{background-color:var(--devui-disabled-line, #e6e6e6)}.devui-slider.disabled .devui-slider-handle{border-color:var(--devui-disabled-line, #e6e6e6)}\n"], dependencies: [{ kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i2.PopoverDirective, selector: "[dPopover]", inputs: ["content", "controlled", "position", "showAnimation", "showAnimate", "scrollElement", "autoHideCoefficient", "appendToBody", "zIndex", "popType", "popMaxWidth", "trigger", "hoverToContent", "hoverDelayTime", "popoverStyle", "mouseEnterDelay", "mouseLeaveDelay", "visible"], exportAs: ["dPopover"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: SliderComponent, decorators: [{ type: Component, args: [{ selector: 'd-slider', providers: [SLIDER_CONTROL_VALUE_ACCESSOR], changeDetection: ChangeDetectionStrategy.OnPush, preserveWhitespaces: false, template: "<div class=\"devui-slider\" #slider [ngClass]=\"{ disabled: disabled }\">\n <div class=\"devui-slider-rail\" #sliderRail [ngClass]=\"{ disabled: disabled }\"></div>\n <div class=\"devui-slider-track\" #sliderTrack [ngClass]=\"{ disabled: disabled }\"></div>\n <div\n class=\"devui-slider-handle\"\n #sliderHandle\n [ngClass]=\"{ disabled: disabled }\"\n dPopover\n position=\"top\"\n [content]=\"showValuePopover && tipsRenderer ? tipsRenderer(value) : ''\"\n [showAnimation]=\"false\"\n [visible]=\"tipsRenderer && showValuePopover\"\n ></div>\n</div>\n", styles: [":host{display:block}.devui-slider{position:relative;width:100%;padding:4px 0;margin:4px 0;cursor:pointer;box-sizing:border-box;height:14px;display:flex;align-items:center}.devui-slider .devui-slider-rail{position:absolute;background-color:var(--devui-area, #f3f3f3);height:5px;width:100%;transition:background-color var(--devui-animation-duration-base, .2s) var(--devui-animation-ease-in-out-smooth, cubic-bezier(.645, .045, .355, 1));border-radius:var(--devui-border-radius-full, 100px)}.devui-slider .devui-slider-rail.disabled{background-color:var(--devui-area, #f3f3f3)}.devui-slider:hover .devui-slider-rail:not(.disabled){background-color:var(--devui-area, #f3f3f3)}.devui-slider:hover .devui-slider-track:not(.disabled){background-color:var(--devui-brand-hover, #3979f9)}.devui-slider:hover .devui-slider-handle:not(.disabled){border-color:var(--devui-brand-hover, #3979f9)}.devui-slider .devui-slider-track{position:absolute;background-color:var(--devui-brand, #0a59f7);height:5px;width:0;border-radius:var(--devui-border-radius-full, 100px)}.devui-slider .devui-slider-handle{position:absolute;background-color:var(--devui-base-bg, #ffffff);width:14px;height:14px;border-radius:50%;left:0;transition:transform .2s ease-in-out;border:2px solid var(--devui-brand, #0a59f7)}.devui-slider .devui-slider-handle:not(.disabled):hover,.devui-slider .devui-slider-handle:not(.disabled):active{transform:scale(1.3)}.devui-slider.disabled{cursor:not-allowed}.devui-slider.disabled .devui-slider-rail{background-color:var(--devui-disabled-bg, #f3f3f3)}.devui-slider.disabled .devui-slider-track{background-color:var(--devui-disabled-line, #e6e6e6)}.devui-slider.disabled .devui-slider-handle{border-color:var(--devui-disabled-line, #e6e6e6)}\n"] }] }], ctorParameters: () => [{ type: i0.ChangeDetectorRef }], propDecorators: { slider: [{ type: ViewChild, args: ['slider', { static: true }] }], sliderHandle: [{ type: ViewChild, args: ['sliderHandle', { static: true }] }], sliderTrack: [{ type: ViewChild, args: ['sliderTrack', { static: true }] }], sliderRail: [{ type: ViewChild, args: ['sliderRail', { static: true }] }], max: [{ type: Input }], min: [{ type: Input }], step: [{ type: Input }], disabled: [{ type: Input }], afterChange: [{ type: Output }], tipsRenderer: [{ type: Input }] } }); class SliderModule { static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: SliderModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); } static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "18.2.13", ngImport: i0, type: SliderModule, declarations: [SliderComponent], imports: [CommonModule, FormsModule, PopoverModule], exports: [SliderComponent] }); } static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: SliderModule, imports: [CommonModule, FormsModule, PopoverModule] }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: SliderModule, decorators: [{ type: NgModule, args: [{ imports: [ CommonModule, FormsModule, PopoverModule ], exports: [SliderComponent], declarations: [SliderComponent], providers: [], }] }] }); /** * Generated bundle index. Do not edit. */ export { SliderComponent, SliderModule }; //# sourceMappingURL=ng-devui-slider.mjs.map