ng-devui
Version:
DevUI components based on Angular
307 lines (302 loc) • 18.2 kB
JavaScript
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