@alyle/ui
Version:
Minimal Design, a set of components for Angular
1,158 lines • 54.9 kB
JavaScript
import { __decorate } from 'tslib';
import * as i0 from '@angular/core';
import { InjectionToken, Input, Inject, Directive, ViewChild, ChangeDetectionStrategy, Component, forwardRef, EventEmitter, Output, ViewChildren, Optional, NgModule } from '@angular/core';
import * as i1 from '@alyle/ui';
import { untilComponentDestroyed, st2c, Dir, LY_COMMON_STYLES, StyleCollection, StyleRenderer, Style, LyCommonModule } from '@alyle/ui';
import { NG_VALUE_ACCESSOR } from '@angular/forms';
import { Subject } from 'rxjs';
import { normalizePassiveListenerOptions } from '@angular/cdk/platform';
import * as i3 from '@angular/common';
import { DOCUMENT, CommonModule } from '@angular/common';
import { hasModifierKey, DOWN_ARROW, RIGHT_ARROW, UP_ARROW, LEFT_ARROW, HOME, END, PAGE_DOWN, PAGE_UP } from '@angular/cdk/keycodes';
import { coerceBooleanProperty, coerceNumberProperty } from '@angular/cdk/coercion';
import * as i2 from '@angular/cdk/a11y';
/**
* Used to provide a slider to some of the sub-components without causing a circular dependency.
* @docs-private
*/
const LY_SLIDER = new InjectionToken('LY_SLIDER');
function гbetween(x, min, max) {
return x >= min && x <= max;
}
function гvalueToPercent(value, min, max) {
return ((value - min) * 100) / (max - min);
}
class LyTick {
constructor(_renderer, _el, _slider) {
this._renderer = _renderer;
this._el = _el;
this._slider = _slider;
/** @docs-private */
this.classes = this._slider.classes;
}
ngOnChanges() {
this._updateTick();
}
ngOnInit() {
this._renderer.addClass(this._getHostElement(), this.classes.tick);
this._slider._changes.pipe(untilComponentDestroyed(this)).subscribe(() => {
this._updateTick();
});
}
_updateTick() {
const min = this._slider._minPercent;
const max = this._slider._maxPercent;
const className = this._slider.classes.tickActive;
const percent = гvalueToPercent(this.value, this._slider.min, this._slider.max);
const pos = this._slider._calculatePosition(percent);
if (гbetween(percent, min, max)) {
this._tickActiveClass = className;
this._renderer.addClass(this._el.nativeElement, className);
}
else if (this._tickActiveClass) {
this._tickActiveClass = null;
this._renderer.removeClass(this._el.nativeElement, className);
}
this._renderer.setStyle(this._getHostElement(), 'bottom', null);
this._renderer.setStyle(this._getHostElement(), 'left', null);
this._renderer.setStyle(this._getHostElement(), 'right', null);
this._renderer.setStyle(this._getHostElement(), pos.style, pos.value);
}
ngOnDestroy() { }
_getHostElement() {
return this._el.nativeElement;
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.10", ngImport: i0, type: LyTick, deps: [{ token: i0.Renderer2 }, { token: i0.ElementRef }, { token: LY_SLIDER }], target: i0.ɵɵFactoryTarget.Directive }); }
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "19.2.10", type: LyTick, isStandalone: false, selector: "ly-tick", inputs: { value: "value" }, usesOnChanges: true, ngImport: i0 }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.10", ngImport: i0, type: LyTick, decorators: [{
type: Directive,
args: [{
selector: 'ly-tick',
standalone: false
}]
}], ctorParameters: () => [{ type: i0.Renderer2 }, { type: i0.ElementRef }, { type: undefined, decorators: [{
type: Inject,
args: [LY_SLIDER]
}] }], propDecorators: { value: [{
type: Input
}] } });
class LyMark {
constructor(_renderer, _el, _slider) {
this._renderer = _renderer;
this._el = _el;
this._slider = _slider;
/** @docs-private */
this.classes = this._slider.classes;
_renderer.addClass(_el.nativeElement, _slider.classes.mark);
}
ngOnInit() {
this._renderer.insertBefore(this._slider._wrapper.nativeElement, this._tick._getHostElement(), this._slider._ticksRef.nativeElement);
this._slider._changes.pipe(untilComponentDestroyed(this)).subscribe(() => {
this._updateMark();
});
this._updateMark();
}
_updateMark() {
const min = this._slider._minPercent;
const max = this._slider._maxPercent;
const className = this._slider.classes.markActive;
const percent = гvalueToPercent(this.value, this._slider.min, this._slider.max);
const pos = this._slider._calculatePosition(percent);
if (гbetween(percent, min, max)) {
this._markActiveClass = className;
this._renderer.addClass(this._el.nativeElement, className);
}
else if (this._markActiveClass) {
this._markActiveClass = null;
this._renderer.removeClass(this._el.nativeElement, className);
}
this._renderer.setStyle(this._getHostElement(), 'bottom', null);
this._renderer.setStyle(this._getHostElement(), 'left', null);
this._renderer.setStyle(this._getHostElement(), 'right', null);
this._renderer.setStyle(this._getHostElement(), pos.style, pos.value);
}
ngOnDestroy() { }
_getHostElement() {
return this._el.nativeElement;
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.10", ngImport: i0, type: LyMark, deps: [{ token: i0.Renderer2 }, { token: i0.ElementRef }, { token: LY_SLIDER }], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.10", type: LyMark, isStandalone: false, selector: "ly-mark", inputs: { value: "value" }, viewQueries: [{ propertyName: "_tick", first: true, predicate: LyTick, descendants: true, static: true }], ngImport: i0, template: "<ly-tick [value]=\"value\"></ly-tick>\n<ng-content></ng-content>", dependencies: [{ kind: "directive", type: LyTick, selector: "ly-tick", inputs: ["value"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.10", ngImport: i0, type: LyMark, decorators: [{
type: Component,
args: [{ selector: 'ly-mark', changeDetection: ChangeDetectionStrategy.OnPush, standalone: false, template: "<ly-tick [value]=\"value\"></ly-tick>\n<ng-content></ng-content>" }]
}], ctorParameters: () => [{ type: i0.Renderer2 }, { type: i0.ElementRef }, { type: undefined, decorators: [{
type: Inject,
args: [LY_SLIDER]
}] }], propDecorators: { _tick: [{
type: ViewChild,
args: [LyTick, { static: true }]
}], value: [{
type: Input
}] } });
const activeEventOptions = normalizePassiveListenerOptions({ passive: false });
const LY_SLIDER_DEFAULT_OPTIONS = new InjectionToken('LY_SLIDER_DEFAULT_OPTIONS');
const LY_SLIDER_CONTROL_VALUE_ACCESSOR = {
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => LySlider),
multi: true
};
const STYLE_PRIORITY = -2;
const STYLES = (theme, ref) => {
const __ = ref.selectorsOf(STYLES);
const { before, after } = theme;
const isRTL = theme.isRTL();
return {
$priority: STYLE_PRIORITY,
root: () => (_className) => `${_className}{display:inline-block;position:relative;box-sizing:border-box;}${st2c((LY_COMMON_STYLES.fill), `${_className} ${__.bg}`)}${_className} ${__.bg}{margin:auto;}${_className}${__.sliding}{cursor:-webkit-grabbing;cursor:grabbing;}${_className}${__.sliding} ${__.thumb},${_className}${__.sliding} ${__.thumbLabel}{cursor:-webkit-grabbing;cursor:grabbing;}${st2c(((theme.slider
&& theme.slider.root
&& (theme.slider.root instanceof StyleCollection
? theme.slider.root.setTransformer(fn => fn(__)).css
: theme.slider.root(__)))), `${_className}`)}`,
wrapper: (_className) => `${_className}{-webkit-print-color-adjust:exact;color-adjust:exact;position:absolute;}`,
track: () => (_className) => `${_className}{position:absolute;margin:auto;}${__.horizontal} ${_className}{transform-origin:${isRTL ? 100 : 0}% 0;}${__.vertical} ${_className}{transform-origin:${isRTL ? 100 : 0}% 100%;}`,
bg: null,
thumbContainer: (_className) => `${_className}{width:0;height:0;position:absolute;margin:auto;}`,
thumbContent: (_className) => `${_className}{display:flex;justify-content:center;align-items:center;}`,
thumb: (_className) => `${_className}{position:absolute;width:16px;height:16px;border-radius:50%;outline:0;cursor:-webkit-grab;cursor:grab;transition:${['border-radius', 'transform'].map(prop => `${prop} ${theme.animations.durations.exiting}ms ${theme.animations.curves.standard} 0ms`).join()};}${_className}::before{content:'';}${st2c((LY_COMMON_STYLES.fill), `${_className}::before`)}${_className}::before{border-radius:50%;transition:${['box-shadow'].map(prop => `${prop} ${theme.animations.durations.entering}ms ${theme.animations.curves.sharp} 0ms`).join()};}`,
thumbLabel: (_className) => `${_className}{position:absolute;font-size:12px;width:28px;height:28px;border-radius:50%;top:-14px;${before}:-14px;cursor:-webkit-grab;cursor:grab;transition:${[
'transform',
'top',
'left',
'right',
'border-radius'
].map(prop => `${prop} 150ms ${theme.animations.curves.standard} 0ms`).join()};}`,
thumbLabelValue: (_className) => `${_className}{display:flex;height:100%;width:100%;align-items:center;justify-content:center;}`,
horizontal: () => (_className) => `${_className}{width:128px;height:48px;padding:8px;touch-action:pan-y !important;}${_className} ${__.wrapper}{top:23px;left:8px;right:8px;}${_className} ${__.track},${_className} ${__.bg},${_className} ${__.track}{height:inherit;}${_className} ${__.track},${_className} ${__.bg}{width:100%;}${_className} ${__.track}{${before}:0;top:0;bottom:0;}${_className} ${__.thumbContainer}{top:0;bottom:0;}${_className} ${__.tick}{width:2px;top:0;bottom:0;transform:translate(-1px,0%);}${_className} ${__.mark}{top:22px;transform:translateX(${theme.direction === Dir.ltr ? '-' : ''}50%);}${_className}${__.marked}{margin-bottom:24px;}`,
vertical: () => (_className) => `${_className}{width:48px;height:128px;padding:8px;touch-action:pan-x !important;}${_className} ${__.wrapper}{${before}:23px;top:8px;bottom:8px;}${_className} ${__.track},${_className} ${__.bg},${_className} ${__.track}{width:inherit;}${_className} ${__.track},${_className} ${__.bg}{height:100%;}${_className} ${__.track}{bottom:0;${before}:-1px;${after}:0;}${_className} ${__.thumbContainer}{left:0;right:0;}${_className} ${__.tick}{width:inherit;height:2px;left:0;right:0;}${_className} ${__.mark}{${before}:22px;transform:translateY(50%);}${_className}${__.marked}{${theme.direction === Dir.ltr ? 'margin-right' : 'margin-left'}:24px;}`,
marked: null,
mark: (_className) => `${_className}{position:absolute;white-space:nowrap;font-size:14px;color:${theme.text.secondary};}`,
markActive: (_className) => `${_className}{color:currentColor;}`,
tick: (_className) => `${_className}{position:absolute;margin:auto;}`,
tickActive: null,
thumbVisible: null,
thumbNotVisible: null,
thumbContentFocused: null,
sliding: null,
disabled: (_className) => `${_className}{cursor:default;}`
};
};
/** A change event emitted by the LySlider component. */
class LySliderChange {
constructor(
/** The LySlider that changed. */
source,
/** The new value of the source slider. */
value) {
this.source = source;
this.value = value;
}
}
class LySlider {
/** Whether or not to show the thumb. */
get thumbVisible() {
return this._thumbVisible;
}
set thumbVisible(val) {
const newVal = val != null ? coerceBooleanProperty(val) : null;
if (newVal !== this.thumbVisible) {
const { thumbVisible: thumbVisibleClass } = this.classes;
const { thumbNotVisible: thumbNotVisibleClass } = this.classes;
this._thumbVisible = newVal;
this.sRenderer.toggleClass(thumbVisibleClass, newVal === true);
this.sRenderer.toggleClass(thumbNotVisibleClass, newVal === false);
}
}
/** Whether or not to show the marks, also accepts an array of marks. */
get marks() {
return this._marks;
}
set marks(val) {
const newVal = coerceBooleanProperty(val);
if (newVal !== this.marks) {
const newClass = this.classes.marked;
if (newVal) {
this._renderer.addClass(this._el.nativeElement, newClass);
this._marksClass = newClass;
this._marks = Array.isArray(val) ? val : newVal;
}
else if (this._marksClass) {
this._marks = false;
this._renderer.removeClass(this._el.nativeElement, newClass);
this._marksClass = null;
}
if (Array.isArray(newVal)) {
this._marksList = val;
}
else {
this._marksList = null;
}
}
}
/** The maximum value that the slider can have. */
get max() {
return this._max;
}
set max(v) {
this._max = coerceNumberProperty(v, this._max);
this._updateThumbs();
this._cd.markForCheck();
}
/** The minimum value that the slider can have. */
get min() {
return this._min;
}
set min(v) {
this._min = coerceNumberProperty(v, this._min);
// If the value wasn't explicitly set by the user, set it to the min.
if (this._value === null) {
this.value = this._min;
}
this._updateThumbs();
this._cd.markForCheck();
}
/** The slider appearance style. */
set appearance(val) {
if (val !== this.appearance) {
this._appearance = val;
this._appearanceClass = this.sRenderer.add(`${LySlider.и}.appearance:${val}`, (theme, ref) => {
const classes = ref.selectorsOf(STYLES);
if (theme.slider && theme.slider.appearance) {
const appearance = theme.slider.appearance[val];
if (appearance) {
return appearance instanceof StyleCollection
? appearance.setTransformer((_) => _(classes)).css
: appearance(classes);
}
}
throw new Error(`${val} not found in theme.slider.appearance`);
}, STYLE_PRIORITY, this._appearanceClass);
}
}
get appearance() {
return this._appearance;
}
/** Color of Slider */
get color() {
return this._color;
}
set color(val) {
this._color = val;
const styleKey = `${LySlider.и}.color:${val}`;
const newStyle = (theme, ref) => {
const color = theme.colorOf(val);
const contrast = theme.colorOf(`${val}:contrast`);
const __ = ref.selectorsOf(STYLES);
if (theme.slider && theme.slider.color) {
const sliderColor = theme.slider.color;
if (sliderColor) {
return sliderColor instanceof StyleCollection
? (sliderColor).setTransformer((_) => _(__, color, contrast)).css
: sliderColor(__, color, contrast);
}
}
throw new Error(`${val} not found in theme.slider.color`);
};
this._colorClass = this.sRenderer.add(styleKey, newStyle, STYLE_PRIORITY + 1, this._colorClass);
}
/** Whether the slider is vertical. */
get vertical() {
return this._vertical;
}
set vertical(val) {
const newVal = coerceBooleanProperty(val);
this._vertical = newVal;
const newClass = newVal
? this.classes.vertical
: this.classes.horizontal;
this._verticalClass = this._theme.updateClass(this._el.nativeElement, this._renderer, newClass, this._verticalClass);
this._updateThumbs();
this._cd.markForCheck();
}
/** The values at which the thumb will snap. */
get step() { return this._step; }
set step(v) {
this._step = coerceNumberProperty(v, this._step);
this._stepPrecision = this._step % 1 !== 0
? this._step.toString().split('.')[1].length
: null;
this._cd.markForCheck();
}
/**
* Value of a slider, this can be a number or an array of numbers.
* If the array of numbers has more than one value,
* then this will create more thumbs
*/
get value() {
return this._value;
}
set value(val) {
if (val !== this._value) {
const valueIsArray = Array.isArray(val);
if (typeof val === 'number') {
let newValue = Number(val);
newValue = (newValue === this.min || newValue === this.max)
? newValue
: parseFloat(newValue.toFixed(this._stepPrecision));
this._value = newValue;
}
else if (valueIsArray && !arrayEquals(this._value, val)) {
let newValue = val;
newValue = newValue.map(_val => _val === null
? _val
: (_val === this.min || _val === this.max)
? _val
: parseFloat(_val.toFixed(this._stepPrecision)));
this._value = newValue;
}
this._thumbs = (valueIsArray ?
this._value
: [this._value]).map((v, index) => ({
index,
value: coerceNumberProperty(v, this.min),
displayValue: null,
percent: null,
styles: {},
focused: this._thumbs?.[index]?.focused
}));
this._updateThumbs();
this._cd.markForCheck();
}
}
/** Whether the slider is disabled. */
get disabled() {
return this._disabled;
}
set disabled(val) {
const newVal = coerceBooleanProperty(val);
if (newVal !== this.disabled) {
this._disabled = newVal;
if (newVal) {
// TODO: remove promise when color parameter removed
Promise.resolve(null).then(() => {
// TODO: deprecated
const color = this.color;
const styleKey = `${LySlider.и}.disabled:${val}-${color}`;
let newStyle;
newStyle = (theme, ref) => {
const clr = theme.colorOf(color);
const __ = ref.selectorsOf(STYLES);
if (theme.slider && theme.slider.disabled) {
const sliderColor = theme.slider.disabled;
if (sliderColor) {
return sliderColor instanceof StyleCollection
? (sliderColor).setTransformer((_) => _(__, clr)).css
: sliderColor(__, clr);
}
}
throw new Error(`${val} not found in theme.slider.color`);
};
const newClass = this.sRenderer.add(styleKey, newStyle, STYLE_PRIORITY + 1.5, this._disabledClass);
this.sRenderer.addClass(this.classes.disabled);
this._disabledClass = newClass;
});
}
else if (this._disabledClass) {
this.sRenderer.removeClass(this._disabledClass);
this.sRenderer.removeClass(this.classes.disabled);
this._disabledClass = null;
}
}
}
/**
* Whether or not to show the thumb label, but if the value is a number,
* it will show ticks according to the steps. For example: if you set
* 3 ticks with a step of 10, you will draw a tick every 30 values
*/
get ticks() {
return this._ticks;
}
set ticks(val) {
const newValue = coerceNumberProperty(val, coerceBooleanProperty(val));
this._ticks = newValue;
}
get _tickList() {
return this.__tickList;
}
// private _ngClass: NgClass;
constructor(_theme, _el, _renderer, _cd, sRenderer, _ngZone, _focusMonitor, _document, _default) {
this._theme = _theme;
this._el = _el;
this._renderer = _renderer;
this._cd = _cd;
this.sRenderer = sRenderer;
this._ngZone = _ngZone;
this._focusMonitor = _focusMonitor;
this._default = _default;
/** The dimensions of the slider. */
this._sliderDimensions = null;
this._step = 1;
this.classes = this._theme.renderStyleSheet(STYLES);
this._value = null;
this._min = 0;
this._max = 100;
this._changes = new Subject();
/**
* Whether or not the thumb is sliding.
*/
this._isSliding = null;
this._thumbs = [];
this._rootClasses = new Set();
/** Event emitted when the slider value has changed. */
this.change = new EventEmitter();
/** Event emitted when the slider thumb moves. */
this.input = new EventEmitter();
/** @docs-private */
this.valueChange = new EventEmitter();
/** Called when the window has lost focus. */
this._windowBlur = () => {
// If the window is blurred while dragging we need to stop dragging because the
// browser won't dispatch the `mouseup` and `touchend` events anymore.
if (this._lastPointerEvent) {
this._pointerUp(this._lastPointerEvent);
}
};
/**
* The registered callback function called when a blur event occurs on the input element.
* @docs-private
*/
this.onTouched = () => { };
this._controlValueAccessorChangeFn = () => { };
this._pointerDown = (event) => {
// Don't do anything if the slider is disabled or the
// user is using anything other than the main mouse button.
if (this.disabled || this._isSliding || (!isTouchEvent(event) && event.button !== 0)) {
return;
}
this._ngZone.run(() => {
this._touchId = isTouchEvent(event)
? getTouchIdForSlider(event, this._el.nativeElement)
: undefined;
const pointerPosition = getPointerPositionOnPage(event, this._touchId);
if (pointerPosition) {
const oldValue = this.value;
this._isSliding = 'pointer';
this._lastPointerEvent = event;
event.preventDefault();
this._focusHostElement();
this._onMouseenter(); // Simulate mouseenter in case this is a mobile device.
this._bindGlobalEvents(event);
this._focusHostElement();
this._setOnSlideStart();
this._updateValueFromPosition(pointerPosition.x, pointerPosition.y);
// Emit a change and input event if the value changed.
if (!valueEquals(oldValue, this.value)) {
this._emitInputEvent();
this._changes.next();
}
}
});
};
/**
* Called when the user has moved their pointer after
* starting to drag. Bound on the document level.
*/
this._pointerMove = (event) => {
if (this._isSliding === 'pointer') {
const pointerPosition = getPointerPositionOnPage(event, this._touchId);
if (pointerPosition) {
// Prevent the slide from selecting anything else.
event.preventDefault();
const oldValue = this.value;
this._lastPointerEvent = event;
this._updateValueFromPosition(pointerPosition.x, pointerPosition.y);
// Native range elements always emit `input` events when the value changed while sliding.
if (!valueEquals(oldValue, this.value)) {
this._emitInputEvent();
this._changes.next();
}
}
}
};
/** Called when the user has lifted their pointer. Bound on the document level. */
this._pointerUp = (event) => {
if (this._isSliding === 'pointer') {
if (!isTouchEvent(event) ||
typeof this._touchId !== 'number' ||
// Note that we use `changedTouches`, rather than `touches` because it
// seems like in most cases `touches` is empty for `touchend` events.
findMatchingTouch(event.changedTouches, this._touchId)) {
event.preventDefault();
this._removeGlobalEvents();
this._isSliding = null;
this._touchId = undefined;
this._renderer.removeClass(this._el.nativeElement, this.classes.sliding);
if (!valueEquals(this._valueOnSlideStart, this.value) && !this.disabled) {
this._emitChangeEvent();
this._changes.next();
}
this._thumbsOnSlideStart = null;
this._closestIndex = null;
this._valueOnSlideStart = this._lastPointerEvent = null;
}
}
};
this._document = _document;
_renderer.addClass(_el.nativeElement, this.classes.root);
_ngZone.runOutsideAngular(() => {
const element = _el.nativeElement;
element.addEventListener('mousedown', this._pointerDown, activeEventOptions);
element.addEventListener('touchstart', this._pointerDown, activeEventOptions);
});
}
static { this.и = 'LySlider'; }
ngOnChanges() {
this._updateTickValues();
this._changes.next();
}
ngOnInit() {
this._theme.directionChanged.pipe(untilComponentDestroyed(this)).subscribe(() => {
this.ngOnChanges();
this._updateThumbs();
this._cd.markForCheck();
});
/** Set default appearance */
if (this.appearance == null) {
this.appearance = (this._default && this._default.appearance) || 'standard';
}
/** Set default size */
if (this.size == null) {
this.size = 'small';
}
/** Set horizontal slider */
if (this.vertical == null) {
this.vertical = false;
}
/** Set default color */
if (this.color == null) {
this.color = 'accent';
}
/** Set default step */
if (this.step == null) {
this.step = 1;
}
}
ngAfterViewInit() {
this._focusMonitor.monitor(this._el, true).subscribe((_origin) => {
this._cd.detectChanges();
});
}
ngOnDestroy() {
const element = this._el.nativeElement;
this._focusMonitor.stopMonitoring(this._el);
element.removeEventListener('mousedown', this._pointerDown, activeEventOptions);
element.removeEventListener('touchstart', this._pointerDown, activeEventOptions);
this._lastPointerEvent = null;
this._changes.complete();
}
writeValue(value) {
this.value = value;
this._changes.next();
}
/**
* Registers a function called when the control value changes.
*
* @param fn The callback function
*/
registerOnChange(fn) {
this._controlValueAccessorChangeFn = fn;
}
/**
* Registers a function called when the control is touched.
*
* @param fn The callback function
*/
registerOnTouched(fn) {
this.onTouched = fn;
}
/**
* Disables the select. Part of the ControlValueAccessor interface required
* to integrate with Angular's core forms API.
*
* @param isDisabled Sets whether the component is disabled.
*/
setDisabledState(isDisabled) {
this.disabled = isDisabled;
}
_onMouseenter() {
if (this.disabled) {
return;
}
// We save the dimensions of the slider here so we can use them to update the spacing of the
// ticks and determine where on the slider click and slide events happen.
this._sliderDimensions = this._getSliderDimensions();
// this._updateTickIntervalPercent();
}
_onFocus() {
// We save the dimensions of the slider here so we can use them to update the spacing of the
// ticks and determine where on the slider click and slide events happen.
this._sliderDimensions = this._getSliderDimensions();
// this._updateTickIntervalPercent();
}
_onBlur() {
this.onTouched();
}
_onKeydown(event) {
if (this.disabled ||
hasModifierKey(event) ||
(this._isSliding && this._isSliding !== 'keyboard')) {
return;
}
this._isSliding = 'keyboard';
const oldValue = this.value;
switch (event.keyCode) {
case PAGE_UP:
this._increment(10);
break;
case PAGE_DOWN:
this._increment(-10);
break;
case END:
this.value = this.max;
break;
case HOME:
this.value = this.min;
break;
case LEFT_ARROW:
this._increment(this._getDirection() === Dir.rtl ? 1 : -1);
break;
case UP_ARROW:
this._increment(1);
break;
case RIGHT_ARROW:
// See comment on LEFT_ARROW about the conditions under which we flip the meaning.
this._increment(this._getDirection() === Dir.rtl ? -1 : 1);
break;
case DOWN_ARROW:
this._increment(-1);
break;
default:
// Return if the key is not one that we explicitly handle to avoid calling preventDefault on
// it.
return;
}
if (!valueEquals(oldValue, this.value)) {
this._emitInputEvent();
this._emitChangeEvent();
this._changes.next();
}
event.preventDefault();
}
_onKeyup() {
if (this._isSliding === 'keyboard') {
this._renderer.removeClass(this._el.nativeElement, this.classes.sliding);
this._isSliding = null;
this._thumbsOnSlideStart = null;
this._valueOnSlideStart = null;
this._closestIndex = null;
}
}
_onFocusThumb(thumb) {
if (!this.disabled) {
thumb.focused = true;
}
}
_onBlurThumb(thumb) {
if (!this.disabled) {
thumb.focused = false;
}
}
_setOnSlideStart() {
if (!this._valueOnSlideStart) {
this._renderer.addClass(this._el.nativeElement, this.classes.sliding);
// clone
this._valueOnSlideStart = Array.isArray(this.value) ? this.value.slice(0) : this.value;
this._thumbsOnSlideStart = this._thumbs.slice(0).map(t => ({ ...t }));
}
}
_trackByFn(_index, item) {
return item.index;
}
_getDirection() {
return this._theme.variables.direction;
}
_updateValueFromPosition(x, y) {
if (!this._sliderDimensions) {
return;
}
const w = this._sliderDimensions.width;
const h = this._sliderDimensions.height;
x -= this._sliderDimensions.left;
y -= this._sliderDimensions.top;
let percent = clamp(this.vertical
? гvalueToPercent(y, 0, h)
: гvalueToPercent(x, 0, w), 0, 100);
if (this.vertical || (!this.vertical && this._theme.variables.direction === Dir.rtl)) {
percent = 100 - percent;
}
let value;
if (percent === 0) {
value = this.min;
}
else if (percent === 100) {
value = this.max;
}
else {
value = this._roundValueToStep(percentToValue(percent, this.min, this.max));
}
if (this._closestIndex == null) {
this._closestIndex = findClosest(this._thumbs.map(thumb => thumb.value), value);
}
const currentThumb = this._thumbsOnSlideStart[this._closestIndex];
this._slidingThumbValue = currentThumb.value = value;
if (Array.isArray(this.value)) {
this.value = this._thumbsOnSlideStart.map(thumb => thumb.value).sort(ASC);
}
else {
this.value = value;
}
// focus slidingThumb
const currentSlidingThumb = this._thumbs.find(thumb => thumb.value === value);
if (currentSlidingThumb) {
currentSlidingThumb.focused = true;
this._thumbsRef.toArray()[currentSlidingThumb.index].nativeElement.focus();
}
}
_updateThumbs() {
this._thumbs.forEach(thumb => {
const val = clamp(thumb.value, this.min, this.max);
const percent = гvalueToPercent(val, this.min, this.max);
const pos = this._calculatePosition(percent);
thumb.value = val;
thumb.displayValue = this._transformValue(val);
thumb.percent = percent;
if (this._isSliding === 'pointer') {
thumb.focused = false;
}
thumb.styles = {
[pos.style]: pos.value
};
});
this._updateTrack();
}
_calculatePosition(percent) {
let style;
const value = `${percent}%`;
if (this.vertical) {
style = 'bottom';
}
else {
style = this._theme.variables.direction === 'rtl' ? 'right' : 'left';
}
return {
style,
value
};
}
_updateTrack() {
const track = this._track;
const thumbs = this._thumbs;
const thumbsPercents = thumbs.map(thumb => thumb.percent);
// const direction = this._theme.variables.direction === 'rtl' ? 'right' : 'left';
// const axis = this.vertical ? 'Y' : 'X';
// const sign = this._theme.variables.direction === 'rtl' ? '-' : '';
if (thumbs.length === 1) {
thumbsPercents.unshift(0);
}
const minPercent = this._minPercent = Math.min(...thumbsPercents);
const maxPercent = this._maxPercent = Math.max(...thumbsPercents);
const percent = (maxPercent / 100) - (minPercent / 100);
// const scale = this.vertical ? `1, ${percent}, 1` : `${percent}, 1, 1`;
const position = this.vertical ? 'height' : 'width';
const location = this.vertical ? 'bottom' : (this._theme.variables.direction === 'rtl' ? 'right' : 'left');
if (track) {
// track.nativeElement.style.transform = `translate${axis}(${sign}${minPercent}%)`;
// track.nativeElement.style.transform = `translate${axis}(${sign}${minPercent}%) scale3d(${scale})`;
track.nativeElement.style.width = '';
track.nativeElement.style.height = '';
track.nativeElement.style.left = '';
track.nativeElement.style.right = '';
track.nativeElement.style[position] = `${percent * 100}%`;
track.nativeElement.style[location] = `${minPercent}%`;
}
}
/** Emits a change event. */
_emitChangeEvent() {
this._controlValueAccessorChangeFn(this.value);
this.valueChange.emit(this.value);
this.change.emit(this._createChangeEvent());
}
/** Emits an input event. */
_emitInputEvent() {
this.input.emit(this._createChangeEvent());
}
_createChangeEvent(value = this.value) {
return new LySliderChange(this, value);
}
_roundValueToStep(value) {
return Number((Math.round(value / this.step) * this.step).toFixed(this._stepPrecision));
}
_transformValue(value) {
if (this.displayWith) {
return this.displayWith(value);
}
return value;
}
/** Increments the slider by the given number of steps (negative number decrements). */
_increment(numSteps) {
this._setOnSlideStart();
if (this._closestIndex == null) {
this._closestIndex = this._thumbsOnSlideStart.findIndex(_ => _.focused);
}
const index = this._closestIndex;
const thumb = this._thumbsOnSlideStart[index];
const newValue = clamp((thumb.value || 0) + this.step * numSteps, this.min, this.max);
thumb.value = newValue;
if (Array.isArray(this.value)) {
this.value = this._thumbsOnSlideStart.map(_ => _.value).sort(ASC);
}
else {
this.value = newValue;
}
// focus slidingThumb
const currentSlidingThumb = this._thumbs.find(t => t.value === newValue);
if (currentSlidingThumb) {
currentSlidingThumb.focused = true;
this._thumbsRef?.forEach((t, i) => {
if (i === currentSlidingThumb.index) {
t.nativeElement.focus();
}
else {
this._thumbs[i].focused = false;
}
});
}
}
_getHostElement() {
return this._el.nativeElement;
}
/**
* Get the bounding client rect of the slider track element.
* The track is used rather than the native element to ignore the extra space that the thumb can
* take up.
*/
_getSliderDimensions() {
return this._bg ? this._bg.nativeElement.getBoundingClientRect() : null;
}
_updateTickValues() {
this.__tickList = [];
if (!this.ticks || this.step == null) {
return false;
}
else {
const ticks = this.ticks;
this._tickInterval = typeof ticks === 'number'
? this.step * ticks
: this.step;
this.__tickList = [];
const stepWidth = this._tickInterval;
let cu = this.min;
this.__tickList.push(cu);
while (cu <= this.max) {
cu += stepWidth;
const newVal = clamp(cu, this.min, this.max);
this.__tickList.push(newVal);
// Remove duplicate value for next
if (newVal === this.max) {
break;
}
}
}
this._cd.markForCheck();
}
/**
* Focuses the native element.
*/
_focusHostElement(options) {
this._el.nativeElement.focus(options);
}
/**
* Binds our global move and end events. They're bound at the document level and only while
* dragging so that the user doesn't have to keep their pointer exactly over the slider
* as they're swiping across the screen.
*/
_bindGlobalEvents(triggerEvent) {
// Note that we bind the events to the `document`, because it allows us to capture
// drag cancel events where the user's pointer is outside the browser window.
const document = this._document;
const isTouch = isTouchEvent(triggerEvent);
const moveEventName = isTouch ? 'touchmove' : 'mousemove';
const endEventName = isTouch ? 'touchend' : 'mouseup';
document.addEventListener(moveEventName, this._pointerMove, activeEventOptions);
document.addEventListener(endEventName, this._pointerUp, activeEventOptions);
if (isTouch) {
document.addEventListener('touchcancel', this._pointerUp, activeEventOptions);
}
const window = this._getWindow();
if (typeof window !== 'undefined' && window) {
window.addEventListener('blur', this._windowBlur);
}
}
/** Removes any global event listeners that we may have added. */
_removeGlobalEvents() {
const document = this._document;
document.removeEventListener('mousemove', this._pointerMove, activeEventOptions);
document.removeEventListener('mouseup', this._pointerUp, activeEventOptions);
document.removeEventListener('touchmove', this._pointerMove, activeEventOptions);
document.removeEventListener('touchend', this._pointerUp, activeEventOptions);
document.removeEventListener('touchcancel', this._pointerUp, activeEventOptions);
const window = this._getWindow();
if (typeof window !== 'undefined' && window) {
window.removeEventListener('blur', this._windowBlur);
}
}
/** Use defaultView of injected document if available or fallback to global window reference */
_getWindow() {
return this._document.defaultView || window;
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.10", ngImport: i0, type: LySlider, deps: [{ token: i1.LyTheme2 }, { token: i0.ElementRef }, { token: i0.Renderer2 }, { token: i0.ChangeDetectorRef }, { token: i1.StyleRenderer }, { token: i0.NgZone }, { token: i2.FocusMonitor }, { token: DOCUMENT }, { token: LY_SLIDER_DEFAULT_OPTIONS, optional: true }], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.10", type: LySlider, isStandalone: false, selector: "ly-slider", inputs: { thumbVisible: "thumbVisible", marks: "marks", max: "max", min: "min", appearance: "appearance", size: "size", color: "color", vertical: "vertical", step: "step", value: "value", disabled: "disabled", ticks: "ticks", displayWith: "displayWith" }, outputs: { change: "change", input: "input", valueChange: "valueChange" }, host: { listeners: { "focus": "_onFocus()", "blur": "_onBlur()", "keydown": "_onKeydown($event)", "keyup": "_onKeyup()", "mouseenter": "_onMouseenter()", "selectstart": "$event.preventDefault()" } }, providers: [
LY_SLIDER_CONTROL_VALUE_ACCESSOR,
StyleRenderer,
{ provide: LY_SLIDER, useExisting: LySlider },
], viewQueries: [{ propertyName: "_wrapper", first: true, predicate: ["wrapper"], descendants: true, static: true }, { propertyName: "_bg", first: true, predicate: ["bg"], descendants: true }, { propertyName: "_track", first: true, predicate: ["track"], descendants: true, static: true }, { propertyName: "_ticksRef", first: true, predicate: ["ticksRef"], descendants: true, static: true }, { propertyName: "_thumbsRef", predicate: ["thumbsRef"], descendants: true }], exportAs: ["lySlider"], usesOnChanges: true, ngImport: i0, template: "<div #wrapper [className]=\"classes.wrapper\">\n <div #bg [className]=\"classes.bg\"></div>\n <div #track [className]=\"classes.track\"></div>\n <ng-template [ngIf]=\"ticks\">\n <ly-tick *ngFor=\"let tick of _tickList\" [value]=\"tick\"></ly-tick>\n </ng-template>\n <span #ticksRef></span>\n <ng-template [ngIf]=\"marks\">\n <ng-content select=\"ly-mark\"></ng-content>\n </ng-template>\n <ng-template [ngIf]=\"_marksList\">\n <ly-mark *ngFor=\"let mark of _marksList\" [value]=\"mark.value\">{{ mark.label }}</ly-mark>\n </ng-template>\n <div\n *ngFor=\"let thumb of _thumbs; trackBy: _trackByFn\"\n [className]=\"classes.thumbContainer\"\n [ngStyle]=\"thumb.styles\"\n >\n <div\n [className]=\"classes.thumbContent\"\n [ngClass]=\"thumb.focused ? classes.thumbContentFocused : null\"\n #thumbsRef\n (focus)=\"_onFocusThumb(thumb)\"\n (blur)=\"_onBlurThumb(thumb)\"\n [attr.tabindex]=\"disabled ? -1 : 0\"\n >\n <div\n [className]=\"classes.thumb\"\n ></div>\n <div [className]=\"classes.thumbLabel\" *ngIf=\"thumbVisible !== false\">\n <span [className]=\"classes.thumbLabelValue\">{{ thumb.displayValue }}</span>\n </div>\n </div>\n </div>\n</div>\n", dependencies: [{ kind: "directive", type: i3.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i3.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i3.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i3.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "component", type: LyMark, selector: "ly-mark", inputs: ["value"] }, { kind: "directive", type: LyTick, selector: "ly-tick", inputs: ["value"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
}
__decorate([
Style(val => (theme, ref) => {
const slider = ref.selectorsOf(STYLES);
if (theme.slider?.size) {
const size = theme.slider.size[val];
if (size) {
return size instanceof StyleCollection
? size.setTransformer((_) => _(slider)).css
: size(slider);
}
}
throw new Error(`${LySlider.и}: styles theme.checkbox.size is undefined`);
}, STYLE_PRIORITY)
], LySlider.prototype, "size", void 0);
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.10", ngImport: i0, type: LySlider, decorators: [{
type: Component,
args: [{ selector: 'ly-slider', changeDetection: ChangeDetectionStrategy.OnPush, exportAs: 'lySlider', providers: [
LY_SLIDER_CONTROL_VALUE_ACCESSOR,
StyleRenderer,
{ provide: LY_SLIDER, useExisting: LySlider },
], host: {
'(focus)': '_onFocus()',
'(blur)': '_onBlur()',
'(keydown)': '_onKeydown($event)',
'(keyup)': '_onKeyup()',
'(mouseenter)': '_onMouseenter()',
// On Safari starting to slide temporarily triggers text selection mode which
// show the wrong cursor. We prevent it by stopping the `selectstart` event.
'(selectstart)': '$event.preventDefault()',
}, standalone: false, template: "<div #wrapper [className]=\"classes.wrapper\">\n <div #bg [className]=\"classes.bg\"></div>\n <div #track [className]=\"classes.track\"></div>\n <ng-template [ngIf]=\"ticks\">\n <ly-tick *ngFor=\"let tick of _tickList\" [value]=\"tick\"></ly-tick>\n </ng-template>\n <span #ticksRef></span>\n <ng-template [ngIf]=\"marks\">\n <ng-content select=\"ly-mark\"></ng-content>\n </ng-template>\n <ng-template [ngIf]=\"_marksList\">\n <ly-mark *ngFor=\"let mark of _marksList\" [value]=\"mark.value\">{{ mark.label }}</ly-mark>\n </ng-template>\n <div\n *ngFor=\"let thumb of _thumbs; trackBy: _trackByFn\"\n [className]=\"classes.thumbContainer\"\n [ngStyle]=\"thumb.styles\"\n >\n <div\n [className]=\"classes.thumbContent\"\n [ngClass]=\"thumb.focused ? classes.thumbContentFocused : null\"\n #thumbsRef\n (focus)=\"_onFocusThumb(thumb)\"\n (blur)=\"_onBlurThumb(thumb)\"\n [attr.tabindex]=\"disabled ? -1 : 0\"\n >\n <div\n [className]=\"classes.thumb\"\n ></div>\n <div [className]=\"classes.thumbLabel\" *ngIf=\"thumbVisible !== false\">\n <span [className]=\"classes.thumbLabelValue\">{{ thumb.displayValue }}</span>\n </div>\n </div>\n </div>\n</div>\n" }]
}], ctorParameters: () => [{ type: i1.LyTheme2 }, { type: i0.ElementRef }, { type: i0.Renderer2 }, { type: i0.ChangeDetectorRef }, { type: i1.StyleRenderer }, { type: i0.NgZone }, { type: i2.FocusMonitor }, { type: undefined, decorators: [{
type: Inject,
args: [DOCUMENT]
}] }, { type: undefined, decorators: [{
type: Optional
}, {
type: Inject,
args: [LY_SLIDER_DEFAULT_OPTIONS]
}] }], propDecorators: { _wrapper: [{
type: ViewChild,
args: ['wrapper', { static: true }]
}], thumbVisible: [{
type: Input
}], marks: [{
type: Input
}], max: [{
type: Input
}], min: [{
type: Input
}], appearance: [{
type: Input
}], size: [{
type: Input
}], color: [{
type: Input
}], vertical: [{
type: Input
}], step: [{
type: Input
}], value: [{
type: Input
}], disabled: [{
type: Input
}], ticks: [{
type: Input
}], _bg: [{
type: ViewChild,
args: ['bg']
}], _track: [{
type: ViewChild,
args: ['track', { static: true }]
}], _ticksRef: [{
type: ViewChild,
args: ['ticksRef', { static: true }]
}], _thumbsRef: [{
type: ViewChildren,
args: ['thumbsRef']
}], displayWith: [{
type: Input
}], change: [{
type: Output
}], input: [{
type: Output
}], valueChange: [{
type: Output
}] } });
function findClosest(values, currentValue) {
const { index: closestIndex } = values.reduce((previousValue, value, index) => {
const distance = Math.abs(currentValue - value);
if (previousValue === null || distance < previousValue.distance || distance === previousValue.distance) {
return {
distance,
index,
};
}
return previousValue;
}, null);
return closestIndex;
}
function percentToValue(percent, min, max) {
return (max - min) * (percent / 100) + min;
}
function arrayEquals(array1, array2) {
return Array.isArray(array1) && Array.isArray(array2) && array1.length === array2.length
&& array1.every((value, index) => value === array2[index]);
}
/** Quickly check if two values are equal */
function valueEquals(value, value2) {
if (value === value2) {
return true;
}
return arrayEquals(value, value2);
}
function clamp(value, min, max) {
if (value < min) {
return min;
}
if (value > max) {
return max;
}
return value;
}
function ASC(a, b) {
return a - b;
}
/** Returns whether an event is a touch event. */
function isTouchEvent(event) {
// This function is called for every pixel that the user has dragged so we need it to be
// as fast as possible. Since we only bind mouse events and touch events, we can assume
// that if the event's name starts with `t`, it's a touch event.
return event.type[0] === 't';
}
/** Gets the unique ID of a touch that matches a specific slider. */
function getTouchIdForSlider(event, sliderHost) {
for (let i = 0; i < event.touches.length; i++) {
const target = event.touches[i].target;
if (sliderHost === target || sliderHost.contains(target)) {
return event.touches[i].identifier;
}
}
return undefined;
}
/** Gets the coordinates of a touch or mouse event relative to the viewport. */
function getPointerPositionOnPage(event, id) {
let point;
if (isTouchEvent(event)) {
// The `identifier` could be undefined if the browser doesn't support `TouchEvent.identifier`.
// If that's the case, attribute the first touch to all active sliders. This should still cover
// the most common case while only breaking multi-touch.
if (typeof id === 'number') {
point = findMatchingTouch(event.touches, id) || findMatchingTouch(event.changedTouches, id);
}
else {
// `touches` will be empty for start/end events so we have to fall back to `changedTouches`.
point = event.touches[0] || event.changedTouches[0];
}
}
else {
point = event;
}
return point ? { x: point.clientX, y: point.clientY } : undefined;
}
/** Finds a `Touch` with a specific ID in a `TouchList`. */
function findMatchingTouch(touches, id) {
for (let i = 0; i < touches.length; i++) {
if (touches[i].identifier === id) {
return touches[i];
}
}
return undefined;
}
class LySliderModule {
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.10", ngImport: i0, type: LySliderModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "19.2.10", ngImport: i0, type: LySliderModule, declarations: [LySlider, LyMark, LyTick], imports: [CommonModule,
LyCommonModule], exports: [LySlider, LyMark] }); }
static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "19.2.10", ngImport: i0, type: LySliderModule, imports: [CommonModule,
LyCommonModule] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.10", ngImport: i0, type: LySliderModule, decorators: [{
type: NgModule,
args: [{
imports: [
CommonModule,
LyCommonModule
],
declarations: [LySlider, LyMark, LyTick],
exports: [LySlider, LyMark]
}]
}] });
/**
* Generated bundle index. Do not edit.
*/
export { LY_SLIDER_CONTROL_VALUE_ACCESSOR, LY_SLIDER_DEFAULT_OPTIONS, LyMark, LySlider, LySliderChange, LySliderModule, LyTick, STYLES };
//# sourceMappingURL=alyle-ui-slider.mjs.map