UNPKG

@ux-aspects/ux-aspects

Version:

Open source user interface framework for building modern, responsive, mobile big data applications

692 lines 129 kB
import { coerceBooleanProperty } from '@angular/cdk/coercion'; import { ChangeDetectionStrategy, ChangeDetectorRef, Component, EventEmitter, inject, Input, Output, ViewChild } from '@angular/core'; import { ColorService } from '../../services/color/index'; import * as i0 from "@angular/core"; import * as i1 from "../../directives/accessibility/focus-indicator/focus-indicator.directive"; import * as i2 from "../../directives/drag/drag.directive"; export class SliderComponent { /** A set of options to customize the appearance and behavior of the slider. */ set options(options) { this._options = options; this.updateOptions(); } /** Whether the slider is disabled. */ set disabled(disabled) { this._disabled = coerceBooleanProperty(disabled); } get disabled() { return this._disabled; } get options() { return this._options; } constructor() { this.colorService = inject(ColorService); this._changeDetectorRef = inject(ChangeDetectorRef); /** A single number or a SliderValue object, depending on the slider type specified. */ this.value = 0; /** Emits when the `value` changes. */ this.valueChange = new EventEmitter(); this._disabled = false; // expose enums to Angular view this.sliderType = SliderType; this.sliderStyle = SliderStyle; this.sliderSize = SliderSize; this.sliderSnap = SliderSnap; this.sliderThumb = SliderThumb; this.sliderTickType = SliderTickType; this.sliderThumbEvent = SliderThumbEvent; this.sliderCalloutTrigger = SliderCalloutTrigger; this.tracks = { lower: { size: 0, color: '' }, middle: { size: 0, color: '' }, upper: { size: 0, color: '' } }; this.tooltips = { lower: { visible: false, position: 0, label: '' }, upper: { visible: false, position: 0, label: '' } }; this.thumbs = { lower: { hover: false, drag: false, position: 0, order: 100, value: null }, upper: { hover: false, drag: false, position: 0, order: 101, value: null } }; // store all the ticks to display this.ticks = []; // setup default options this.defaultOptions = { type: SliderType.Value, handles: { style: SliderStyle.Button, callout: { trigger: SliderCalloutTrigger.None, background: this.colorService.getColor('grey2').toHex(), color: '#fff', formatter: (value) => value }, keyboard: { major: 5, minor: 1 }, aria: { thumb: 'Slider value', lowerThumb: 'Slider lower value', upperThumb: 'Slider upper value' } }, track: { height: SliderSize.Wide, min: 0, max: 100, ticks: { snap: SliderSnap.None, major: { show: true, steps: 10, labels: true, formatter: (value) => value }, minor: { show: true, steps: 5, labels: false, formatter: (value) => value } }, colors: {} } }; } ngOnInit() { this.updateValues(); this.setThumbState(SliderThumb.Lower, false, false); this.setThumbState(SliderThumb.Upper, false, false); // emit the initial value this.valueChange.next(this.clone(this.value)); } ngDoCheck() { if (this.detectValueChange(this.value, this._value)) { this.updateValues(); this._value = this.clone(this.value); } } ngAfterViewInit() { // persistent tooltips will need positioned correctly at this stage setTimeout(() => { this.updateTooltipPosition(SliderThumb.Lower); this.updateTooltipPosition(SliderThumb.Upper); // mark as dirty this._changeDetectorRef.markForCheck(); }); this.updateOrder(); } snapToNearestTick(thumb, snapTarget, forwards) { if (this.disabled) { return; } // get the value for the thumb const { value } = this.getThumbState(thumb); // get the closest ticks - remove any tick if we are currently on it const closest = this.getTickDistances(value, thumb, snapTarget) .filter(tick => tick.value !== value) .find(tick => forwards ? tick.value > value : tick.value < value); // If we have no ticks then move by a predefined amount if (closest) { return this.setThumbValue(thumb, this.validateValue(thumb, closest.value)); } const step = snapTarget === SliderSnap.Major ? this.options.handles.keyboard.major : this.options.handles.keyboard.minor; this.setThumbValue(thumb, this.validateValue(thumb, value + (forwards ? step : -step))); } snapToEnd(thumb, forwards) { this.setThumbValue(thumb, this.validateValue(thumb, forwards ? this.options.track.max : this.options.track.min)); } getThumbValue(thumb) { return this.getThumbState(thumb).value; } getFormattedValue(thumb) { return this.options.handles.callout.formatter(this.getThumbState(thumb).value); } getThumbState(thumb) { return thumb === SliderThumb.Lower ? this.thumbs.lower : this.thumbs.upper; } setThumbState(thumb, hover, drag) { if (thumb === SliderThumb.Lower) { this.thumbs.lower.hover = hover; this.thumbs.lower.drag = drag; } else { this.thumbs.upper.hover = hover; this.thumbs.upper.drag = drag; } // update the visibility of the tooltips this.updateTooltips(thumb); } thumbEvent(thumb, event) { if (this.disabled) { return; } // get the current thumb state const state = this.getThumbState(thumb); // update based upon event switch (event) { case SliderThumbEvent.DragStart: state.drag = true; break; case SliderThumbEvent.DragEnd: state.drag = false; break; case SliderThumbEvent.MouseOver: state.hover = true; break; case SliderThumbEvent.MouseLeave: state.hover = false; break; case SliderThumbEvent.None: state.drag = false; state.hover = false; break; } // update the thumb state this.setThumbState(thumb, state.hover, state.drag); this.updateOrder(); } getAriaValueText(thumb) { // get the current thumb value const value = this.getThumbValue(thumb); // get all the ticks const tick = this.ticks.find(_tick => _tick.value === value); if (tick && tick.label) { return tick.label; } // otherwise simply display the formatted value return this.getFormattedValue(thumb); } updateTooltips(thumb) { let visible = false; const state = this.getThumbState(thumb); switch (this.options.handles.callout.trigger) { case SliderCalloutTrigger.Persistent: visible = true; break; case SliderCalloutTrigger.Drag: visible = state.drag; break; case SliderCalloutTrigger.Hover: visible = state.hover || state.drag; break; case SliderCalloutTrigger.Dynamic: visible = true; break; } // update the state for the corresponding thumb this.getTooltip(thumb).visible = visible; // update the tooltip text this.updateTooltipText(thumb); // update the tooltip positions this.updateTooltipPosition(thumb); } updateTooltipText(thumb) { // get the thumb value const tooltip = this.getTooltip(thumb); // store the formatted label tooltip.label = this.getFormattedValue(thumb).toString(); } getTooltipElement(thumb) { return thumb === SliderThumb.Lower ? this.lowerTooltip : this.upperTooltip; } getTooltip(thumb) { return thumb === SliderThumb.Lower ? this.tooltips.lower : this.tooltips.upper; } updateTooltipPosition(thumb) { const tooltip = this.getTooltip(thumb); // if tooltip is not visible then stop here if (tooltip.visible === false) { return; } const tooltipElement = this.getTooltipElement(thumb); // get the element widths let thumbWidth; if (this._options.handles.style === SliderStyle.Button) { thumbWidth = this._options.track.height === SliderSize.Narrow ? 16 : 24; } else { thumbWidth = 2; } const tooltipWidth = tooltipElement.nativeElement.offsetWidth; // calculate the tooltips new position const tooltipPosition = Math.ceil((tooltipWidth - thumbWidth) / 2); // update tooltip position tooltip.position = -tooltipPosition; if (this._options.type === SliderType.Range && this._options.handles.callout.trigger === SliderCalloutTrigger.Dynamic) { this.preventTooltipOverlap(tooltip); } } preventTooltipOverlap(tooltip) { const trackWidth = this.track.nativeElement.offsetWidth; const lower = (trackWidth / 100) * this.thumbs.lower.position; const upper = (trackWidth / 100) * this.thumbs.upper.position; const lowerWidth = this.lowerTooltip.nativeElement.offsetWidth / 2; const upperWidth = this.upperTooltip.nativeElement.offsetWidth / 2; const diff = (lower + lowerWidth) - (upper - upperWidth); // if the tooltips are closer than 16px then adjust so the dont move any close if (diff > 0) { if (tooltip === this.tooltips.lower && this.thumbs.lower.drag === false) { tooltip.position -= (diff / 2); } else if (tooltip === this.tooltips.upper && this.thumbs.upper.drag === false) { tooltip.position += (diff / 2); } } } clamp(value, min, max) { return Math.min(Math.max(value, min), max); } updateThumbPosition(event, thumb) { if (this.disabled) { return; } // get event position - either mouse or touch const eventPosition = event instanceof MouseEvent ? event.clientX : event.touches && event.touches.length > 0 ? event.touches[0].clientX : null; // if event position is null do nothing if (eventPosition === null) { return; } // get mouse position const mouseX = window.pageXOffset + eventPosition; // get track size and position const trackBounds = this.track.nativeElement.getBoundingClientRect(); // restrict the value within the range size const position = this.clamp(mouseX - trackBounds.left, 0, trackBounds.width); // get fraction representation of location within the track const fraction = (position / trackBounds.width); // convert to value within the range let value = ((this._options.track.max - this._options.track.min) * fraction) + this._options.track.min; // ensure value is valid value = this.validateValue(thumb, value); // snap to a tick if required value = this.snapToTick(value, thumb); // update the value accordingly this.setThumbValue(thumb, value); this.updateOrderOnDrag(thumb); this.updateValues(); // update tooltip text & position this.updateTooltipText(thumb); // update the position of all visible tooltips this.updateTooltipPosition(SliderThumb.Lower); this.updateTooltipPosition(SliderThumb.Upper); // mark as dirty for change detection this._changeDetectorRef.markForCheck(); } updateOrderOnDrag(thumb) { const lower = thumb === SliderThumb.Lower ? 101 : 100; const upper = thumb === SliderThumb.Lower ? 100 : 101; // Ensure currently dragged thumb is on top this.thumbs.lower.order = lower; this.thumbs.upper.order = upper; } updateOrder() { const isDragged = this.thumbs.lower.drag || this.thumbs.upper.drag; if (this._options && !isDragged) { const lowerValue = this.getThumbValue(this.sliderThumb.Lower); const upperValue = this.getThumbValue(this.sliderThumb.Upper); const max = this._options.track.max; const min = this._options.track.min; const range = max - min; const median = (range / 100 * 50) + min; if (upperValue <= median) { this.thumbs.lower.order = 100; this.thumbs.upper.order = 101; } if (lowerValue > median) { this.thumbs.lower.order = 101; this.thumbs.upper.order = 100; } } } getTickDistances(value, thumb, snapTarget) { // if snap target is none then return original value if (snapTarget === SliderSnap.None) { return []; } // get filtered ticks let ticks; switch (snapTarget) { case SliderSnap.Minor: ticks = this.ticks.filter(tick => tick.type === SliderTickType.Minor); break; case SliderSnap.Major: ticks = this.ticks.filter(tick => tick.type === SliderTickType.Major); break; default: ticks = this.ticks.slice(0); } // get the track limit let lowerLimit = this._options.track.min; let upperLimit = this._options.track.max; if (this._options.type === SliderType.Range && thumb === SliderThumb.Lower) { upperLimit = this.thumbs.upper.value; } if (this._options.type === SliderType.Range && thumb === SliderThumb.Upper) { lowerLimit = this.thumbs.lower.value; } // Find the closest tick to the current position const range = ticks.filter(tick => tick.value >= lowerLimit && tick.value <= upperLimit); // If there are no close ticks in the valid range then dont snap if (range.length === 0) { return []; } return range.sort((tickOne, tickTwo) => { const tickOneDelta = Math.max(tickOne.value, value) - Math.min(tickOne.value, value); const tickTwoDelta = Math.max(tickTwo.value, value) - Math.min(tickTwo.value, value); return tickOneDelta - tickTwoDelta; }); } snapToTick(value, thumb) { const tickDistances = this.getTickDistances(value, thumb, this._options.track.ticks.snap); // if there are no ticks return the current value if (tickDistances.length === 0) { return value; } // get the closest tick return tickDistances[0].value; } validateValue(thumb, value) { // if slider is not a range value is always valid providing it is within the chart min and max values if (this._options.type === SliderType.Value) { return Math.max(Math.min(value, this._options.track.max), this._options.track.min); } // check if value is with chart ranges if (value > this._options.track.max) { return thumb === SliderThumb.Lower ? Math.min(this._options.track.max, this.thumbs.upper.value) : this._options.track.max; } if (value < this._options.track.min) { return thumb === SliderThumb.Upper ? Math.max(this._options.track.min, this.thumbs.lower.value) : this._options.track.min; } // otherwise we need to check to make sure lower thumb cannot go above higher and vice versa if (thumb === SliderThumb.Lower) { if (this.thumbs.upper.value === null) { return value; } return value <= this.thumbs.upper.value ? value : this.thumbs.upper.value; } if (thumb === SliderThumb.Upper) { if (this.thumbs.lower.value === null) { return value; } return value >= this.thumbs.lower.value ? value : this.thumbs.lower.value; } } updateOptions() { // add in the default options that user hasn't specified this._options = this.deepMerge(this._options || {}, this.defaultOptions); this.updateTrackColors(); this.updateTicks(); this.updateValues(); } updateValues() { if (this.value === undefined || this.value === null) { this.value = 0; } let lowerValue = typeof this.value === 'number' ? this.value : this.value.low; let upperValue = typeof this.value === 'number' ? this.value : this.value.high; // validate values lowerValue = this.validateValue(SliderThumb.Lower, Number(lowerValue.toFixed(4))); upperValue = this.validateValue(SliderThumb.Upper, Number(upperValue.toFixed(4))); // calculate the positions as percentages const lowerPosition = (((lowerValue - this._options.track.min) / (this._options.track.max - this._options.track.min)) * 100); const upperPosition = (((upperValue - this._options.track.min) / (this._options.track.max - this._options.track.min)) * 100); // update thumb positions this.thumbs.lower.position = lowerPosition; this.thumbs.upper.position = upperPosition; // calculate the track sizes this.tracks.lower.size = lowerPosition; this.tracks.middle.size = upperPosition - lowerPosition; this.tracks.upper.size = this._options.type === SliderType.Value ? 100 - lowerPosition : 100 - upperPosition; // update the value input this.setValue(lowerValue, upperValue); } setValue(low, high) { this.thumbs.lower.value = low; this.thumbs.upper.value = high; const previousValue = this.clone(this._value); this.value = this._options.type === SliderType.Value ? low : { low, high }; // call the event emitter if changes occured if (this.detectValueChange(this.value, previousValue)) { this.valueChange.emit(this.clone(this.value)); this.updateTooltipText(SliderThumb.Lower); this.updateTooltipText(SliderThumb.Upper); } else { this.valueChange.emit(this.clone(this.value)); } } setThumbValue(thumb, value) { // update the thumb value this.getThumbState(thumb).value = value; // forward these changes to the value this.setValue(this.thumbs.lower.value, this.thumbs.upper.value); } updateTicks() { // get tick options const majorOptions = this._options.track.ticks.major; const minorOptions = this._options.track.ticks.minor; // check if we should show ticks if (majorOptions.show === false && minorOptions.show === false) { this.ticks = []; } // create ticks for both major and minor - only get the ones to be shown const majorTicks = this.getTicks(majorOptions, SliderTickType.Major).filter(tick => tick.showTicks); const minorTicks = this.getTicks(minorOptions, SliderTickType.Minor).filter(tick => tick.showTicks); // remove any minor ticks that are on a major interval this.ticks = this.unionTicks(majorTicks, minorTicks); } updateTrackColors() { // get colors for each part of the track const { lower, range, higher } = this._options.track.colors; // update the controller value this.tracks.lower.color = this.getTrackColorStyle(lower); this.tracks.middle.color = this.getTrackColorStyle(range); this.tracks.upper.color = this.getTrackColorStyle(higher); } /** Map the color value to the correct CSS color value */ getTrackColorStyle(color) { return Array.isArray(color) ? `linear-gradient(to right, ${color.join(', ')})` : color; } getSteps(steps) { // if they are already an array just return it if (steps instanceof Array) { return steps; } const output = []; // otherwise calculate the steps for (let idx = this._options.track.min; idx <= this._options.track.max; idx += steps) { output.push(idx); } return output; } getTicks(options, type) { // create an array to store the ticks and step points const steps = this.getSteps(options.steps); // get some chart options const min = this._options.track.min; const max = this._options.track.max; // convert each step to a slider tick and remove invalid ticks return steps.map(step => { return { showTicks: options.show, showLabels: options.labels, type, position: ((step - min) / (max - min)) * 100, value: step, label: options.formatter(step) }; }).filter(tick => tick.position >= 0 && tick.position <= 100); } unionTicks(majorTicks, minorTicks) { // get all ticks combined removing any minor ticks with the same value as major ticks return majorTicks.concat(minorTicks) .filter((tick, index, array) => tick.type === SliderTickType.Major || !array.find(tk => tk.type === SliderTickType.Major && tk.position === tick.position)) .sort((t1, t2) => t1.value - t2.value); } deepMerge(destination, source) { // loop though all of the properties in the source object for (const prop in source) { // check if the destination object has the property // eslint-disable-next-line no-prototype-builtins if (!destination.hasOwnProperty(prop)) { // copy the property across destination[prop] = source[prop]; continue; } // if the property exists and is not an object then skip if (typeof destination[prop] !== 'object') { continue; } // check if property is an array if (destination[prop] instanceof Array) { continue; } // if it is an object then perform a recursive check destination[prop] = this.deepMerge(destination[prop], source[prop]); } return destination; } detectValueChange(value1, value2) { // compare two slider values if (this.isSliderValue(value1) && this.isSliderValue(value2)) { // references to the objects in the correct types const obj1 = value1; const obj2 = value2; return obj1.low !== obj2.low || obj1.high !== obj2.high; } // if not a slider value - should be number of nullable type - compare normally return value1 !== value2; } /** * Determines whether or not an object conforms to the * SliderValue interface. * @param value - The object to check - this must be type any */ isSliderValue(value) { // check if is an object if (typeof value !== 'object') { return false; } // next check if it contains the necessary properties return 'low' in value && 'high' in value; } clone(value) { // if it is not an object simply return the value if (typeof value !== 'object') { return value; } // create a new object from the existing one const instance = { ...value }; // delete remove the value from the old object value = undefined; // return the new instance of the object return instance; } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.12", ngImport: i0, type: SliderComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); } static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.12", type: SliderComponent, selector: "ux-slider", inputs: { value: "value", options: "options", disabled: "disabled" }, outputs: { valueChange: "valueChange" }, host: { properties: { "class.disabled": "disabled" } }, viewQueries: [{ propertyName: "lowerTooltip", first: true, predicate: ["lowerTooltip"], descendants: true, static: true }, { propertyName: "upperTooltip", first: true, predicate: ["upperTooltip"], descendants: true, static: true }, { propertyName: "track", first: true, predicate: ["track"], descendants: true, static: true }], ngImport: i0, template: "<div class=\"track\"\n #track\n [class.narrow]=\"_options.track.height === sliderSize.Narrow\"\n [class.wide]=\"_options.track.height === sliderSize.Wide\"\n [class.range]=\"_options.type === sliderType.Range\">\n\n <!-- Section Beneath Lower Thumb -->\n <div class=\"track-section track-lower\" [style.flex-grow]=\"tracks.lower.size\" [style.background]=\"this.disabled ? null : tracks.lower.color\"></div>\n\n <!-- Lower Thumb Button / Line -->\n <div class=\"thumb lower\"\n uxDrag\n uxFocusIndicator\n role=\"slider\"\n [tabindex]=\"disabled ? -1 : 0\"\n #lowerthumb\n [attr.aria-label]=\"_options.type === sliderType.Range ? _options.handles.aria.lowerThumb :\n _options.handles.aria.thumb\"\n [attr.aria-valuemin]=\"_options?.track?.min\"\n [attr.aria-valuemax]=\"_options.type === sliderType.Range ? getThumbValue(sliderThumb.Upper) :\n _options?.track?.max\"\n [attr.aria-valuenow]=\"getThumbValue(sliderThumb.Lower)\"\n [attr.aria-valuetext]=\"getAriaValueText(sliderThumb.Lower)\"\n [style.left.%]=\"thumbs.lower.position\"\n [class.active]=\"thumbs.lower.drag\"\n [style.z-index]=\"thumbs.lower.order\"\n [class.button]=\"_options.handles.style === sliderStyle.Button\"\n [class.line]=\"_options.handles.style === sliderStyle.Line\"\n [class.narrow]=\"_options.track.height === sliderSize.Narrow\"\n [class.wide]=\"_options.track.height === sliderSize.Wide\"\n (onDragStart)=\"thumbEvent(sliderThumb.Lower, sliderThumbEvent.DragStart); lowerthumb.focus()\"\n (onDrag)=\"updateThumbPosition($event, sliderThumb.Lower)\"\n (onDragEnd)=\"thumbEvent(sliderThumb.Lower, sliderThumbEvent.DragEnd)\"\n (mouseenter)=\"thumbEvent(sliderThumb.Lower, sliderThumbEvent.MouseOver)\"\n (mouseleave)=\"thumbEvent(sliderThumb.Lower, sliderThumbEvent.MouseLeave)\"\n (focus)=\"thumbEvent(sliderThumb.Lower, sliderThumbEvent.MouseOver)\"\n (blur)=\"thumbEvent(sliderThumb.Lower, sliderThumbEvent.MouseLeave)\"\n (keydown.ArrowLeft)=\"snapToNearestTick(sliderThumb.Lower, sliderSnap.All, false); $event.preventDefault()\"\n (keydown.ArrowRight)=\"snapToNearestTick(sliderThumb.Lower, sliderSnap.All, true); $event.preventDefault()\"\n (keydown.ArrowUp)=\"snapToNearestTick(sliderThumb.Lower, sliderSnap.All, false); $event.preventDefault()\"\n (keydown.ArrowDown)=\"snapToNearestTick(sliderThumb.Lower, sliderSnap.All, true); $event.preventDefault()\"\n (keydown.PageDown)=\"snapToNearestTick(sliderThumb.Lower, sliderSnap.Major, false); $event.preventDefault()\"\n (keydown.PageUp)=\"snapToNearestTick(sliderThumb.Lower, sliderSnap.Major, true); $event.preventDefault()\"\n (keydown.Home)=\"snapToEnd(sliderThumb.Lower, false); $event.preventDefault()\"\n (keydown.End)=\"snapToEnd(sliderThumb.Lower, true); $event.preventDefault()\">\n\n <!-- Lower Thumb Callout -->\n <div class=\"tooltip top tooltip-lower\" #lowerTooltip\n [class.tooltip-dynamic]=\"_options.handles.callout.trigger === sliderCalloutTrigger.Dynamic &&\n thumbs.lower.drag === false\"\n [style.opacity]=\"tooltips.lower.visible ? 1 : 0\"\n [style.left.px]=\"tooltips.lower.position\">\n\n <div class=\"tooltip-arrow\" [style.border-top-color]=\"_options.handles.callout.background\"></div>\n\n <div class=\"tooltip-inner\"\n [style.background-color]=\"_options.handles.callout.background\"\n [style.color]=\"_options.handles.callout.color\">\n {{ tooltips.lower.label }}\n </div>\n </div>\n\n </div>\n\n <!-- Section of Track Between Lower and Upper Thumbs -->\n @if (_options.type === sliderType.Range) {\n <div class=\"track-section track-range\"\n [style.flex-grow]=\"tracks.middle.size\" [style.background]=\"this.disabled ? null : tracks.middle.color\">\n </div>\n }\n\n <!-- Upper Thumb Button / Line -->\n <div class=\"thumb upper\"\n uxDrag\n uxFocusIndicator\n role=\"slider\"\n [tabindex]=\"disabled ? -1 : 0\"\n #upperthumb\n [attr.aria-label]=\"_options.handles.aria.upperThumb\"\n [attr.aria-valuemin]=\"getThumbValue(sliderThumb.Lower) || _options?.track?.min\"\n [attr.aria-valuemax]=\"_options?.track?.max\"\n [attr.aria-valuenow]=\"getThumbValue(sliderThumb.Upper)\"\n [attr.aria-valuetext]=\"getAriaValueText(sliderThumb.Upper)\"\n [hidden]=\"_options.type !== sliderType.Range\"\n [class.active]=\"thumbs.upper.drag\"\n [style.left.%]=\"thumbs.upper.position\"\n [style.z-index]=\"thumbs.upper.order\"\n [class.button]=\"_options.handles.style === sliderStyle.Button\"\n [class.line]=\"_options.handles.style === sliderStyle.Line\"\n [class.narrow]=\"_options.track.height === sliderSize.Narrow\"\n [class.wide]=\"_options.track.height === sliderSize.Wide\"\n (onDragStart)=\"thumbEvent(sliderThumb.Upper, sliderThumbEvent.DragStart); upperthumb.focus()\"\n (onDrag)=\"updateThumbPosition($event, sliderThumb.Upper)\"\n (onDragEnd)=\"thumbEvent(sliderThumb.Upper, sliderThumbEvent.DragEnd)\"\n (mouseenter)=\"thumbEvent(sliderThumb.Upper, sliderThumbEvent.MouseOver)\"\n (mouseleave)=\"thumbEvent(sliderThumb.Upper, sliderThumbEvent.MouseLeave)\"\n (focus)=\"thumbEvent(sliderThumb.Upper, sliderThumbEvent.MouseOver)\"\n (blur)=\"thumbEvent(sliderThumb.Upper, sliderThumbEvent.MouseLeave)\"\n (keydown.ArrowLeft)=\"snapToNearestTick(sliderThumb.Upper, sliderSnap.All, false); $event.preventDefault()\"\n (keydown.ArrowRight)=\"snapToNearestTick(sliderThumb.Upper, sliderSnap.All, true); $event.preventDefault()\"\n (keydown.ArrowUp)=\"snapToNearestTick(sliderThumb.Upper, sliderSnap.All, false); $event.preventDefault()\"\n (keydown.ArrowDown)=\"snapToNearestTick(sliderThumb.Upper, sliderSnap.All, true); $event.preventDefault()\"\n (keydown.PageDown)=\"snapToNearestTick(sliderThumb.Upper, sliderSnap.Major, false); $event.preventDefault()\"\n (keydown.PageUp)=\"snapToNearestTick(sliderThumb.Upper, sliderSnap.Major, true); $event.preventDefault()\"\n (keydown.Home)=\"snapToEnd(sliderThumb.Upper, false); $event.preventDefault()\"\n (keydown.End)=\"snapToEnd(sliderThumb.Upper, true); $event.preventDefault()\">\n\n <!-- Upper Thumb Callout -->\n <div class=\"tooltip top tooltip-upper\" #upperTooltip\n [class.tooltip-dynamic]=\"_options.handles.callout.trigger === sliderCalloutTrigger.Dynamic &&\n thumbs.upper.drag === false\"\n [style.opacity]=\"tooltips.upper.visible ? 1 : 0\"\n [style.left.px]=\"tooltips.upper.position\">\n\n <div class=\"tooltip-arrow\" [style.border-top-color]=\"_options.handles.callout.background\"></div>\n\n @if (_options.type === sliderType.Range) {\n <div class=\"tooltip-inner\"\n [style.background-color]=\"_options.handles.callout.background\"\n [style.color]=\"_options.handles.callout.color\">\n {{ tooltips.upper.label }}\n </div>\n }\n </div>\n </div>\n\n <!-- Section of Track Abover Upper Thumb -->\n <div class=\"track-section track-higher\" [style.flex-grow]=\"tracks.upper.size\" [style.background]=\"this.disabled ? null : tracks.upper.color\"></div>\n\n</div>\n\n<!-- Chart Ticks and Tick Labels -->\n@if ((_options.track.ticks.major.show || _options.track.ticks.minor.show) &&\n _options.handles.callout.trigger !== sliderCalloutTrigger.Dynamic) {\n <div class=\"tick-container\"\n role=\"presentation\"\n [class.show-labels]=\"_options.track.ticks.major.labels || _options.track.ticks.minor.labels\">\n @for (tick of ticks; track tick) {\n <div class=\"tick\"\n [class.major]=\"tick.type === sliderTickType.Major\"\n [class.minor]=\"tick.type === sliderTickType.Minor\"\n [style.left.%]=\"tick.position\"\n [hidden]=\"!tick.showTicks\">\n <div class=\"tick-indicator\"></div>\n <div class=\"tick-label\" aria-hidden=\"true\" [hidden]=\"!tick.showLabels\">{{ tick.label }}</div>\n </div>\n }\n </div>\n}\n", dependencies: [{ kind: "directive", type: i1.FocusIndicatorDirective, selector: "[uxFocusIndicator]", inputs: ["checkChildren", "mouseFocusIndicator", "touchFocusIndicator", "keyboardFocusIndicator", "programmaticFocusIndicator"], outputs: ["indicator"], exportAs: ["ux-focus-indicator"] }, { kind: "directive", type: i2.DragDirective, selector: "[uxDrag]", inputs: ["clone", "group", "model", "draggable"], outputs: ["onDragStart", "onDrag", "onDragScroll", "onDragEnd", "onDrop", "onDropEnter", "onDropLeave"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.12", ngImport: i0, type: SliderComponent, decorators: [{ type: Component, args: [{ selector: 'ux-slider', changeDetection: ChangeDetectionStrategy.OnPush, host: { '[class.disabled]': 'disabled', }, template: "<div class=\"track\"\n #track\n [class.narrow]=\"_options.track.height === sliderSize.Narrow\"\n [class.wide]=\"_options.track.height === sliderSize.Wide\"\n [class.range]=\"_options.type === sliderType.Range\">\n\n <!-- Section Beneath Lower Thumb -->\n <div class=\"track-section track-lower\" [style.flex-grow]=\"tracks.lower.size\" [style.background]=\"this.disabled ? null : tracks.lower.color\"></div>\n\n <!-- Lower Thumb Button / Line -->\n <div class=\"thumb lower\"\n uxDrag\n uxFocusIndicator\n role=\"slider\"\n [tabindex]=\"disabled ? -1 : 0\"\n #lowerthumb\n [attr.aria-label]=\"_options.type === sliderType.Range ? _options.handles.aria.lowerThumb :\n _options.handles.aria.thumb\"\n [attr.aria-valuemin]=\"_options?.track?.min\"\n [attr.aria-valuemax]=\"_options.type === sliderType.Range ? getThumbValue(sliderThumb.Upper) :\n _options?.track?.max\"\n [attr.aria-valuenow]=\"getThumbValue(sliderThumb.Lower)\"\n [attr.aria-valuetext]=\"getAriaValueText(sliderThumb.Lower)\"\n [style.left.%]=\"thumbs.lower.position\"\n [class.active]=\"thumbs.lower.drag\"\n [style.z-index]=\"thumbs.lower.order\"\n [class.button]=\"_options.handles.style === sliderStyle.Button\"\n [class.line]=\"_options.handles.style === sliderStyle.Line\"\n [class.narrow]=\"_options.track.height === sliderSize.Narrow\"\n [class.wide]=\"_options.track.height === sliderSize.Wide\"\n (onDragStart)=\"thumbEvent(sliderThumb.Lower, sliderThumbEvent.DragStart); lowerthumb.focus()\"\n (onDrag)=\"updateThumbPosition($event, sliderThumb.Lower)\"\n (onDragEnd)=\"thumbEvent(sliderThumb.Lower, sliderThumbEvent.DragEnd)\"\n (mouseenter)=\"thumbEvent(sliderThumb.Lower, sliderThumbEvent.MouseOver)\"\n (mouseleave)=\"thumbEvent(sliderThumb.Lower, sliderThumbEvent.MouseLeave)\"\n (focus)=\"thumbEvent(sliderThumb.Lower, sliderThumbEvent.MouseOver)\"\n (blur)=\"thumbEvent(sliderThumb.Lower, sliderThumbEvent.MouseLeave)\"\n (keydown.ArrowLeft)=\"snapToNearestTick(sliderThumb.Lower, sliderSnap.All, false); $event.preventDefault()\"\n (keydown.ArrowRight)=\"snapToNearestTick(sliderThumb.Lower, sliderSnap.All, true); $event.preventDefault()\"\n (keydown.ArrowUp)=\"snapToNearestTick(sliderThumb.Lower, sliderSnap.All, false); $event.preventDefault()\"\n (keydown.ArrowDown)=\"snapToNearestTick(sliderThumb.Lower, sliderSnap.All, true); $event.preventDefault()\"\n (keydown.PageDown)=\"snapToNearestTick(sliderThumb.Lower, sliderSnap.Major, false); $event.preventDefault()\"\n (keydown.PageUp)=\"snapToNearestTick(sliderThumb.Lower, sliderSnap.Major, true); $event.preventDefault()\"\n (keydown.Home)=\"snapToEnd(sliderThumb.Lower, false); $event.preventDefault()\"\n (keydown.End)=\"snapToEnd(sliderThumb.Lower, true); $event.preventDefault()\">\n\n <!-- Lower Thumb Callout -->\n <div class=\"tooltip top tooltip-lower\" #lowerTooltip\n [class.tooltip-dynamic]=\"_options.handles.callout.trigger === sliderCalloutTrigger.Dynamic &&\n thumbs.lower.drag === false\"\n [style.opacity]=\"tooltips.lower.visible ? 1 : 0\"\n [style.left.px]=\"tooltips.lower.position\">\n\n <div class=\"tooltip-arrow\" [style.border-top-color]=\"_options.handles.callout.background\"></div>\n\n <div class=\"tooltip-inner\"\n [style.background-color]=\"_options.handles.callout.background\"\n [style.color]=\"_options.handles.callout.color\">\n {{ tooltips.lower.label }}\n </div>\n </div>\n\n </div>\n\n <!-- Section of Track Between Lower and Upper Thumbs -->\n @if (_options.type === sliderType.Range) {\n <div class=\"track-section track-range\"\n [style.flex-grow]=\"tracks.middle.size\" [style.background]=\"this.disabled ? null : tracks.middle.color\">\n </div>\n }\n\n <!-- Upper Thumb Button / Line -->\n <div class=\"thumb upper\"\n uxDrag\n uxFocusIndicator\n role=\"slider\"\n [tabindex]=\"disabled ? -1 : 0\"\n #upperthumb\n [attr.aria-label]=\"_options.handles.aria.upperThumb\"\n [attr.aria-valuemin]=\"getThumbValue(sliderThumb.Lower) || _options?.track?.min\"\n [attr.aria-valuemax]=\"_options?.track?.max\"\n [attr.aria-valuenow]=\"getThumbValue(sliderThumb.Upper)\"\n [attr.aria-valuetext]=\"getAriaValueText(sliderThumb.Upper)\"\n [hidden]=\"_options.type !== sliderType.Range\"\n [class.active]=\"thumbs.upper.drag\"\n [style.left.%]=\"thumbs.upper.position\"\n [style.z-index]=\"thumbs.upper.order\"\n [class.button]=\"_options.handles.style === sliderStyle.Button\"\n [class.line]=\"_options.handles.style === sliderStyle.Line\"\n [class.narrow]=\"_options.track.height === sliderSize.Narrow\"\n [class.wide]=\"_options.track.height === sliderSize.Wide\"\n (onDragStart)=\"thumbEvent(sliderThumb.Upper, sliderThumbEvent.DragStart); upperthumb.focus()\"\n (onDrag)=\"updateThumbPosition($event, sliderThumb.Upper)\"\n (onDragEnd)=\"thumbEvent(sliderThumb.Upper, sliderThumbEvent.DragEnd)\"\n (mouseenter)=\"thumbEvent(sliderThumb.Upper, sliderThumbEvent.MouseOver)\"\n (mouseleave)=\"thumbEvent(sliderThumb.Upper, sliderThumbEvent.MouseLeave)\"\n (focus)=\"thumbEvent(sliderThumb.Upper, sliderThumbEvent.MouseOver)\"\n (blur)=\"thumbEvent(sliderThumb.Upper, sliderThumbEvent.MouseLeave)\"\n (keydown.ArrowLeft)=\"snapToNearestTick(sliderThumb.Upper, sliderSnap.All, false); $event.preventDefault()\"\n (keydown.ArrowRight)=\"snapToNearestTick(sliderThumb.Upper, sliderSnap.All, true); $event.preventDefault()\"\n (keydown.ArrowUp)=\"snapToNearestTick(sliderThumb.Upper, sliderSnap.All, false); $event.preventDefault()\"\n (keydown.ArrowDown)=\"snapToNearestTick(sliderThumb.Upper, sliderSnap.All, true); $event.preventDefault()\"\n (keydown.PageDown)=\"snapToNearestTick(sliderThumb.Upper, sliderSnap.Major, false); $event.preventDefault()\"\n (keydown.PageUp)=\"snapToNearestTick(sliderThumb.Upper, sliderSnap.Major, true); $event.preventDefault()\"\n (keydown.Home)=\"snapToEnd(sliderThumb.Upper, false); $event.preventDefault()\"\n (keydown.End)=\"snapToEnd(sliderThumb.Upper, true); $event.preventDefault()\">\n\n <!-- Upper Thumb Callout -->\n <div class=\"tooltip top tooltip-upper\" #upperTooltip\n [class.tooltip-dynamic]=\"_options.handles.callout.trigger === sliderCalloutTrigger.Dynamic &&\n thumbs.upper.drag === false\"\n [style.opacity]=\"tooltips.upper.visible ? 1 : 0\"\n [style.left.px]=\"tooltips.upper.position\">\n\n <div class=\"tooltip-arrow\" [style.border-top-color]=\"_options.handles.callout.background\"></div>\n\n @if (_options.type === sliderType.Range) {\n <div class=\"tooltip-inner\"\n [style.background-color]=\"_options.handles.callout.background\"\n [style.color]=\"_options.handles.callout.color\">\n {{ tooltips.upper.label }}\n </div>\n }\n </div>\n </div>\n\n <!-- Section of Track Abover Upper Thumb -->\n <div class=\"track-section track-higher\" [style.flex-grow]=\"tracks.upper.size\" [style.background]=\"this.disabled ? null : tracks.upper.color\"></div>\n\n</div>\n\n<!-- Chart Ticks and Tick Labels -->\n@if ((_options.track.ticks.major.show || _options.track.ticks.minor.show) &&\n _options.handles.callout.trigger !== sliderCalloutTrigger.Dynamic) {\n <div class=\"tick-container\"\n role=\"presentation\"\n [class.show-labels]=\"_options.track.ticks.major.labels || _options.track.ticks.minor.labels\">\n @for (tick of ticks; track tick) {\n <div class=\"tick\"\n [class.major]=\"tick.type === sliderTickType.Major\"\n [class.minor]=\"tick.type === sliderTickType.Minor\"\n [style.left.%]=\"tick.position\"\n [hidden]=\"!tick.showTicks\">\n <div class=\"tick-indicator\"></div>\n <div class=\"tick-label\" aria-hidden=\"true\" [hidden]=\"!tick.showLabels\">{{ tick.label }}</div>\n </div>\n }\n </div>\n}\n" }] }], ctorParameters: () => [], propDecorators: { value: [{ type: Input }], options: [{ type: Input }], disabled: [{ type: Input }], valueChange: [{ type: Output }], lowerTooltip: [{ type: ViewChild, args: ['lowerTooltip', { static: true }] }], upperTooltip: [{ type: ViewChild, args: ['upperTooltip', { static: true }] }], track: [{ type: ViewChild, args: ['track', { static: true }] }] } }); export var SliderType; (function (SliderType) { SliderType[SliderType["Value"] = 0] = "Value"; SliderType[SliderType["Range"] = 1] = "Range"; })(SliderType || (SliderType = {})); export var SliderStyle; (function (SliderStyle) { SliderStyle[SliderStyle["Button"] = 0] = "Button"; SliderStyle[SliderStyle["Line"] = 1] = "Line"; })(SliderStyle || (SliderStyle = {})); export var SliderSize; (function (SliderSize) { SliderSize[SliderSize["Narrow"] = 0] = "Narrow"; SliderSize[SliderSize["Wide"] = 1] = "Wide"; })(SliderSize || (SliderSize = {})); export var SliderCalloutTrigger; (function (SliderCalloutTrigger) { SliderCalloutTrigger[SliderCalloutTrigger["None"] = 0] = "None"; SliderCalloutTrigger[SliderCalloutTrigger["Hover"] = 1] = "Hover"; SliderCalloutTrigger[SliderCalloutTrigger["Drag"] = 2] = "Drag"; SliderCalloutTrigger[SliderCalloutTrigger["Persistent"] = 3] = "Persistent"; SliderCalloutTrigger[SliderCalloutTrigger["Dynamic"] = 4] = "Dynamic"; })(SliderCalloutTrigger || (SliderCalloutTrigger = {})); export var SliderSnap; (function (SliderSnap) { SliderSnap[SliderSnap["None"] = 0] = "None"; SliderSnap[SliderSnap["Minor"] = 1] = "Minor"; SliderSnap[SliderSnap["Major"] = 2] = "Major"; SliderSnap[SliderSnap["All"] = 3] = "All"; })(SliderSnap || (SliderSnap = {})); export var SliderTickType; (function (SliderTickType) { SliderTickType[SliderTickType["Minor"] = 0] = "Minor"; SliderTickType[SliderTickType["Major"] = 1] = "Major"; })(SliderTickType || (SliderTickType = {})); export var SliderThumbEvent; (function (SliderThumbEvent) { SliderThumbEvent[SliderThumbEvent["None"] = 0] = "None"; SliderThumbEvent[SliderThumbEvent["MouseOver"] = 1] = "MouseOver"; SliderThumbEvent[SliderThumbEvent["MouseLeave"] = 2] = "MouseLeave"; SliderThumbEvent[SliderThumbEvent["DragStart"] = 3] = "DragStart"; SliderThumbEvent[SliderThumbEvent["DragEnd"] = 4] = "DragEnd"; })(SliderThumbEvent || (SliderThumbEvent = {})); export var SliderThumb; (function (SliderThumb) { SliderThumb[SliderThumb["Lower"] = 0] = "Lower"; SliderThumb[SliderThumb["Upper"] = 1] = "Upper"; })(SliderThumb || (SliderThumb = {})); //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic2xpZGVyLmNvbXBvbmVudC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uLy4uL3NyYy9jb21wb25lbnRzL3NsaWRlci9zbGlkZXIuY29tcG9uZW50LnRzIiwiLi4vLi4vLi4vLi4vLi4vc3JjL2NvbXBvbmVudHMvc2xpZGVyL3NsaWRlci5jb21wb25lbnQuaHRtbCJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQUUscUJBQXFCLEVBQUUsTUFBTSx1QkFBdUIsQ0FBQztBQUM5RCxPQUFPLEVBQWlCLHVCQUF1QixFQUFFLGlCQUFpQixFQUFFLFNBQVMsRUFBdUIsWUFBWSxFQUFFLE1BQU0sRUFBRSxLQUFLLEVBQVUsTUFBTSxFQUFFLFNBQVMsRUFBRSxNQUFNLGVBQWUsQ0FBQztBQUNsTCxPQUFPLEVBQUUsWUFBWSxFQUFFLE1BQU0sNEJBQTRCLENBQUM7Ozs7QUFVMUQsTUFBTSxPQUFPLGVBQWU7SUFReEIsK0VBQStFO0lBQy9FLElBQWEsT0FBTyxDQUFDLE9BQXNCO1FBQ3ZDLElBQUksQ0FBQyxRQUFRLEdBQUcsT0FBTyxDQUFDO1FBQ3hCLElBQUksQ0FBQyxhQUFhLEVBQUUsQ0FBQztJQUN6QixDQUFDO0lBRUQsc0NBQXNDO0lBQ3RDLElBQWEsUUFBUSxDQUFDLFFBQWlCO1FBQ25DLElBQUksQ0FBQyxTQUFTLEdBQUcscUJBQXFCLENBQUMsUUFBUSxDQUFDLENBQUM7SUFDckQsQ0FBQztJQUVELElBQUksUUFBUTtRQUNSLE9BQU8sSUFBSSxDQUFDLFNBQVMsQ0FBQztJQUMxQixDQUFDO0lBRUQsSUFBSSxPQUFPO1FBQ1AsT0FBTyxJQUFJLENBQUMsUUFBUSxDQUFDO0lBQ3pCLENBQUM7SUEwRUQ7UUFsR1MsaUJBQVksR0FBRyxNQUFNLENBQUMsWUFBWSxDQUFDLENBQUM7UUFFNUIsdUJBQWtCLEdBQUcsTUFBTSxDQUFDLGlCQUFpQixDQUFDLENBQUM7UUFFaEUsdUZBQXVGO1FBQzlFLFVBQUssR0FBeUIsQ0FB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