@ux-aspects/ux-aspects
Version:
Open source user interface framework for building modern, responsive, mobile big data applications
692 lines • 129 kB
JavaScript
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,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic2xpZGVyLmNvbXBvbmVudC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uLy4uL3NyYy9jb21wb25lbnRzL3NsaWRlci9zbGlkZXIuY29tcG9uZW50LnRzIiwiLi4vLi4vLi4vLi4vLi4vc3JjL2NvbXBvbmVudHMvc2xpZGVyL3NsaWRlci5jb21wb25lbnQuaHRtbCJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQUUscUJBQXFCLEVBQUUsTUFBTSx1QkFBdUIsQ0FBQztBQUM5RCxPQUFPLEVBQWlCLHVCQUF1QixFQUFFLGlCQUFpQixFQUFFLFNBQVMsRUFBdUIsWUFBWSxFQUFFLE1BQU0sRUFBRSxLQUFLLEVBQVUsTUFBTSxFQUFFLFNBQVMsRUFBRSxNQUFNLGVBQWUsQ0FBQztBQUNsTCxPQUFPLEVBQUUsWUFBWSxFQUFFLE1BQU0sNEJBQTRCLENBQUM7Ozs7QUFVMUQsTUFBTSxPQUFPLGVBQWU7SUFReEIsK0VBQStFO0lBQy9FLElBQWEsT0FBTyxDQUFDLE9BQXNCO1FBQ3ZDLElBQUksQ0FBQyxRQUFRLEdBQUcsT0FBTyxDQUFDO1FBQ3hCLElBQUksQ0FBQyxhQUFhLEVBQUUsQ0FBQztJQUN6QixDQUFDO0lBRUQsc0NBQXNDO0lBQ3RDLElBQWEsUUFBUSxDQUFDLFFBQWlCO1FBQ25DLElBQUksQ0FBQyxTQUFTLEdBQUcscUJBQXFCLENBQUMsUUFBUSxDQUFDLENBQUM7SUFDckQsQ0FBQztJQUVELElBQUksUUFBUTtRQUNSLE9BQU8sSUFBSSxDQUFDLFNBQVMsQ0FBQztJQUMxQixDQUFDO0lBRUQsSUFBSSxPQUFPO1FBQ1AsT0FBTyxJQUFJLENBQUMsUUFBUSxDQUFDO0lBQ3pCLENBQUM7SUEwRUQ7UUFsR1MsaUJBQVksR0FBRyxNQUFNLENBQUMsWUFBWSxDQUFDLENBQUM7UUFFNUIsdUJBQWtCLEdBQUcsTUFBTSxDQUFDLGlCQUFpQixDQUFDLENBQUM7UUFFaEUsdUZBQXVGO1FBQzlFLFVBQUssR0FBeUIsQ0FBQyxDQUFDO1FBcUJ6QyxzQ0FBc0M7UUFDNUIsZ0JBQVcsR0FBdUMsSUFBSSxZQUFZLEVBQXdCLENBQUM7UUFRN0YsY0FBUyxHQUFZLEtBQUssQ0FBQztRQUluQywrQkFBK0I7UUFDL0IsZUFBVSxHQUFHLFVBQVUsQ0FBQztRQUN4QixnQkFBVyxHQUFHLFdBQVcsQ0FBQztRQUMxQixlQUFVLEdBQUcsVUFBVSxDQUFDO1FBQ3hCLGVBQVUsR0FBRyxVQUFVLENBQUM7UUFDeEIsZ0JBQVcsR0FBRyxXQUFXLENBQUM7UUFDMUIsbUJBQWMsR0FBRyxjQUFjLENBQUM7UUFDaEMscUJBQWdCLEdBQUcsZ0JBQWdCLENBQUM7UUFDcEMseUJBQW9CLEdBQUcsb0JBQW9CLENBQUM7UUFFNUMsV0FBTSxHQUFHO1lBQ0wsS0FBSyxFQUFFO2dCQUNILElBQUksRUFBRSxDQUFDO2dCQUNQLEtBQUssRUFBRSxFQUFFO2FBQ1o7WUFDRCxNQUFNLEVBQUU7Z0JBQ0osSUFBSSxFQUFFLENBQUM7Z0JBQ1AsS0FBSyxFQUFFLEVBQUU7YUFDWjtZQUNELEtBQUssRUFBRTtnQkFDSCxJQUFJLEVBQUUsQ0FBQztnQkFDUCxLQUFLLEVBQUUsRUFBRTthQUNaO1NBQ0osQ0FBQztRQUVGLGFBQVEsR0FBRztZQUNQLEtBQUssRUFBRTtnQkFDSCxPQUFPLEVBQUUsS0FBSztnQkFDZCxRQUFRLEVBQUUsQ0FBQztnQkFDWCxLQUFLLEVBQUUsRUFBRTthQUNaO1lBQ0QsS0FBSyxFQUFFO2dCQUNILE9BQU8sRUFBRSxLQUFLO2dCQUNkLFFBQVEsRUFBRSxDQUFDO2dCQUNYLEtBQUssRUFBRSxFQUFFO2FBQ1o7U0FDSixDQUFDO1FBRUYsV0FBTSxHQUFHO1lBQ0wsS0FBSyxFQUFFO2dCQUNILEtBQUssRUFBRSxLQUFLO2dCQUNaLElBQUksRUFBRSxLQUFLO2dCQUNYLFFBQVEsRUFBRSxDQUFDO2dCQUNYLEtBQUssRUFBRSxHQUFHO2dCQUNWLEtBQUssRUFBRSxJQUFjO2FBQ3hCO1lBQ0QsS0FBSyxFQUFFO2dCQUNILEtBQUssRUFBRSxLQUFLO2dCQUNaLElBQUksRUFBRSxLQUFLO2dCQUNYLFFBQVEsRUFBRSxDQUFDO2dCQUNYLEtBQUssRUFBRSxHQUFHO2dCQUNWLEtBQUssRUFBRSxJQUFjO2FBQ3hCO1NBQ0osQ0FBQztRQUVGLGlDQUFpQztRQUNqQyxVQUFLLEdBQWlCLEVBQUUsQ0FBQztRQUlyQix3QkFBd0I7UUFDeEIsSUFBSSxDQUFDLGNBQWMsR0FBRztZQUNsQixJQUFJLEVBQUUsVUFBVSxDQUFDLEtBQUs7WUFDdEIsT0FBTyxFQUFFO2dCQUNMLEtBQUssRUFBRSxXQUFXLENBQUMsTUFBTTtnQkFDekIsT0FBTyxFQUFFO29CQUNMLE9BQU8sRUFBRSxvQkFBb0IsQ0FBQyxJQUFJO29CQUNsQyxVQUFVLEVBQUUsSUFBSSxDQUFDLFlBQVksQ0FBQyxRQUFRLENBQUMsT0FBTyxDQUFDLENBQUMsS0FBSyxFQUFFO29CQUN2RCxLQUFLLEVBQUUsTUFBTTtvQkFDYixTQUFTLEVBQUUsQ0FBQyxLQUFhLEVBQW1CLEVBQUUsQ0FBQyxLQUFLO2lCQUN2RDtnQkFDRCxRQUFRLEVBQUU7b0JBQ04sS0FBSyxFQUFFLENBQUM7b0JBQ1IsS0FBSyxFQUFFLENBQUM7aUJBQ1g7Z0JBQ0QsSUFBSSxFQUFFO29CQUNGLEtBQUssRUFBRSxjQUFjO29CQUNyQixVQUFVLEVBQUUsb0JBQW9CO29CQUNoQyxVQUFVLEVBQUUsb0JBQW9CO2lCQUNuQzthQUNKO1lBQ0QsS0FBSyxFQUFFO2dCQUNILE1BQU0sRUFBRSxVQUFVLENBQUMsSUFBSTtnQkFDdkIsR0FBRyxFQUFFLENBQUM7Z0JBQ04sR0FBRyxFQUFFLEdBQUc7Z0JBQ1IsS0FBSyxFQUFFO29CQUNILElBQUksRUFBRSxVQUFVLENBQUMsSUFBSTtvQkFDckIsS0FBSyxFQUFFO3dCQUNILElBQUksRUFBRSxJQUFJO3dCQUNWLEtBQUssRUFBRSxFQUFFO3dCQUNULE1BQU0sRUFBRSxJQUFJO3dCQUNaLFNBQVMsRUFBRSxDQUFDLEtBQWEsRUFBbUIsRUFBRSxDQUFDLEtBQUs7cUJBQ3ZEO29CQUNELEtBQUssRUFBRTt3QkFDSCxJQUFJLEVBQUUsSUFBSTt3QkFDVixLQUFLLEVBQUUsQ0FBQzt3QkFDUixNQUFNLEVBQUUsS0FBSzt3QkFDYixTQUFTLEVBQUUsQ0FBQyxLQUFhLEVBQW1CLEVBQUUsQ0FBQyxLQUFLO3FCQUN2RDtpQkFDSjtnQkFDRCxNQUFNLEVBQUUsRUFBRTthQUNiO1NBQ0osQ0FBQztJQUNOLENBQUM7SUFFRCxRQUFRO1FBRUosSUFBSSxDQUFDLFlBQVksRUFBRSxDQUFDO1FBRXBCLElBQUksQ0FBQyxhQUFhLENBQUMsV0FBVyxDQUFDLEtBQUssRUFBRSxLQUFLLEVBQUUsS0FBSyxDQUFDLENBQUM7UUFDcEQsSUFBSSxDQUFDLGFBQWEsQ0FBQyxXQUFXLENBQUMsS0FBSyxFQUFFLEtBQUssRUFBRSxLQUFLLENBQUMsQ0FBQztRQUVwRCx5QkFBeUI7UUFDekIsSUFBSSxDQUFDLFdBQVcsQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLEtBQUssQ0FBQyxJQUFJLENBQUMsS0FBSyxDQUFDLENBQUMsQ0FBQztJQUNs