@esri/calcite-components
Version:
Web Components for Esri's Calcite Design System.
1,077 lines (1,076 loc) • 43.5 kB
JavaScript
import { Component, Element, Prop, Host, Event, Listen, Method, h, State, Watch } from "@stencil/core";
import { guid } from "../../utils/guid";
import { getKey } from "../../utils/key";
import { hasLabel } from "../../utils/dom";
export class CalciteSlider {
constructor() {
/** Disable and gray out the slider */
this.disabled = false;
/** Minimum selectable value */
this.min = 0;
/** Maximum selectable value */
this.max = 100;
/** Currently selected number (if single select) */
this.value = null;
/** When true, enables snap selection along the step interval */
this.snap = false;
/** Interval to move on up/down keys */
this.step = 1;
/** Indicates if a histogram is present */
this.hasHistogram = false;
//--------------------------------------------------------------------------
//
// Private State/Props
//
//--------------------------------------------------------------------------
/** @internal */
this.guid = `calcite-slider-${guid()}`;
/** @internal */
this.isRange = false;
/** @internal */
this.tickValues = [];
/** @internal */
this.activeProp = "value";
/** @internal */
this.minMaxValueRange = null;
/** @internal */
this.minValueDragRange = null;
/** @internal */
this.maxValueDragRange = null;
}
histogramWatcher(newHistogram) {
this.hasHistogram = newHistogram ? true : false;
}
//--------------------------------------------------------------------------
//
// Lifecycle
//
//--------------------------------------------------------------------------
componentWillLoad() {
this.isRange = !!(this.maxValue || this.maxValue === 0);
this.tickValues = this.generateTickValues();
this.value = this.bound(this.value);
if (this.snap) {
this.value = this.getClosestStep(this.value);
}
if (this.histogram) {
this.hasHistogram = true;
}
this.calciteSliderUpdate.emit();
}
componentDidRender() {
if (this.labelHandles) {
this.adjustHostObscuredHandleLabel("value");
if (this.isRange) {
this.adjustHostObscuredHandleLabel("minValue");
if (!(this.precise && this.isRange && !this.hasHistogram)) {
this.hyphenateCollidingRangeHandleLabels();
}
}
}
this.hideObscuredBoundingTickLabels();
}
render() {
const id = this.el.id || this.guid;
const min = this.minValue || this.min;
const max = this.maxValue || this.value;
const maxProp = this.isRange ? "maxValue" : "value";
const value = this[maxProp];
const left = `${this.getUnitInterval(min) * 100}%`;
const right = `${100 - this.getUnitInterval(max) * 100}%`;
const handle = (h("button", { "aria-label": this.isRange ? this.maxLabel : this.minLabel, "aria-orientation": "horizontal", "aria-valuemax": this.max, "aria-valuemin": this.min, "aria-valuenow": value, class: {
thumb: true,
"thumb--value": true,
"thumb--active": this.lastDragProp !== "minMaxValue" && this.dragProp === maxProp
}, disabled: this.disabled, onBlur: () => (this.activeProp = null), onFocus: () => (this.activeProp = maxProp), onMouseDown: () => this.dragStart(maxProp), onTouchStart: (e) => this.dragStart(maxProp, e), ref: (el) => (this.maxHandle = el), role: "slider", style: { right } },
h("div", { class: "handle" })));
const labeledHandle = (h("button", { "aria-label": this.isRange ? this.maxLabel : this.minLabel, "aria-orientation": "horizontal", "aria-valuemax": this.max, "aria-valuemin": this.min, "aria-valuenow": value, class: {
thumb: true,
"thumb--value": true,
"thumb--active": this.lastDragProp !== "minMaxValue" && this.dragProp === maxProp
}, disabled: this.disabled, onBlur: () => (this.activeProp = null), onFocus: () => (this.activeProp = maxProp), onMouseDown: () => this.dragStart(maxProp), onTouchStart: (e) => this.dragStart(maxProp, e), ref: (el) => (this.maxHandle = el), role: "slider", style: { right } },
h("span", { "aria-hidden": "true", class: "handle__label handle__label--value" }, value ? value.toLocaleString() : value),
h("span", { "aria-hidden": "true", class: "handle__label handle__label--value static" }, value ? value.toLocaleString() : value),
h("span", { "aria-hidden": "true", class: "handle__label handle__label--value transformed" }, value ? value.toLocaleString() : value),
h("div", { class: "handle" })));
const histogramLabeledHandle = (h("button", { "aria-label": this.isRange ? this.maxLabel : this.minLabel, "aria-orientation": "horizontal", "aria-valuemax": this.max, "aria-valuemin": this.min, "aria-valuenow": value, class: {
thumb: true,
"thumb--value": true,
"thumb--active": this.lastDragProp !== "minMaxValue" && this.dragProp === maxProp
}, disabled: this.disabled, onBlur: () => (this.activeProp = null), onFocus: () => (this.activeProp = maxProp), onMouseDown: () => this.dragStart(maxProp), onTouchStart: (e) => this.dragStart(maxProp, e), ref: (el) => (this.maxHandle = el), role: "slider", style: { right } },
h("div", { class: "handle" }),
h("span", { "aria-hidden": "true", class: "handle__label handle__label--value" }, value ? value.toLocaleString() : value),
h("span", { "aria-hidden": "true", class: "handle__label handle__label--value static" }, value ? value.toLocaleString() : value),
h("span", { "aria-hidden": "true", class: "handle__label handle__label--value transformed" }, value ? value.toLocaleString() : value)));
const preciseHandle = (h("button", { "aria-label": this.isRange ? this.maxLabel : this.minLabel, "aria-orientation": "horizontal", "aria-valuemax": this.max, "aria-valuemin": this.min, "aria-valuenow": value, class: {
thumb: true,
"thumb--value": true,
"thumb--active": this.lastDragProp !== "minMaxValue" && this.dragProp === maxProp,
"thumb--precise": true
}, disabled: this.disabled, onBlur: () => (this.activeProp = null), onFocus: () => (this.activeProp = maxProp), onMouseDown: () => this.dragStart(maxProp), onTouchStart: (e) => this.dragStart(maxProp, e), ref: (el) => (this.maxHandle = el), role: "slider", style: { right } },
h("div", { class: "handle" }),
h("div", { class: "handle-extension" })));
const histogramPreciseHandle = (h("button", { "aria-label": this.isRange ? this.maxLabel : this.minLabel, "aria-orientation": "horizontal", "aria-valuemax": this.max, "aria-valuemin": this.min, "aria-valuenow": value, class: {
thumb: true,
"thumb--value": true,
"thumb--active": this.lastDragProp !== "minMaxValue" && this.dragProp === maxProp,
"thumb--precise": true
}, disabled: this.disabled, onBlur: () => (this.activeProp = null), onFocus: () => (this.activeProp = maxProp), onMouseDown: () => this.dragStart(maxProp), onTouchStart: (e) => this.dragStart(maxProp, e), ref: (el) => (this.maxHandle = el), role: "slider", style: { right } },
h("div", { class: "handle-extension" }),
h("div", { class: "handle" })));
const labeledPreciseHandle = (h("button", { "aria-label": this.isRange ? this.maxLabel : this.minLabel, "aria-orientation": "horizontal", "aria-valuemax": this.max, "aria-valuemin": this.min, "aria-valuenow": value, class: {
thumb: true,
"thumb--value": true,
"thumb--active": this.lastDragProp !== "minMaxValue" && this.dragProp === maxProp,
"thumb--precise": true
}, disabled: this.disabled, onBlur: () => (this.activeProp = null), onFocus: () => (this.activeProp = maxProp), onMouseDown: () => this.dragStart(maxProp), onTouchStart: (e) => this.dragStart(maxProp, e), ref: (el) => (this.maxHandle = el), role: "slider", style: { right } },
h("span", { "aria-hidden": "true", class: "handle__label handle__label--value" }, value ? value.toLocaleString() : value),
h("span", { "aria-hidden": "true", class: "handle__label handle__label--value static" }, value ? value.toLocaleString() : value),
h("span", { "aria-hidden": "true", class: "handle__label handle__label--value transformed" }, value ? value.toLocaleString() : value),
h("div", { class: "handle" }),
h("div", { class: "handle-extension" })));
const histogramLabeledPreciseHandle = (h("button", { "aria-label": this.isRange ? this.maxLabel : this.minLabel, "aria-orientation": "horizontal", "aria-valuemax": this.max, "aria-valuemin": this.min, "aria-valuenow": value, class: {
thumb: true,
"thumb--value": true,
"thumb--active": this.lastDragProp !== "minMaxValue" && this.dragProp === maxProp,
"thumb--precise": true
}, disabled: this.disabled, onBlur: () => (this.activeProp = null), onFocus: () => (this.activeProp = maxProp), onMouseDown: () => this.dragStart(maxProp), onTouchStart: (e) => this.dragStart(maxProp, e), ref: (el) => (this.maxHandle = el), role: "slider", style: { right } },
h("div", { class: "handle-extension" }),
h("div", { class: "handle" }),
h("span", { "aria-hidden": "true", class: "handle__label handle__label--value" }, value ? value.toLocaleString() : value),
h("span", { "aria-hidden": "true", class: "handle__label handle__label--value static" }, value ? value.toLocaleString() : value),
h("span", { "aria-hidden": "true", class: "handle__label handle__label--value transformed" }, value ? value.toLocaleString() : value)));
const minHandle = (h("button", { "aria-label": this.minLabel, "aria-orientation": "horizontal", "aria-valuemax": this.max, "aria-valuemin": this.min, "aria-valuenow": this.minValue, class: {
thumb: true,
"thumb--minValue": true,
"thumb--active": this.dragProp === "minValue"
}, disabled: this.disabled, onBlur: () => (this.activeProp = null), onFocus: () => (this.activeProp = "minValue"), onMouseDown: () => this.dragStart("minValue"), onTouchStart: (e) => this.dragStart("minValue", e), ref: (el) => (this.minHandle = el), role: "slider", style: { left } },
h("div", { class: "handle" })));
const minLabeledHandle = (h("button", { "aria-label": this.minLabel, "aria-orientation": "horizontal", "aria-valuemax": this.max, "aria-valuemin": this.min, "aria-valuenow": this.minValue, class: {
thumb: true,
"thumb--minValue": true,
"thumb--active": this.dragProp === "minValue"
}, disabled: this.disabled, onBlur: () => (this.activeProp = null), onFocus: () => (this.activeProp = "minValue"), onMouseDown: () => this.dragStart("minValue"), onTouchStart: (e) => this.dragStart("minValue", e), ref: (el) => (this.minHandle = el), role: "slider", style: { left } },
h("span", { "aria-hidden": "true", class: "handle__label handle__label--minValue" }, this.minValue && this.minValue.toLocaleString()),
h("span", { "aria-hidden": "true", class: "handle__label handle__label--minValue static" }, this.minValue && this.minValue.toLocaleString()),
h("span", { "aria-hidden": "true", class: "handle__label handle__label--minValue transformed" }, this.minValue && this.minValue.toLocaleString()),
h("div", { class: "handle" })));
const minHistogramLabeledHandle = (h("button", { "aria-label": this.minLabel, "aria-orientation": "horizontal", "aria-valuemax": this.max, "aria-valuemin": this.min, "aria-valuenow": this.minValue, class: {
thumb: true,
"thumb--minValue": true,
"thumb--active": this.dragProp === "minValue"
}, disabled: this.disabled, onBlur: () => (this.activeProp = null), onFocus: () => (this.activeProp = "minValue"), onMouseDown: () => this.dragStart("minValue"), onTouchStart: (e) => this.dragStart("minValue", e), ref: (el) => (this.minHandle = el), role: "slider", style: { left } },
h("div", { class: "handle" }),
h("span", { "aria-hidden": "true", class: "handle__label handle__label--minValue" }, this.minValue && this.minValue.toLocaleString()),
h("span", { "aria-hidden": "true", class: "handle__label handle__label--minValue static" }, this.minValue && this.minValue.toLocaleString()),
h("span", { "aria-hidden": "true", class: "handle__label handle__label--minValue transformed" }, this.minValue && this.minValue.toLocaleString())));
const minPreciseHandle = (h("button", { "aria-label": this.minLabel, "aria-orientation": "horizontal", "aria-valuemax": this.max, "aria-valuemin": this.min, "aria-valuenow": this.minValue, class: {
thumb: true,
"thumb--minValue": true,
"thumb--active": this.dragProp === "minValue",
"thumb--precise": true
}, disabled: this.disabled, onBlur: () => (this.activeProp = null), onFocus: () => (this.activeProp = "minValue"), onMouseDown: () => this.dragStart("minValue"), onTouchStart: (e) => this.dragStart("minValue", e), ref: (el) => (this.minHandle = el), role: "slider", style: { left } },
h("div", { class: "handle-extension" }),
h("div", { class: "handle" })));
const minLabeledPreciseHandle = (h("button", { "aria-label": this.minLabel, "aria-orientation": "horizontal", "aria-valuemax": this.max, "aria-valuemin": this.min, "aria-valuenow": this.minValue, class: {
thumb: true,
"thumb--minValue": true,
"thumb--active": this.dragProp === "minValue",
"thumb--precise": true
}, disabled: this.disabled, onBlur: () => (this.activeProp = null), onFocus: () => (this.activeProp = "minValue"), onMouseDown: () => this.dragStart("minValue"), onTouchStart: (e) => this.dragStart("minValue", e), ref: (el) => (this.minHandle = el), role: "slider", style: { left } },
h("div", { class: "handle-extension" }),
h("div", { class: "handle" }),
h("span", { "aria-hidden": "true", class: "handle__label handle__label--minValue" }, this.minValue && this.minValue.toLocaleString()),
h("span", { "aria-hidden": "true", class: "handle__label handle__label--minValue static" }, this.minValue && this.minValue.toLocaleString()),
h("span", { "aria-hidden": "true", class: "handle__label handle__label--minValue transformed" }, this.minValue && this.minValue.toLocaleString())));
return (h(Host, { id: id, "is-range": this.isRange },
this.renderGraph(),
h("div", { class: "track" },
h("div", { class: "track__range", onMouseDown: () => this.dragStart("minMaxValue"), onTouchStart: (e) => this.dragStart("minMaxValue", e), style: { left, right } }),
h("div", { class: "ticks" }, this.tickValues.map((tick) => (h("span", { class: {
tick: true,
"tick--active": tick >= min && tick <= max
}, style: {
left: `${this.getUnitInterval(tick) * 100}%`
} }, this.renderTickLabel(tick)))))),
!this.precise && !this.labelHandles && this.isRange && minHandle,
!this.hasHistogram &&
!this.precise &&
this.labelHandles &&
this.isRange &&
minLabeledHandle,
this.precise && !this.labelHandles && this.isRange && minPreciseHandle,
this.precise && this.labelHandles && this.isRange && minLabeledPreciseHandle,
this.hasHistogram &&
!this.precise &&
this.labelHandles &&
this.isRange &&
minHistogramLabeledHandle,
!this.precise && !this.labelHandles && handle,
!this.hasHistogram && !this.precise && this.labelHandles && labeledHandle,
!this.hasHistogram && this.precise && !this.labelHandles && preciseHandle,
this.hasHistogram && this.precise && !this.labelHandles && histogramPreciseHandle,
!this.hasHistogram && this.precise && this.labelHandles && labeledPreciseHandle,
this.hasHistogram && !this.precise && this.labelHandles && histogramLabeledHandle,
this.hasHistogram && this.precise && this.labelHandles && histogramLabeledPreciseHandle));
}
renderGraph() {
return this.histogram ? (h("div", { class: "graph" },
h("calcite-graph", { data: this.histogram, height: 48, highlightMax: this.isRange ? this.maxValue : this.value, highlightMin: this.isRange ? this.minValue : this.min, width: 300 }))) : null;
}
renderTickLabel(tick) {
const isMinTickLabel = tick === this.min;
const isMaxTickLabel = tick === this.max;
const tickLabel = (h("span", { class: {
tick__label: true,
"tick__label--min": isMinTickLabel,
"tick__label--max": isMaxTickLabel
} }, tick.toLocaleString()));
if (this.labelTicks && !this.hasHistogram && !this.isRange) {
return tickLabel;
}
if (this.labelTicks &&
!this.hasHistogram &&
this.isRange &&
!this.precise &&
!this.labelHandles) {
return tickLabel;
}
if (this.labelTicks &&
!this.hasHistogram &&
this.isRange &&
!this.precise &&
this.labelHandles) {
return tickLabel;
}
if (this.labelTicks &&
!this.hasHistogram &&
this.isRange &&
this.precise &&
(isMinTickLabel || isMaxTickLabel)) {
return tickLabel;
}
if (this.labelTicks && this.hasHistogram && !this.precise && !this.labelHandles) {
return tickLabel;
}
if (this.labelTicks &&
this.hasHistogram &&
this.precise &&
!this.labelHandles &&
(isMinTickLabel || isMaxTickLabel)) {
return tickLabel;
}
if (this.labelTicks &&
this.hasHistogram &&
!this.precise &&
this.labelHandles &&
(isMinTickLabel || isMaxTickLabel)) {
return tickLabel;
}
if (this.labelTicks &&
this.hasHistogram &&
this.precise &&
this.labelHandles &&
(isMinTickLabel || isMaxTickLabel)) {
return tickLabel;
}
return null;
}
//--------------------------------------------------------------------------
//
// Event Listeners
//
//--------------------------------------------------------------------------
handleLabelFocus(e) {
if (e.detail.interactedEl !== this.el && hasLabel(e.detail.labelEl, this.el)) {
this.setFocus();
}
}
keyDownHandler(e) {
const value = this[this.activeProp];
switch (getKey(e.key)) {
case "ArrowUp":
case "ArrowRight":
e.preventDefault();
this[this.activeProp] = this.bound(value + this.step, this.activeProp);
this.calciteSliderUpdate.emit();
break;
case "ArrowDown":
case "ArrowLeft":
e.preventDefault();
this[this.activeProp] = this.bound(value - this.step, this.activeProp);
this.calciteSliderUpdate.emit();
break;
case "PageUp":
if (this.pageStep) {
e.preventDefault();
this[this.activeProp] = this.bound(value + this.pageStep, this.activeProp);
this.calciteSliderUpdate.emit();
}
break;
case "PageDown":
if (this.pageStep) {
e.preventDefault();
this[this.activeProp] = this.bound(value - this.pageStep, this.activeProp);
this.calciteSliderUpdate.emit();
}
break;
case "Home":
e.preventDefault();
this[this.activeProp] = this.bound(this.min, this.activeProp);
this.calciteSliderUpdate.emit();
break;
case "End":
e.preventDefault();
this[this.activeProp] = this.bound(this.max, this.activeProp);
this.calciteSliderUpdate.emit();
break;
}
}
clickHandler(e) {
const x = e.clientX || e.pageX;
const num = this.translate(x);
let prop = "value";
if (this.isRange) {
if (this.lastDragProp === "minMaxValue") {
prop = "minMaxValue";
}
else {
const closerToMax = Math.abs(this.maxValue - num) < Math.abs(this.minValue - num);
prop = closerToMax ? "maxValue" : "minValue";
}
}
this[prop] = this.bound(num, prop);
this.calciteSliderUpdate.emit();
switch (prop) {
default:
case "maxValue":
this.maxHandle.focus();
break;
case "minValue":
this.minHandle.focus();
break;
case "minMaxValue":
break;
}
}
//--------------------------------------------------------------------------
//
// Public Methods
//
//--------------------------------------------------------------------------
async setFocus() {
const handle = this.minHandle ? this.minHandle : this.maxHandle;
handle.focus();
}
//--------------------------------------------------------------------------
//
// Private Methods
//
//--------------------------------------------------------------------------
generateTickValues() {
const ticks = [];
let current = this.min;
while (this.ticks && current < this.max + this.ticks) {
ticks.push(current);
current = current + this.ticks;
}
return ticks;
}
dragStart(prop, e) {
if (e) {
e.preventDefault();
}
if (this.dragListener) {
this.dragEnd();
}
this.dragProp = prop;
this.lastDragProp = this.dragProp;
this.activeProp = prop;
this.dragListener = this.dragListener || this.dragUpdate.bind(this);
document.addEventListener("mousemove", this.dragListener);
document.addEventListener("touchmove", this.dragListener, {
capture: false
});
document.addEventListener("mouseup", this.dragEnd.bind(this));
document.addEventListener("touchend", this.dragEnd.bind(this), false);
document.addEventListener("touchcancel", this.dragEnd.bind(this));
}
dragUpdate(e) {
e.preventDefault();
e.stopPropagation();
if (this.dragProp) {
const value = this.translate(e.clientX || e.pageX);
if (this.isRange && this.dragProp === "minMaxValue") {
if (this.minValueDragRange && this.maxValueDragRange && this.minMaxValueRange) {
const newMinValue = value - this.minValueDragRange;
const newMaxValue = value + this.maxValueDragRange;
if (newMaxValue <= this.max &&
newMinValue >= this.min &&
newMaxValue - newMinValue === this.minMaxValueRange) {
this.minValue = this.bound(newMinValue, "minValue");
this.maxValue = this.bound(newMaxValue, "maxValue");
}
}
else {
this.minValueDragRange = value - this.minValue;
this.maxValueDragRange = this.maxValue - value;
this.minMaxValueRange = this.maxValue - this.minValue;
}
}
else {
this[this.dragProp] = this.bound(value, this.dragProp);
}
this.calciteSliderUpdate.emit();
}
}
dragEnd() {
this.dragProp = null;
document.removeEventListener("mousemove", this.dragListener);
document.removeEventListener("touchmove", this.dragListener);
this.minValueDragRange = null;
this.maxValueDragRange = null;
this.minMaxValueRange = null;
}
/**
* If number is outside range, constrain to min or max
* @internal
*/
bound(num, prop) {
num = Math.min(num, this.max);
num = Math.max(num, this.min);
// ensure that maxValue and minValue don't swap positions
if (prop === "maxValue") {
num = Math.max(num, this.minValue);
}
if (prop === "minValue") {
num = Math.min(num, this.maxValue);
}
return num;
}
/**
* Translate a pixel position to value along the range
* @internal
*/
translate(x) {
const range = this.max - this.min;
const { left, width } = this.el.getBoundingClientRect();
const percent = (x - left) / width;
let value = this.bound(this.min + range * percent);
if (this.snap && this.step) {
value = this.getClosestStep(value);
}
return value;
}
/**
* Get closest allowed value along stepped values
* @internal
*/
getClosestStep(num) {
num = this.bound(num);
if (this.step) {
const step = Math.round(num / this.step) * this.step;
num = this.bound(step);
}
return num;
}
getFontSizeForElement(element) {
return Number(window.getComputedStyle(element).getPropertyValue("font-size").match(/\d+/)[0]);
}
/**
* Get position of value along range as fractional value
* @return {number} number in the unit interval [0,1]
* @internal
*/
getUnitInterval(num) {
num = this.bound(num);
const range = this.max - this.min;
return (num - this.min) / range;
}
adjustHostObscuredHandleLabel(name) {
const label = this.el.shadowRoot.querySelector(`.handle__label--${name}`);
const labelStatic = this.el.shadowRoot.querySelector(`.handle__label--${name}.static`);
const labelTransformed = this.el.shadowRoot.querySelector(`.handle__label--${name}.transformed`);
const labelStaticOffset = this.getHostOffset(labelStatic.getBoundingClientRect().left, labelStatic.getBoundingClientRect().right);
label.style.transform = `translateX(${labelStaticOffset}px)`;
labelTransformed.style.transform = `translateX(${labelStaticOffset}px)`;
}
hyphenateCollidingRangeHandleLabels() {
const minValueLabel = this.el.shadowRoot.querySelector(`.handle__label--minValue`);
const minValueLabelStatic = this.el.shadowRoot.querySelector(`.handle__label--minValue.static`);
const minValueLabelTransformed = this.el.shadowRoot.querySelector(`.handle__label--minValue.transformed`);
const minValueLabelStaticHostOffset = this.getHostOffset(minValueLabelStatic.getBoundingClientRect().left, minValueLabelStatic.getBoundingClientRect().right);
const valueLabel = this.el.shadowRoot.querySelector(`.handle__label--value`);
const valueLabelStatic = this.el.shadowRoot.querySelector(`.handle__label--value.static`);
const valueLabelTransformed = this.el.shadowRoot.querySelector(`.handle__label--value.transformed`);
const valueLabelStaticHostOffset = this.getHostOffset(valueLabelStatic.getBoundingClientRect().left, valueLabelStatic.getBoundingClientRect().right);
const labelFontSize = this.getFontSizeForElement(minValueLabel);
const labelTransformedOverlap = this.getRangeLabelOverlap(minValueLabelTransformed, valueLabelTransformed);
if (labelTransformedOverlap > 0) {
minValueLabel.classList.add("hyphen");
if (valueLabelStaticHostOffset === 0 && minValueLabelStaticHostOffset === 0) {
// Neither handle overlaps the host boundary
let minValueLabelTranslate = labelTransformedOverlap / 2 - labelFontSize / 2;
if (Math.sign(minValueLabelTranslate) === -1) {
minValueLabelTranslate = Math.abs(minValueLabelTranslate);
}
else {
minValueLabelTranslate = -minValueLabelTranslate;
}
const minValueLabelTransformedHostOffset = this.getHostOffset(minValueLabelTransformed.getBoundingClientRect().left +
minValueLabelTranslate -
labelFontSize / 2, minValueLabelTransformed.getBoundingClientRect().right +
minValueLabelTranslate -
labelFontSize / 2);
let valueLabelTranslate = labelTransformedOverlap / 2;
const valueLabelTransformedHostOffset = this.getHostOffset(valueLabelTransformed.getBoundingClientRect().left + valueLabelTranslate, valueLabelTransformed.getBoundingClientRect().right + valueLabelTranslate);
if (minValueLabelTransformedHostOffset !== 0) {
minValueLabelTranslate = minValueLabelTranslate + minValueLabelTransformedHostOffset;
valueLabelTranslate = valueLabelTranslate + minValueLabelTransformedHostOffset;
}
if (valueLabelTransformedHostOffset !== 0) {
minValueLabelTranslate = minValueLabelTranslate + valueLabelTransformedHostOffset;
valueLabelTranslate = valueLabelTranslate + valueLabelTransformedHostOffset;
}
minValueLabel.style.transform = `translateX(${minValueLabelTranslate}px)`;
minValueLabelTransformed.style.transform = `translateX(${minValueLabelTranslate - labelFontSize / 2}px)`;
valueLabel.style.transform = `translateX(${valueLabelTranslate}px)`;
valueLabelTransformed.style.transform = `translateX(${valueLabelTranslate}px)`;
}
else if (minValueLabelStaticHostOffset !== 0 &&
(Math.sign(valueLabelStaticHostOffset) === 0 || Math.sign(valueLabelStaticHostOffset) === 1)) {
// minValueLabel overlaps host boundary on the left side
minValueLabel.style.transform = `translateX(${minValueLabelStaticHostOffset + labelFontSize / 2}px)`;
valueLabel.style.transform = `translateX(${labelTransformedOverlap + valueLabelStaticHostOffset}px)`;
valueLabelTransformed.style.transform = `translateX(${labelTransformedOverlap + valueLabelStaticHostOffset}px)`;
}
else if (valueLabelStaticHostOffset !== 0) {
// valueLabel overlaps host boundary on the right side
let minValueLabelTranslate = Math.abs(minValueLabelStaticHostOffset) + labelTransformedOverlap - labelFontSize / 2;
if (Math.sign(minValueLabelTranslate) === -1) {
minValueLabelTranslate = Math.abs(minValueLabelTranslate);
}
else {
minValueLabelTranslate = -minValueLabelTranslate;
}
minValueLabel.style.transform = `translateX(${minValueLabelTranslate}px)`;
minValueLabelTransformed.style.transform = `translateX(${minValueLabelTranslate - labelFontSize / 2}px)`;
}
}
else {
minValueLabel.classList.remove("hyphen");
minValueLabel.style.transform = `translateX(${minValueLabelStaticHostOffset}px)`;
minValueLabelTransformed.style.transform = `translateX(${minValueLabelStaticHostOffset}px)`;
valueLabel.style.transform = `translateX(${valueLabelStaticHostOffset}px)`;
valueLabelTransformed.style.transform = `translateX(${valueLabelStaticHostOffset}px)`;
}
}
/**
* Hides bounding tick labels that are obscured by either handle.
*/
hideObscuredBoundingTickLabels() {
if (!this.hasHistogram && !this.isRange && !this.labelHandles && !this.precise) {
return;
}
if (!this.hasHistogram && !this.isRange && this.labelHandles && !this.precise) {
return;
}
if (!this.hasHistogram && !this.isRange && !this.labelHandles && this.precise) {
return;
}
if (!this.hasHistogram && !this.isRange && this.labelHandles && this.precise) {
return;
}
if (!this.hasHistogram && this.isRange && !this.precise) {
return;
}
if (this.hasHistogram && !this.precise && !this.labelHandles) {
return;
}
const minHandle = this.el.shadowRoot.querySelector(".thumb--minValue");
const maxHandle = this.el.shadowRoot.querySelector(".thumb--value");
const minTickLabel = this.el.shadowRoot.querySelector(".tick__label--min");
const maxTickLabel = this.el.shadowRoot.querySelector(".tick__label--max");
if (!minHandle && maxHandle && minTickLabel && maxTickLabel) {
if (this.isMinTickLabelObscured(minTickLabel, maxHandle)) {
minTickLabel.style.opacity = "0";
}
else {
minTickLabel.style.opacity = "1";
}
if (this.isMaxTickLabelObscured(maxTickLabel, maxHandle)) {
maxTickLabel.style.opacity = "0";
}
else {
maxTickLabel.style.opacity = "1";
}
}
if (minHandle && maxHandle && minTickLabel && maxTickLabel) {
if (this.isMinTickLabelObscured(minTickLabel, minHandle) ||
this.isMinTickLabelObscured(minTickLabel, maxHandle)) {
minTickLabel.style.opacity = "0";
}
else {
minTickLabel.style.opacity = "1";
}
if (this.isMaxTickLabelObscured(maxTickLabel, minHandle) ||
(this.isMaxTickLabelObscured(maxTickLabel, maxHandle) && this.hasHistogram)) {
maxTickLabel.style.opacity = "0";
}
else {
maxTickLabel.style.opacity = "1";
}
}
}
/**
* Returns an integer representing the number of pixels to offset on the left or right side based on desired position behavior.
* @internal
*/
getHostOffset(leftBounds, rightBounds) {
const hostBounds = this.el.getBoundingClientRect();
if (leftBounds + 7 < hostBounds.left) {
const offset = hostBounds.left - leftBounds - 7;
return offset;
}
if (rightBounds - 7 > hostBounds.right) {
const offset = -(rightBounds - hostBounds.right) + 7;
return offset;
}
return 0;
}
/**
* Returns an integer representing the number of pixels that the two given span elements are overlapping, taking into account
* a space in between the two spans equal to the font-size set on them to account for the space needed to render a hyphen.
* @param minValueLabel
* @param valueLabel
*/
getRangeLabelOverlap(minValueLabel, valueLabel) {
const minValueLabelBounds = minValueLabel.getBoundingClientRect();
const valueLabelBounds = valueLabel.getBoundingClientRect();
const minValueLabelFontSize = this.getFontSizeForElement(minValueLabel);
const rangeLabelOverlap = minValueLabelBounds.right + minValueLabelFontSize - valueLabelBounds.left;
return rangeLabelOverlap > 0 ? rangeLabelOverlap : 0;
}
/**
* Returns a boolean value representing if the minLabel span element is obscured (being overlapped) by the given handle button element.
* @param minLabel
* @param handle
*/
isMinTickLabelObscured(minLabel, handle) {
const minLabelBounds = minLabel.getBoundingClientRect();
const handleBounds = handle.getBoundingClientRect();
if (handleBounds.left < minLabelBounds.right) {
return true;
}
return false;
}
/**
* Returns a boolean value representing if the maxLabel span element is obscured (being overlapped) by the given handle button element.
* @param maxLabel
* @param handle
*/
isMaxTickLabelObscured(maxLabel, handle) {
const maxLabelBounds = maxLabel.getBoundingClientRect();
const handleBounds = handle.getBoundingClientRect();
if (handleBounds.right > maxLabelBounds.left) {
return true;
}
return false;
}
static get is() { return "calcite-slider"; }
static get encapsulation() { return "shadow"; }
static get originalStyleUrls() { return {
"$": ["calcite-slider.scss"]
}; }
static get styleUrls() { return {
"$": ["calcite-slider.css"]
}; }
static get properties() { return {
"theme": {
"type": "string",
"mutable": false,
"complexType": {
"original": "Theme",
"resolved": "\"dark\" | \"light\"",
"references": {
"Theme": {
"location": "import",
"path": "../interfaces"
}
}
},
"required": false,
"optional": false,
"docs": {
"tags": [],
"text": "Select theme (light or dark)"
},
"attribute": "theme",
"reflect": true
},
"disabled": {
"type": "boolean",
"mutable": false,
"complexType": {
"original": "boolean",
"resolved": "boolean",
"references": {}
},
"required": false,
"optional": false,
"docs": {
"tags": [],
"text": "Disable and gray out the slider"
},
"attribute": "disabled",
"reflect": true,
"defaultValue": "false"
},
"min": {
"type": "number",
"mutable": false,
"complexType": {
"original": "number",
"resolved": "number",
"references": {}
},
"required": false,
"optional": false,
"docs": {
"tags": [],
"text": "Minimum selectable value"
},
"attribute": "min",
"reflect": true,
"defaultValue": "0"
},
"max": {
"type": "number",
"mutable": false,
"complexType": {
"original": "number",
"resolved": "number",
"references": {}
},
"required": false,
"optional": false,
"docs": {
"tags": [],
"text": "Maximum selectable value"
},
"attribute": "max",
"reflect": true,
"defaultValue": "100"
},
"value": {
"type": "number",
"mutable": true,
"complexType": {
"original": "null | number",
"resolved": "number",
"references": {}
},
"required": false,
"optional": false,
"docs": {
"tags": [],
"text": "Currently selected number (if single select)"
},
"attribute": "value",
"reflect": true,
"defaultValue": "null"
},
"minValue": {
"type": "number",
"mutable": false,
"complexType": {
"original": "number",
"resolved": "number",
"references": {}
},
"required": false,
"optional": true,
"docs": {
"tags": [],
"text": "Currently selected lower number (if multi-select)"
},
"attribute": "min-value",
"reflect": false
},
"maxValue": {
"type": "number",
"mutable": false,
"complexType": {
"original": "number",
"resolved": "number",
"references": {}
},
"required": false,
"optional": true,
"docs": {
"tags": [],
"text": "Currently selected upper number (if multi-select)"
},
"attribute": "max-value",
"reflect": false
},
"minLabel": {
"type": "string",
"mutable": false,
"complexType": {
"original": "string",
"resolved": "string",
"references": {}
},
"required": false,
"optional": false,
"docs": {
"tags": [],
"text": "Label for first (or only) handle (ex. \"Temperature, lower bound\")"
},
"attribute": "min-label",
"reflect": false
},
"maxLabel": {
"type": "string",
"mutable": false,
"complexType": {
"original": "string",
"resolved": "string",
"references": {}
},
"required": false,
"optional": true,
"docs": {
"tags": [],
"text": "Label for second handle if needed (ex. \"Temperature, upper bound\")"
},
"attribute": "max-label",
"reflect": false
},
"snap": {
"type": "boolean",
"mutable": false,
"complexType": {
"original": "boolean",
"resolved": "boolean",
"references": {}
},
"required": false,
"optional": true,
"docs": {
"tags": [],
"text": "When true, enables snap selection along the step interval"
},
"attribute": "snap",
"reflect": false,
"defaultValue": "false"
},
"step": {
"type": "number",
"mutable": false,
"complexType": {
"original": "number",
"resolved": "number",
"references": {}
},
"required": false,
"optional": true,
"docs": {
"tags": [],
"text": "Interval to move on up/down keys"
},
"attribute": "step",
"reflect": false,
"defaultValue": "1"
},
"pageStep": {
"type": "number",
"mutable": false,
"complexType": {
"original": "number",
"resolved": "number",
"references": {}
},
"required": false,
"optional": true,
"docs": {
"tags": [],
"text": "Interval to move on page up/page down keys"
},
"attribute": "page-step",
"reflect": false
},
"ticks": {
"type": "number",
"mutable": false,
"complexType": {
"original": "number",
"resolved": "number",
"references": {}
},
"required": false,
"optional": true,
"docs": {
"tags": [],
"text": "Show tick marks on the number line at provided interval"
},
"attribute": "ticks",
"reflect": false
},
"labelTicks": {
"type": "boolean",
"mutable": false,
"complexType": {
"original": "boolean",
"resolved": "boolean",
"references": {}
},
"required": false,
"optional": true,
"docs": {
"tags": [],
"text": "Label tick marks with their numeric value."
},
"attribute": "label-ticks",
"reflect": true
},
"labelHandles": {
"type": "boolean",
"mutable": false,
"complexType": {
"original": "boolean",
"resolved": "boolean",
"references": {}
},
"required": false,
"optional": true,
"docs": {
"tags": [],
"text": "Label handles with their numeric value"
},
"attribute": "label-handles",
"reflect": true
},
"precise": {
"type": "boolean",
"mutable": false,
"complexType": {
"original": "boolean",
"resolved": "boolean",
"references": {}
},
"required": false,
"optional": true,
"docs": {
"tags": [],
"text": "Use finer point for handles"
},
"attribute": "precise",
"reflect": false
},
"histogram": {
"type": "unknown",
"mutable": false,
"complexType": {
"original": "DataSeries",
"resolved": "Point[]",
"references": {
"DataSeries": {
"location": "import",
"path": "../calcite-graph/interfaces"
}
}
},
"required": false,
"optional": true,
"docs": {
"tags": [],
"text": "Display a histogram above the slider"
}
},
"hasHistogram": {
"type": "boolean",
"mutable": true,
"complexType": {
"original": "boolean",
"resolved": "boolean",
"references": {}
},
"required": false,
"optional": false,
"docs": {
"tags": [],
"text": "Indicates if a histogram is present"
},
"attribute": "has-histogram",
"reflect": true,
"defaultValue": "false"
}
}; }
static get states() { return {
"tickValues": {},
"activeProp": {},
"minMaxValueRange": {},
"minValueDragRange": {},
"maxValueDragRange": {}
}; }
static get events() { return [{
"method": "calciteSliderUpdate",
"name": "calciteSliderUpdate",
"bubbles": true,
"cancelable": true,
"composed": true,
"docs": {
"tags": [],
"text": "Fires on all updates to the slider.\n:warning: Will be fired frequently during drag. If you are performing any\nexpensive operations consider using a debounce or throttle to avoid\nlocking up the main thread."
},
"complexType": {
"original": "any",
"resolved": "any",
"references": {}
}
}]; }
static get methods() { return {
"setFocus": {
"complexType": {
"signature": "() => Promise<void>",
"parameters": [],
"references": {
"Promise": {
"location": "global"
}
},
"return": "Promise<void>"
},
"docs": {
"text": "",
"tags": []
}
}
}; }
static get elementRef() { return "el"; }
static get watchers() { return [{
"propName": "histogram",
"methodName": "histogramWatcher"
}]; }
static get listeners() { return [{
"name": "calciteLabelFocus",
"method": "handleLabelFocus",
"target": "window",
"capture": false,
"passive": false
}, {
"name": "keydown",
"method": "keyDownHandler",
"target": undefined,
"capture": false,
"passive": false
}, {
"name": "click",
"method": "clickHandler",
"target": undefined,
"capture": false,
"passive": false
}]; }
}