calcite-slider.js 51 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894
  1. /*!
  2. * All material copyright ESRI, All Rights Reserved, unless otherwise specified.
  3. * See https://github.com/Esri/calcite-components/blob/master/LICENSE.md for details.
  4. * v1.0.0-beta.97
  5. */
  6. import { proxyCustomElement, HTMLElement, createEvent, h, Host } from '@stencil/core/internal/client/index.js';
  7. import { g as guid } from './guid.js';
  8. import { i as isPrimaryPointerButton, k as intersects } from './dom.js';
  9. import { d as decimalPlaces, c as clamp } from './math.js';
  10. import { c as connectLabel, d as disconnectLabel } from './label2.js';
  11. import { c as connectForm, d as disconnectForm, a as afterConnectDefaultValueSet, H as HiddenFormInputSlot } from './form.js';
  12. import { u as updateHostInteraction } from './interactive.js';
  13. import { i as isActivationKey } from './key.js';
  14. import { n as numberStringFormatter, c as connectLocalized, d as disconnectLocalized } from './locale.js';
  15. import { d as defineCustomElement$2 } from './graph.js';
  16. const CSS = {
  17. handleLabel: "handle__label",
  18. handleLabelMinValue: "handle__label--minValue",
  19. handleLabelValue: "handle__label--value",
  20. tickMin: "tick__label--min",
  21. tickMax: "tick__label--max"
  22. };
  23. const sliderCss = "@charset \"UTF-8\";@keyframes in{0%{opacity:0}100%{opacity:1}}@keyframes in-down{0%{opacity:0;transform:translate3D(0, -5px, 0)}100%{opacity:1;transform:translate3D(0, 0, 0)}}@keyframes in-up{0%{opacity:0;transform:translate3D(0, 5px, 0)}100%{opacity:1;transform:translate3D(0, 0, 0)}}@keyframes in-scale{0%{opacity:0;transform:scale3D(0.95, 0.95, 1)}100%{opacity:1;transform:scale3D(1, 1, 1)}}:root{--calcite-animation-timing:calc(150ms * var(--calcite-internal-duration-factor));--calcite-internal-duration-factor:var(--calcite-duration-factor, 1);--calcite-internal-animation-timing-fast:calc(100ms * var(--calcite-internal-duration-factor));--calcite-internal-animation-timing-medium:calc(200ms * var(--calcite-internal-duration-factor));--calcite-internal-animation-timing-slow:calc(300ms * var(--calcite-internal-duration-factor))}.calcite-animate{opacity:0;animation-fill-mode:both;animation-duration:var(--calcite-animation-timing)}.calcite-animate__in{animation-name:in}.calcite-animate__in-down{animation-name:in-down}.calcite-animate__in-up{animation-name:in-up}.calcite-animate__in-scale{animation-name:in-scale}@media (prefers-reduced-motion: reduce){:root{--calcite-internal-duration-factor:0.01}}:root{--calcite-floating-ui-transition:var(--calcite-animation-timing)}:host([hidden]){display:none}:host([disabled]){pointer-events:none;cursor:default;-webkit-user-select:none;user-select:none;opacity:var(--calcite-ui-opacity-disabled)}.scale--s{--calcite-slider-handle-size:10px;--calcite-slider-handle-extension-height:6.5px;--calcite-slider-container-font-size:var(--calcite-font-size--3)}.scale--s .handle__label,.scale--s .tick__label{line-height:.75rem}.scale--m{--calcite-slider-handle-size:14px;--calcite-slider-handle-extension-height:8px;--calcite-slider-container-font-size:var(--calcite-font-size--2)}.scale--m .handle__label,.scale--m .tick__label{line-height:1rem}.scale--l{--calcite-slider-handle-size:16px;--calcite-slider-handle-extension-height:10.5px;--calcite-slider-container-font-size:var(--calcite-font-size--1)}.scale--l .handle__label,.scale--l .tick__label{line-height:1rem}.handle__label,.tick__label{font-weight:var(--calcite-font-weight-medium);color:var(--calcite-ui-text-2);font-size:var(--calcite-slider-container-font-size)}:host{display:block}.container{position:relative;display:block;overflow-wrap:normal;word-break:normal;padding-inline:calc(var(--calcite-slider-handle-size) * 0.5);padding-block:calc(var(--calcite-slider-handle-size) * 0.5);margin-block:calc(var(--calcite-slider-handle-size) * 0.5);margin-inline:0;--calcite-slider-full-handle-height:calc(\n var(--calcite-slider-handle-size) + var(--calcite-slider-handle-extension-height)\n )}:host([disabled]) .track__range,:host([disabled]) .tick--active{background-color:var(--calcite-ui-text-3)}:host([disabled]) ::slotted([calcite-hydrated][disabled]),:host([disabled]) [calcite-hydrated][disabled]{opacity:1}.scale--s .thumb:not(.thumb--precise){--calcite-slider-thumb-y-offset:-6px}.scale--m .thumb:not(.thumb--precise){--calcite-slider-thumb-y-offset:-8px}.scale--l .thumb:not(.thumb--precise){--calcite-slider-thumb-y-offset:-9px}:host([precise]:not([has-histogram])) .container .thumb--value{--calcite-slider-thumb-y-offset:calc(var(--calcite-slider-full-handle-height) * -1)}.thumb-container{position:relative;max-inline-size:100%}.thumb{--calcite-slider-thumb-x-offset:calc(var(--calcite-slider-handle-size) * 0.5);position:absolute;margin:0px;display:flex;cursor:pointer;flex-direction:column;align-items:center;border-style:none;background-color:transparent;padding:0px;font-family:inherit;outline:2px solid transparent;outline-offset:2px;transform:translate(var(--calcite-slider-thumb-x-offset), var(--calcite-slider-thumb-y-offset))}.thumb .handle__label.static,.thumb .handle__label.transformed{position:absolute;inset-block:0px;opacity:0}.thumb .handle__label.hyphen::after{content:\"—\";display:inline-block;inline-size:1em}.thumb .handle__label.hyphen--wrap{display:flex}.thumb .handle{box-sizing:border-box;border-radius:9999px;background-color:var(--calcite-ui-foreground-1);outline-color:transparent;block-size:var(--calcite-slider-handle-size);inline-size:var(--calcite-slider-handle-size);box-shadow:0 0 0 2px var(--calcite-ui-text-3) inset;transition:border var(--calcite-internal-animation-timing-medium) ease, background-color var(--calcite-internal-animation-timing-medium) ease, box-shadow var(--calcite-animation-timing) ease}.thumb .handle-extension{inline-size:0.125rem;block-size:var(--calcite-slider-handle-extension-height);background-color:var(--calcite-ui-text-3)}.thumb:hover .handle{box-shadow:0 0 0 3px var(--calcite-ui-brand) inset}.thumb:hover .handle-extension{background-color:var(--calcite-ui-brand)}.thumb:focus .handle{outline:2px solid var(--calcite-ui-brand);outline-offset:2px}.thumb:focus .handle-extension{background-color:var(--calcite-ui-brand)}.thumb.thumb--minValue{transform:translate(calc(var(--calcite-slider-thumb-x-offset) * -1), var(--calcite-slider-thumb-y-offset))}.thumb.thumb--precise{--calcite-slider-thumb-y-offset:-2px}:host([label-handles]) .thumb{--calcite-slider-thumb-x-offset:50%}:host([label-handles]):host(:not([has-histogram])) .scale--s .thumb:not(.thumb--precise){--calcite-slider-thumb-y-offset:-23px}:host([label-handles]):host(:not([has-histogram])) .scale--m .thumb:not(.thumb--precise){--calcite-slider-thumb-y-offset:-30px}:host([label-handles]):host(:not([has-histogram])) .scale--l .thumb:not(.thumb--precise){--calcite-slider-thumb-y-offset:-32px}:host([has-histogram][label-handles]) .handle__label,:host([label-handles]:not([has-histogram])) .thumb--minValue.thumb--precise .handle__label{margin-block-start:0.5em}:host(:not([has-histogram]):not([precise])) .handle__label,:host([label-handles]:not([has-histogram])) .thumb--value .handle__label{margin-block-end:0.5em}:host([label-handles][precise]):host(:not([has-histogram])) .scale--s .thumb--value{--calcite-slider-thumb-y-offset:-33px}:host([label-handles][precise]):host(:not([has-histogram])) .scale--m .thumb--value{--calcite-slider-thumb-y-offset:-44px}:host([label-handles][precise]):host(:not([has-histogram])) .scale--l .thumb--value{--calcite-slider-thumb-y-offset:-49px}.thumb:focus .handle,.thumb--active .handle{background-color:var(--calcite-ui-brand);box-shadow:0 0 8px 0 rgba(0, 0, 0, 0.16)}.thumb:hover.thumb--precise:after,.thumb:focus.thumb--precise:after,.thumb--active.thumb--precise:after{background-color:var(--calcite-ui-brand)}.track{position:relative;block-size:0.125rem;border-radius:0px;background-color:var(--calcite-ui-border-2);transition:all var(--calcite-internal-animation-timing-medium) ease-in}.track__range{position:absolute;inset-block-start:0px;block-size:0.125rem;background-color:var(--calcite-ui-brand)}.container--range .track__range:hover{cursor:ew-resize}.container--range .track__range:after{position:absolute;inline-size:100%;content:\"\";inset-block-start:calc(var(--calcite-slider-full-handle-height) * 0.5 * -1);block-size:calc(var(--calcite-slider-handle-size) + var(--calcite-slider-handle-extension-height))}@media (forced-colors: active){.thumb{outline-width:0;outline-offset:0}.handle{outline:2px solid transparent;outline-offset:2px}.thumb:focus .handle,.thumb .handle-extension,.thumb:hover .handle-extension,.thumb:focus .handle-extension,.thumb:active .handle-extension{background-color:canvasText}.track{background-color:canvasText}.track__range{background-color:highlight}}.tick{position:absolute;block-size:0.25rem;inline-size:0.125rem;border-width:1px;border-style:solid;background-color:var(--calcite-ui-border-input);border-color:var(--calcite-ui-foreground-1);inset-block-start:-2px;pointer-events:none;margin-inline-start:calc(-1 * 0.125rem)}.tick--active{background-color:var(--calcite-ui-brand)}.tick__label{pointer-events:none;margin-block-start:0.875rem;display:flex;justify-content:center}.tick__label--min{transition:opacity var(--calcite-animation-timing)}.tick__label--max{transition:opacity var(--calcite-internal-animation-timing-fast)}:host([has-histogram][label-handles]) .tick__label--min,:host([has-histogram][label-handles]) .tick__label--max,:host([has-histogram][precise]) .tick__label--min,:host([has-histogram][precise]) .tick__label--max{font-weight:var(--calcite-font-weight-normal);color:var(--calcite-ui-text-3)}.graph{color:var(--calcite-ui-foreground-3);block-size:48px}:host([label-ticks][ticks]) .container{padding-block-end:calc(0.875rem + var(--calcite-slider-container-font-size))}:host([has-histogram]):host([precise][label-handles]) .container{padding-block-end:calc(var(--calcite-slider-full-handle-height) + 1em)}:host([has-histogram]):host([label-handles]:not([precise])) .container{padding-block-end:calc(var(--calcite-slider-handle-size) * 0.5 + 1em)}:host([has-histogram]):host([precise]:not([label-handles])) .container{padding-block-end:var(--calcite-slider-full-handle-height)}:host(:not([has-histogram])):host([precise]:not([label-handles])) .container{padding-block-start:var(--calcite-slider-full-handle-height)}:host(:not([has-histogram])):host([precise]:not([label-handles])) .container--range{padding-block-end:var(--calcite-slider-full-handle-height)}:host(:not([has-histogram])):host([label-handles]:not([precise])) .container{padding-block-start:calc(var(--calcite-slider-full-handle-height) + 4px)}:host(:not([has-histogram])):host([label-handles][precise]) .container{padding-block-start:calc(var(--calcite-slider-full-handle-height) + var(--calcite-slider-container-font-size) + 4px)}:host(:not([has-histogram])):host([label-handles][precise]) .container--range{padding-block-end:calc(var(--calcite-slider-full-handle-height) + var(--calcite-slider-container-font-size) + 4px)}::slotted(input[slot=hidden-form-input]){margin:0 !important;opacity:0 !important;outline:none !important;padding:0 !important;position:absolute !important;inset:0 !important;transform:none !important;-webkit-appearance:none !important;z-index:-1 !important}";
  24. function isRange(value) {
  25. return Array.isArray(value);
  26. }
  27. const Slider = /*@__PURE__*/ proxyCustomElement(class extends HTMLElement {
  28. constructor() {
  29. super();
  30. this.__registerHost();
  31. this.__attachShadow();
  32. this.calciteSliderInput = createEvent(this, "calciteSliderInput", 6);
  33. this.calciteSliderChange = createEvent(this, "calciteSliderChange", 6);
  34. this.calciteSliderUpdate = createEvent(this, "calciteSliderUpdate", 6);
  35. //--------------------------------------------------------------------------
  36. //
  37. // Properties
  38. //
  39. //--------------------------------------------------------------------------
  40. /** When `true`, interaction is prevented and the component is displayed with lower opacity. */
  41. this.disabled = false;
  42. /**
  43. * When `true`, number values are displayed with a group separator corresponding to the language and country format.
  44. */
  45. this.groupSeparator = false;
  46. /** When `true`, indicates a histogram is present. */
  47. this.hasHistogram = false;
  48. /** When `true`, displays label handles with their numeric value. */
  49. this.labelHandles = false;
  50. /** When `true` and `ticks` is specified, displays label tick marks with their numeric value. */
  51. this.labelTicks = false;
  52. /** The component's maximum selectable value. */
  53. this.max = 100;
  54. /** The component's minimum selectable value. */
  55. this.min = 0;
  56. /**
  57. * When `true`, the slider will display values from high to low.
  58. *
  59. * Note that this value will be ignored if the slider has an associated histogram.
  60. */
  61. this.mirrored = false;
  62. /** When `true`, sets a finer point for handles. */
  63. this.precise = false;
  64. /**
  65. * When `true`, the component must have a value in order for the form to submit.
  66. */
  67. this.required = false;
  68. /** When `true`, enables snap selection in coordination with `step` via a mouse. */
  69. this.snap = false;
  70. /** Specifies the interval to move with the up, or down keys. */
  71. this.step = 1;
  72. /** The component's value. */
  73. this.value = 0;
  74. /**
  75. * Specifies the size of the component.
  76. */
  77. this.scale = "m";
  78. this.activeProp = "value";
  79. this.guid = `calcite-slider-${guid()}`;
  80. this.effectiveLocale = "";
  81. this.minMaxValueRange = null;
  82. this.minValueDragRange = null;
  83. this.maxValueDragRange = null;
  84. this.tickValues = [];
  85. this.dragUpdate = (event) => {
  86. event.preventDefault();
  87. if (this.dragProp) {
  88. const value = this.translate(event.clientX || event.pageX);
  89. if (isRange(this.value) && this.dragProp === "minMaxValue") {
  90. if (this.minValueDragRange && this.maxValueDragRange && this.minMaxValueRange) {
  91. const newMinValue = value - this.minValueDragRange;
  92. const newMaxValue = value + this.maxValueDragRange;
  93. if (newMaxValue <= this.max &&
  94. newMinValue >= this.min &&
  95. newMaxValue - newMinValue === this.minMaxValueRange) {
  96. this.setValue({
  97. minValue: this.clamp(newMinValue, "minValue"),
  98. maxValue: this.clamp(newMaxValue, "maxValue")
  99. });
  100. }
  101. }
  102. else {
  103. this.minValueDragRange = value - this.minValue;
  104. this.maxValueDragRange = this.maxValue - value;
  105. this.minMaxValueRange = this.maxValue - this.minValue;
  106. }
  107. }
  108. else {
  109. this.setValue({ [this.dragProp]: this.clamp(value, this.dragProp) });
  110. }
  111. }
  112. };
  113. this.pointerUpDragEnd = (event) => {
  114. if (!isPrimaryPointerButton(event)) {
  115. return;
  116. }
  117. this.dragEnd(event);
  118. };
  119. this.dragEnd = (event) => {
  120. this.removeDragListeners();
  121. this.focusActiveHandle(event.clientX);
  122. if (this.lastDragPropValue != this[this.dragProp]) {
  123. this.emitChange();
  124. }
  125. this.dragProp = null;
  126. this.lastDragPropValue = null;
  127. this.minValueDragRange = null;
  128. this.maxValueDragRange = null;
  129. this.minMaxValueRange = null;
  130. };
  131. /**
  132. * Set the reference of the track Element
  133. *
  134. * @internal
  135. * @param node
  136. */
  137. this.storeTrackRef = (node) => {
  138. this.trackEl = node;
  139. };
  140. /**
  141. * Returns a string representing the localized label value based if the groupSeparator prop is parsed.
  142. *
  143. * @param value
  144. */
  145. this.determineGroupSeparator = (value) => {
  146. if (typeof value === "number") {
  147. numberStringFormatter.numberFormatOptions = {
  148. locale: this.effectiveLocale,
  149. numberingSystem: this.numberingSystem,
  150. useGrouping: this.groupSeparator
  151. };
  152. return numberStringFormatter.localize(value.toString());
  153. }
  154. };
  155. }
  156. histogramWatcher(newHistogram) {
  157. this.hasHistogram = !!newHistogram;
  158. }
  159. valueHandler() {
  160. this.setMinMaxFromValue();
  161. }
  162. minMaxValueHandler() {
  163. this.setValueFromMinMax();
  164. }
  165. //--------------------------------------------------------------------------
  166. //
  167. // Lifecycle
  168. //
  169. //--------------------------------------------------------------------------
  170. connectedCallback() {
  171. connectLocalized(this);
  172. this.setMinMaxFromValue();
  173. this.setValueFromMinMax();
  174. connectLabel(this);
  175. connectForm(this);
  176. }
  177. disconnectedCallback() {
  178. disconnectLabel(this);
  179. disconnectForm(this);
  180. disconnectLocalized(this);
  181. this.removeDragListeners();
  182. }
  183. componentWillLoad() {
  184. this.tickValues = this.generateTickValues();
  185. if (!isRange(this.value)) {
  186. this.value = this.clamp(this.value);
  187. }
  188. afterConnectDefaultValueSet(this, this.value);
  189. if (this.snap && !isRange(this.value)) {
  190. this.value = this.getClosestStep(this.value);
  191. }
  192. if (this.histogram) {
  193. this.hasHistogram = true;
  194. }
  195. }
  196. componentDidRender() {
  197. if (this.labelHandles) {
  198. this.adjustHostObscuredHandleLabel("value");
  199. if (isRange(this.value)) {
  200. this.adjustHostObscuredHandleLabel("minValue");
  201. if (!(this.precise && !this.hasHistogram)) {
  202. this.hyphenateCollidingRangeHandleLabels();
  203. }
  204. }
  205. }
  206. this.hideObscuredBoundingTickLabels();
  207. updateHostInteraction(this);
  208. }
  209. render() {
  210. const id = this.el.id || this.guid;
  211. const maxProp = isRange(this.value) ? "maxValue" : "value";
  212. const value = isRange(this.value) ? this.maxValue : this.value;
  213. const displayedValue = this.determineGroupSeparator(value);
  214. const displayedMinValue = this.determineGroupSeparator(this.minValue);
  215. const min = this.minValue || this.min;
  216. const useMinValue = this.shouldUseMinValue();
  217. const minInterval = this.getUnitInterval(useMinValue ? this.minValue : min) * 100;
  218. const maxInterval = this.getUnitInterval(value) * 100;
  219. const mirror = this.shouldMirror();
  220. const leftThumbOffset = `${mirror ? 100 - minInterval : minInterval}%`;
  221. const rightThumbOffset = `${mirror ? maxInterval : 100 - maxInterval}%`;
  222. const valueIsRange = isRange(this.value);
  223. const handleLabelMinValueClasses = `${CSS.handleLabel} ${CSS.handleLabelMinValue}`;
  224. const handleLabelValueClasses = `${CSS.handleLabel} ${CSS.handleLabelValue}`;
  225. const handle = (h("div", { "aria-disabled": this.disabled, "aria-label": valueIsRange ? this.maxLabel : this.minLabel, "aria-orientation": "horizontal", "aria-valuemax": this.max, "aria-valuemin": this.min, "aria-valuenow": value, class: {
  226. thumb: true,
  227. "thumb--value": true,
  228. "thumb--active": this.lastDragProp !== "minMaxValue" && this.dragProp === maxProp
  229. }, onBlur: () => (this.activeProp = null), onFocus: () => (this.activeProp = maxProp), onPointerDown: (event) => this.pointerDownDragStart(event, maxProp), ref: (el) => (this.maxHandle = el), role: "slider", style: { right: rightThumbOffset }, tabIndex: 0 }, h("div", { class: "handle" })));
  230. const labeledHandle = (h("div", { "aria-disabled": this.disabled, "aria-label": valueIsRange ? this.maxLabel : this.minLabel, "aria-orientation": "horizontal", "aria-valuemax": this.max, "aria-valuemin": this.min, "aria-valuenow": value, class: {
  231. thumb: true,
  232. "thumb--value": true,
  233. "thumb--active": this.lastDragProp !== "minMaxValue" && this.dragProp === maxProp
  234. }, onBlur: () => (this.activeProp = null), onFocus: () => (this.activeProp = maxProp), onPointerDown: (event) => this.pointerDownDragStart(event, maxProp), ref: (el) => (this.maxHandle = el), role: "slider", style: { right: rightThumbOffset }, tabIndex: 0 }, h("span", { "aria-hidden": "true", class: handleLabelValueClasses }, displayedValue), h("span", { "aria-hidden": "true", class: `${handleLabelValueClasses} static` }, displayedValue), h("span", { "aria-hidden": "true", class: `${handleLabelValueClasses} transformed` }, displayedValue), h("div", { class: "handle" })));
  235. const histogramLabeledHandle = (h("div", { "aria-disabled": this.disabled, "aria-label": valueIsRange ? this.maxLabel : this.minLabel, "aria-orientation": "horizontal", "aria-valuemax": this.max, "aria-valuemin": this.min, "aria-valuenow": value, class: {
  236. thumb: true,
  237. "thumb--value": true,
  238. "thumb--active": this.lastDragProp !== "minMaxValue" && this.dragProp === maxProp
  239. }, onBlur: () => (this.activeProp = null), onFocus: () => (this.activeProp = maxProp), onPointerDown: (event) => this.pointerDownDragStart(event, maxProp), ref: (el) => (this.maxHandle = el), role: "slider", style: { right: rightThumbOffset }, tabIndex: 0 }, h("div", { class: "handle" }), h("span", { "aria-hidden": "true", class: handleLabelValueClasses }, displayedValue), h("span", { "aria-hidden": "true", class: `${handleLabelValueClasses} static` }, displayedValue), h("span", { "aria-hidden": "true", class: `${handleLabelValueClasses} transformed` }, displayedValue)));
  240. const preciseHandle = (h("div", { "aria-disabled": this.disabled, "aria-label": valueIsRange ? this.maxLabel : this.minLabel, "aria-orientation": "horizontal", "aria-valuemax": this.max, "aria-valuemin": this.min, "aria-valuenow": value, class: {
  241. thumb: true,
  242. "thumb--value": true,
  243. "thumb--active": this.lastDragProp !== "minMaxValue" && this.dragProp === maxProp,
  244. "thumb--precise": true
  245. }, onBlur: () => (this.activeProp = null), onFocus: () => (this.activeProp = maxProp), onPointerDown: (event) => this.pointerDownDragStart(event, maxProp), ref: (el) => (this.maxHandle = el), role: "slider", style: { right: rightThumbOffset }, tabIndex: 0 }, h("div", { class: "handle" }), h("div", { class: "handle-extension" })));
  246. const histogramPreciseHandle = (h("div", { "aria-disabled": this.disabled, "aria-label": valueIsRange ? this.maxLabel : this.minLabel, "aria-orientation": "horizontal", "aria-valuemax": this.max, "aria-valuemin": this.min, "aria-valuenow": value, class: {
  247. thumb: true,
  248. "thumb--value": true,
  249. "thumb--active": this.lastDragProp !== "minMaxValue" && this.dragProp === maxProp,
  250. "thumb--precise": true
  251. }, onBlur: () => (this.activeProp = null), onFocus: () => (this.activeProp = maxProp), onPointerDown: (event) => this.pointerDownDragStart(event, maxProp), ref: (el) => (this.maxHandle = el), role: "slider", style: { right: rightThumbOffset }, tabIndex: 0 }, h("div", { class: "handle-extension" }), h("div", { class: "handle" })));
  252. const labeledPreciseHandle = (h("div", { "aria-disabled": this.disabled, "aria-label": valueIsRange ? this.maxLabel : this.minLabel, "aria-orientation": "horizontal", "aria-valuemax": this.max, "aria-valuemin": this.min, "aria-valuenow": value, class: {
  253. thumb: true,
  254. "thumb--value": true,
  255. "thumb--active": this.lastDragProp !== "minMaxValue" && this.dragProp === maxProp,
  256. "thumb--precise": true
  257. }, onBlur: () => (this.activeProp = null), onFocus: () => (this.activeProp = maxProp), onPointerDown: (event) => this.pointerDownDragStart(event, maxProp), ref: (el) => (this.maxHandle = el), role: "slider", style: { right: rightThumbOffset }, tabIndex: 0 }, h("span", { "aria-hidden": "true", class: handleLabelValueClasses }, displayedValue), h("span", { "aria-hidden": "true", class: `${handleLabelValueClasses} static` }, displayedValue), h("span", { "aria-hidden": "true", class: `${handleLabelValueClasses} transformed` }, displayedValue), h("div", { class: "handle" }), h("div", { class: "handle-extension" })));
  258. const histogramLabeledPreciseHandle = (h("div", { "aria-disabled": this.disabled, "aria-label": valueIsRange ? this.maxLabel : this.minLabel, "aria-orientation": "horizontal", "aria-valuemax": this.max, "aria-valuemin": this.min, "aria-valuenow": value, class: {
  259. thumb: true,
  260. "thumb--value": true,
  261. "thumb--active": this.lastDragProp !== "minMaxValue" && this.dragProp === maxProp,
  262. "thumb--precise": true
  263. }, onBlur: () => (this.activeProp = null), onFocus: () => (this.activeProp = maxProp), onPointerDown: (event) => this.pointerDownDragStart(event, maxProp), ref: (el) => (this.maxHandle = el), role: "slider", style: { right: rightThumbOffset }, tabIndex: 0 }, h("div", { class: "handle-extension" }), h("div", { class: "handle" }), h("span", { "aria-hidden": "true", class: handleLabelValueClasses }, displayedValue), h("span", { "aria-hidden": "true", class: `${handleLabelValueClasses} static` }, displayedValue), h("span", { "aria-hidden": "true", class: `${handleLabelValueClasses} transformed` }, displayedValue)));
  264. const minHandle = (h("div", { "aria-disabled": this.disabled, "aria-label": this.minLabel, "aria-orientation": "horizontal", "aria-valuemax": this.max, "aria-valuemin": this.min, "aria-valuenow": this.minValue, class: {
  265. thumb: true,
  266. "thumb--minValue": true,
  267. "thumb--active": this.dragProp === "minValue"
  268. }, onBlur: () => (this.activeProp = null), onFocus: () => (this.activeProp = "minValue"), onPointerDown: (event) => this.pointerDownDragStart(event, "minValue"), ref: (el) => (this.minHandle = el), role: "slider", style: { left: leftThumbOffset }, tabIndex: 0 }, h("div", { class: "handle" })));
  269. const minLabeledHandle = (h("div", { "aria-disabled": this.disabled, "aria-label": this.minLabel, "aria-orientation": "horizontal", "aria-valuemax": this.max, "aria-valuemin": this.min, "aria-valuenow": this.minValue, class: {
  270. thumb: true,
  271. "thumb--minValue": true,
  272. "thumb--active": this.dragProp === "minValue"
  273. }, onBlur: () => (this.activeProp = null), onFocus: () => (this.activeProp = "minValue"), onPointerDown: (event) => this.pointerDownDragStart(event, "minValue"), ref: (el) => (this.minHandle = el), role: "slider", style: { left: leftThumbOffset }, tabIndex: 0 }, h("span", { "aria-hidden": "true", class: handleLabelMinValueClasses }, displayedMinValue), h("span", { "aria-hidden": "true", class: `${handleLabelMinValueClasses} static` }, displayedMinValue), h("span", { "aria-hidden": "true", class: `${handleLabelMinValueClasses} transformed` }, displayedMinValue), h("div", { class: "handle" })));
  274. const minHistogramLabeledHandle = (h("div", { "aria-disabled": this.disabled, "aria-label": this.minLabel, "aria-orientation": "horizontal", "aria-valuemax": this.max, "aria-valuemin": this.min, "aria-valuenow": this.minValue, class: {
  275. thumb: true,
  276. "thumb--minValue": true,
  277. "thumb--active": this.dragProp === "minValue"
  278. }, onBlur: () => (this.activeProp = null), onFocus: () => (this.activeProp = "minValue"), onPointerDown: (event) => this.pointerDownDragStart(event, "minValue"), ref: (el) => (this.minHandle = el), role: "slider", style: { left: leftThumbOffset }, tabIndex: 0 }, h("div", { class: "handle" }), h("span", { "aria-hidden": "true", class: handleLabelMinValueClasses }, displayedMinValue), h("span", { "aria-hidden": "true", class: `${handleLabelMinValueClasses} static` }, displayedMinValue), h("span", { "aria-hidden": "true", class: `${handleLabelMinValueClasses} transformed` }, displayedMinValue)));
  279. const minPreciseHandle = (h("div", { "aria-disabled": this.disabled, "aria-label": this.minLabel, "aria-orientation": "horizontal", "aria-valuemax": this.max, "aria-valuemin": this.min, "aria-valuenow": this.minValue, class: {
  280. thumb: true,
  281. "thumb--minValue": true,
  282. "thumb--active": this.dragProp === "minValue",
  283. "thumb--precise": true
  284. }, onBlur: () => (this.activeProp = null), onFocus: () => (this.activeProp = "minValue"), onPointerDown: (event) => this.pointerDownDragStart(event, "minValue"), ref: (el) => (this.minHandle = el), role: "slider", style: { left: leftThumbOffset }, tabIndex: 0 }, h("div", { class: "handle-extension" }), h("div", { class: "handle" })));
  285. const minLabeledPreciseHandle = (h("div", { "aria-disabled": this.disabled, "aria-label": this.minLabel, "aria-orientation": "horizontal", "aria-valuemax": this.max, "aria-valuemin": this.min, "aria-valuenow": this.minValue, class: {
  286. thumb: true,
  287. "thumb--minValue": true,
  288. "thumb--active": this.dragProp === "minValue",
  289. "thumb--precise": true
  290. }, onBlur: () => (this.activeProp = null), onFocus: () => (this.activeProp = "minValue"), onPointerDown: (event) => this.pointerDownDragStart(event, "minValue"), ref: (el) => (this.minHandle = el), role: "slider", style: { left: leftThumbOffset }, tabIndex: 0 }, h("div", { class: "handle-extension" }), h("div", { class: "handle" }), h("span", { "aria-hidden": "true", class: handleLabelMinValueClasses }, displayedMinValue), h("span", { "aria-hidden": "true", class: `${handleLabelMinValueClasses} static` }, displayedMinValue), h("span", { "aria-hidden": "true", class: `${handleLabelMinValueClasses} transformed` }, displayedMinValue)));
  291. return (h(Host, { id: id, onTouchStart: this.handleTouchStart }, h("div", { class: {
  292. ["container"]: true,
  293. ["container--range"]: valueIsRange,
  294. [`scale--${this.scale}`]: true
  295. } }, this.renderGraph(), h("div", { class: "track", ref: this.storeTrackRef }, h("div", { class: "track__range", onPointerDown: (event) => this.pointerDownDragStart(event, "minMaxValue"), style: {
  296. left: `${mirror ? 100 - maxInterval : minInterval}%`,
  297. right: `${mirror ? minInterval : 100 - maxInterval}%`
  298. } }), h("div", { class: "ticks" }, this.tickValues.map((tick) => {
  299. const tickOffset = `${this.getUnitInterval(tick) * 100}%`;
  300. let activeTicks = tick >= min && tick <= value;
  301. if (useMinValue) {
  302. activeTicks = tick >= this.minValue && tick <= this.maxValue;
  303. }
  304. return (h("span", { class: {
  305. tick: true,
  306. "tick--active": activeTicks
  307. }, style: {
  308. left: mirror ? "" : tickOffset,
  309. right: mirror ? tickOffset : ""
  310. } }, this.renderTickLabel(tick)));
  311. }))), h("div", { class: "thumb-container" }, !this.precise && !this.labelHandles && valueIsRange && minHandle, !this.hasHistogram &&
  312. !this.precise &&
  313. this.labelHandles &&
  314. valueIsRange &&
  315. minLabeledHandle, this.precise && !this.labelHandles && valueIsRange && minPreciseHandle, this.precise && this.labelHandles && valueIsRange && minLabeledPreciseHandle, this.hasHistogram &&
  316. !this.precise &&
  317. this.labelHandles &&
  318. valueIsRange &&
  319. 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 &&
  320. this.precise &&
  321. this.labelHandles &&
  322. histogramLabeledPreciseHandle, h(HiddenFormInputSlot, { component: this })))));
  323. }
  324. renderGraph() {
  325. return this.histogram ? (h("calcite-graph", { class: "graph", colorStops: this.histogramStops, data: this.histogram, highlightMax: isRange(this.value) ? this.maxValue : this.value, highlightMin: isRange(this.value) ? this.minValue : this.min, max: this.max, min: this.min })) : null;
  326. }
  327. renderTickLabel(tick) {
  328. const valueIsRange = isRange(this.value);
  329. const isMinTickLabel = tick === this.min;
  330. const isMaxTickLabel = tick === this.max;
  331. const displayedTickValue = this.determineGroupSeparator(tick);
  332. const tickLabel = (h("span", { class: {
  333. tick__label: true,
  334. [CSS.tickMin]: isMinTickLabel,
  335. [CSS.tickMax]: isMaxTickLabel
  336. } }, displayedTickValue));
  337. if (this.labelTicks && !this.hasHistogram && !valueIsRange) {
  338. return tickLabel;
  339. }
  340. if (this.labelTicks &&
  341. !this.hasHistogram &&
  342. valueIsRange &&
  343. !this.precise &&
  344. !this.labelHandles) {
  345. return tickLabel;
  346. }
  347. if (this.labelTicks &&
  348. !this.hasHistogram &&
  349. valueIsRange &&
  350. !this.precise &&
  351. this.labelHandles) {
  352. return tickLabel;
  353. }
  354. if (this.labelTicks &&
  355. !this.hasHistogram &&
  356. valueIsRange &&
  357. this.precise &&
  358. (isMinTickLabel || isMaxTickLabel)) {
  359. return tickLabel;
  360. }
  361. if (this.labelTicks && this.hasHistogram && !this.precise && !this.labelHandles) {
  362. return tickLabel;
  363. }
  364. if (this.labelTicks &&
  365. this.hasHistogram &&
  366. this.precise &&
  367. !this.labelHandles &&
  368. (isMinTickLabel || isMaxTickLabel)) {
  369. return tickLabel;
  370. }
  371. if (this.labelTicks &&
  372. this.hasHistogram &&
  373. !this.precise &&
  374. this.labelHandles &&
  375. (isMinTickLabel || isMaxTickLabel)) {
  376. return tickLabel;
  377. }
  378. if (this.labelTicks &&
  379. this.hasHistogram &&
  380. this.precise &&
  381. this.labelHandles &&
  382. (isMinTickLabel || isMaxTickLabel)) {
  383. return tickLabel;
  384. }
  385. return null;
  386. }
  387. //--------------------------------------------------------------------------
  388. //
  389. // Event Listeners
  390. //
  391. //--------------------------------------------------------------------------
  392. keyDownHandler(event) {
  393. const mirror = this.shouldMirror();
  394. const { activeProp, max, min, pageStep, step } = this;
  395. const value = this[activeProp];
  396. const { key } = event;
  397. if (isActivationKey(key)) {
  398. event.preventDefault();
  399. return;
  400. }
  401. let adjustment;
  402. if (key === "ArrowUp" || key === "ArrowRight") {
  403. const directionFactor = mirror && key === "ArrowRight" ? -1 : 1;
  404. adjustment = value + step * directionFactor;
  405. }
  406. else if (key === "ArrowDown" || key === "ArrowLeft") {
  407. const directionFactor = mirror && key === "ArrowLeft" ? -1 : 1;
  408. adjustment = value - step * directionFactor;
  409. }
  410. else if (key === "PageUp") {
  411. if (pageStep) {
  412. adjustment = value + pageStep;
  413. }
  414. }
  415. else if (key === "PageDown") {
  416. if (pageStep) {
  417. adjustment = value - pageStep;
  418. }
  419. }
  420. else if (key === "Home") {
  421. adjustment = min;
  422. }
  423. else if (key === "End") {
  424. adjustment = max;
  425. }
  426. if (isNaN(adjustment)) {
  427. return;
  428. }
  429. event.preventDefault();
  430. const fixedDecimalAdjustment = Number(adjustment.toFixed(decimalPlaces(step)));
  431. this.setValue({
  432. [activeProp]: this.clamp(fixedDecimalAdjustment, activeProp)
  433. });
  434. }
  435. pointerDownHandler(event) {
  436. if (!isPrimaryPointerButton(event)) {
  437. return;
  438. }
  439. const x = event.clientX || event.pageX;
  440. const position = this.translate(x);
  441. let prop = "value";
  442. if (isRange(this.value)) {
  443. const inRange = position >= this.minValue && position <= this.maxValue;
  444. if (inRange && this.lastDragProp === "minMaxValue") {
  445. prop = "minMaxValue";
  446. }
  447. else {
  448. const closerToMax = Math.abs(this.maxValue - position) < Math.abs(this.minValue - position);
  449. prop = closerToMax || position > this.maxValue ? "maxValue" : "minValue";
  450. }
  451. }
  452. this.lastDragPropValue = this[prop];
  453. this.dragStart(prop);
  454. const isThumbActive = this.el.shadowRoot.querySelector(".thumb:active");
  455. if (!isThumbActive) {
  456. this.setValue({ [prop]: this.clamp(position, prop) });
  457. }
  458. this.focusActiveHandle(x);
  459. }
  460. handleTouchStart(event) {
  461. // needed to prevent extra click at the end of a handle drag
  462. event.preventDefault();
  463. }
  464. //--------------------------------------------------------------------------
  465. //
  466. // Public Methods
  467. //
  468. //--------------------------------------------------------------------------
  469. /** Sets focus on the component. */
  470. async setFocus() {
  471. const handle = this.minHandle ? this.minHandle : this.maxHandle;
  472. handle === null || handle === void 0 ? void 0 : handle.focus();
  473. }
  474. //--------------------------------------------------------------------------
  475. //
  476. // Private Methods
  477. //
  478. //--------------------------------------------------------------------------
  479. setValueFromMinMax() {
  480. const { minValue, maxValue } = this;
  481. if (typeof minValue === "number" && typeof maxValue === "number") {
  482. this.value = [minValue, maxValue];
  483. }
  484. }
  485. setMinMaxFromValue() {
  486. const { value } = this;
  487. if (isRange(value)) {
  488. this.minValue = value[0];
  489. this.maxValue = value[1];
  490. }
  491. }
  492. onLabelClick() {
  493. this.setFocus();
  494. }
  495. shouldMirror() {
  496. return this.mirrored && !this.hasHistogram;
  497. }
  498. shouldUseMinValue() {
  499. if (!isRange(this.value)) {
  500. return false;
  501. }
  502. return ((this.hasHistogram && this.maxValue === 0) || (!this.hasHistogram && this.minValue === 0));
  503. }
  504. generateTickValues() {
  505. const ticks = [];
  506. let current = this.min;
  507. while (this.ticks && current < this.max + this.ticks) {
  508. ticks.push(Math.min(current, this.max));
  509. current = current + this.ticks;
  510. }
  511. return ticks;
  512. }
  513. pointerDownDragStart(event, prop) {
  514. if (!isPrimaryPointerButton(event)) {
  515. return;
  516. }
  517. this.dragStart(prop);
  518. }
  519. dragStart(prop) {
  520. this.dragProp = prop;
  521. this.lastDragProp = this.dragProp;
  522. this.activeProp = prop;
  523. document.addEventListener("pointermove", this.dragUpdate);
  524. document.addEventListener("pointerup", this.pointerUpDragEnd);
  525. document.addEventListener("pointercancel", this.dragEnd);
  526. }
  527. focusActiveHandle(valueX) {
  528. switch (this.dragProp) {
  529. case "minValue":
  530. this.minHandle.focus();
  531. break;
  532. case "maxValue":
  533. case "value":
  534. this.maxHandle.focus();
  535. break;
  536. case "minMaxValue":
  537. this.getClosestHandle(valueX).focus();
  538. break;
  539. }
  540. }
  541. emitInput() {
  542. this.calciteSliderInput.emit();
  543. this.calciteSliderUpdate.emit();
  544. }
  545. emitChange() {
  546. this.calciteSliderChange.emit();
  547. }
  548. removeDragListeners() {
  549. document.removeEventListener("pointermove", this.dragUpdate);
  550. document.removeEventListener("pointerup", this.pointerUpDragEnd);
  551. document.removeEventListener("pointercancel", this.dragEnd);
  552. }
  553. /**
  554. * Set prop value(s) if changed at the component level
  555. *
  556. * @param {object} values - a set of key/value pairs delineating what properties in the component to update
  557. */
  558. setValue(values) {
  559. let valueChanged;
  560. Object.keys(values).forEach((propName) => {
  561. const newValue = values[propName];
  562. if (!valueChanged) {
  563. const oldValue = this[propName];
  564. valueChanged = oldValue !== newValue;
  565. }
  566. this[propName] = newValue;
  567. });
  568. if (!valueChanged) {
  569. return;
  570. }
  571. const dragging = this.dragProp;
  572. if (!dragging) {
  573. this.emitChange();
  574. }
  575. this.emitInput();
  576. }
  577. /**
  578. * If number is outside range, constrain to min or max
  579. *
  580. * @param value
  581. * @param prop
  582. * @internal
  583. */
  584. clamp(value, prop) {
  585. value = clamp(value, this.min, this.max);
  586. // ensure that maxValue and minValue don't swap positions
  587. if (prop === "maxValue") {
  588. value = Math.max(value, this.minValue);
  589. }
  590. if (prop === "minValue") {
  591. value = Math.min(value, this.maxValue);
  592. }
  593. return value;
  594. }
  595. /**
  596. * Translate a pixel position to value along the range
  597. *
  598. * @param x
  599. * @internal
  600. */
  601. translate(x) {
  602. const range = this.max - this.min;
  603. const { left, width } = this.trackEl.getBoundingClientRect();
  604. const percent = (x - left) / width;
  605. const mirror = this.shouldMirror();
  606. const clampedValue = this.clamp(this.min + range * (mirror ? 1 - percent : percent));
  607. let value = Number(clampedValue.toFixed(decimalPlaces(this.step)));
  608. if (this.snap && this.step) {
  609. value = this.getClosestStep(value);
  610. }
  611. return value;
  612. }
  613. /**
  614. * Get closest allowed value along stepped values
  615. *
  616. * @param num
  617. * @internal
  618. */
  619. getClosestStep(num) {
  620. num = Number(this.clamp(num).toFixed(decimalPlaces(this.step)));
  621. if (this.step) {
  622. const step = Math.round(num / this.step) * this.step;
  623. num = Number(this.clamp(step).toFixed(decimalPlaces(this.step)));
  624. }
  625. return num;
  626. }
  627. getClosestHandle(valueX) {
  628. return this.getDistanceX(this.maxHandle, valueX) > this.getDistanceX(this.minHandle, valueX)
  629. ? this.minHandle
  630. : this.maxHandle;
  631. }
  632. getDistanceX(el, valueX) {
  633. return Math.abs(el.getBoundingClientRect().left - valueX);
  634. }
  635. getFontSizeForElement(element) {
  636. return Number(window.getComputedStyle(element).getPropertyValue("font-size").match(/\d+/)[0]);
  637. }
  638. /**
  639. * Get position of value along range as fractional value
  640. *
  641. * @param num
  642. * @return {number} number in the unit interval [0,1]
  643. * @internal
  644. */
  645. getUnitInterval(num) {
  646. num = this.clamp(num);
  647. const range = this.max - this.min;
  648. return (num - this.min) / range;
  649. }
  650. adjustHostObscuredHandleLabel(name) {
  651. const label = this.el.shadowRoot.querySelector(`.handle__label--${name}`);
  652. const labelStatic = this.el.shadowRoot.querySelector(`.handle__label--${name}.static`);
  653. const labelTransformed = this.el.shadowRoot.querySelector(`.handle__label--${name}.transformed`);
  654. const labelStaticBounds = labelStatic.getBoundingClientRect();
  655. const labelStaticOffset = this.getHostOffset(labelStaticBounds.left, labelStaticBounds.right);
  656. label.style.transform = `translateX(${labelStaticOffset}px)`;
  657. labelTransformed.style.transform = `translateX(${labelStaticOffset}px)`;
  658. }
  659. hyphenateCollidingRangeHandleLabels() {
  660. const { shadowRoot } = this.el;
  661. const mirror = this.shouldMirror();
  662. const leftModifier = mirror ? "value" : "minValue";
  663. const rightModifier = mirror ? "minValue" : "value";
  664. const leftValueLabel = shadowRoot.querySelector(`.handle__label--${leftModifier}`);
  665. const leftValueLabelStatic = shadowRoot.querySelector(`.handle__label--${leftModifier}.static`);
  666. const leftValueLabelTransformed = shadowRoot.querySelector(`.handle__label--${leftModifier}.transformed`);
  667. const leftValueLabelStaticHostOffset = this.getHostOffset(leftValueLabelStatic.getBoundingClientRect().left, leftValueLabelStatic.getBoundingClientRect().right);
  668. const rightValueLabel = shadowRoot.querySelector(`.handle__label--${rightModifier}`);
  669. const rightValueLabelStatic = shadowRoot.querySelector(`.handle__label--${rightModifier}.static`);
  670. const rightValueLabelTransformed = shadowRoot.querySelector(`.handle__label--${rightModifier}.transformed`);
  671. const rightValueLabelStaticHostOffset = this.getHostOffset(rightValueLabelStatic.getBoundingClientRect().left, rightValueLabelStatic.getBoundingClientRect().right);
  672. const labelFontSize = this.getFontSizeForElement(leftValueLabel);
  673. const labelTransformedOverlap = this.getRangeLabelOverlap(leftValueLabelTransformed, rightValueLabelTransformed);
  674. const hyphenLabel = leftValueLabel;
  675. const labelOffset = labelFontSize / 2;
  676. if (labelTransformedOverlap > 0) {
  677. hyphenLabel.classList.add("hyphen", "hyphen--wrap");
  678. if (rightValueLabelStaticHostOffset === 0 && leftValueLabelStaticHostOffset === 0) {
  679. // Neither handle overlaps the host boundary
  680. let leftValueLabelTranslate = labelTransformedOverlap / 2 - labelOffset;
  681. leftValueLabelTranslate =
  682. Math.sign(leftValueLabelTranslate) === -1
  683. ? Math.abs(leftValueLabelTranslate)
  684. : -leftValueLabelTranslate;
  685. const leftValueLabelTransformedHostOffset = this.getHostOffset(leftValueLabelTransformed.getBoundingClientRect().left +
  686. leftValueLabelTranslate -
  687. labelOffset, leftValueLabelTransformed.getBoundingClientRect().right +
  688. leftValueLabelTranslate -
  689. labelOffset);
  690. let rightValueLabelTranslate = labelTransformedOverlap / 2;
  691. const rightValueLabelTransformedHostOffset = this.getHostOffset(rightValueLabelTransformed.getBoundingClientRect().left + rightValueLabelTranslate, rightValueLabelTransformed.getBoundingClientRect().right + rightValueLabelTranslate);
  692. if (leftValueLabelTransformedHostOffset !== 0) {
  693. leftValueLabelTranslate += leftValueLabelTransformedHostOffset;
  694. rightValueLabelTranslate += leftValueLabelTransformedHostOffset;
  695. }
  696. if (rightValueLabelTransformedHostOffset !== 0) {
  697. leftValueLabelTranslate += rightValueLabelTransformedHostOffset;
  698. rightValueLabelTranslate += rightValueLabelTransformedHostOffset;
  699. }
  700. leftValueLabel.style.transform = `translateX(${leftValueLabelTranslate}px)`;
  701. leftValueLabelTransformed.style.transform = `translateX(${leftValueLabelTranslate - labelOffset}px)`;
  702. rightValueLabel.style.transform = `translateX(${rightValueLabelTranslate}px)`;
  703. rightValueLabelTransformed.style.transform = `translateX(${rightValueLabelTranslate}px)`;
  704. }
  705. else if (leftValueLabelStaticHostOffset > 0 || rightValueLabelStaticHostOffset > 0) {
  706. // labels overlap host boundary on the left side
  707. leftValueLabel.style.transform = `translateX(${leftValueLabelStaticHostOffset + labelOffset}px)`;
  708. rightValueLabel.style.transform = `translateX(${labelTransformedOverlap + rightValueLabelStaticHostOffset}px)`;
  709. rightValueLabelTransformed.style.transform = `translateX(${labelTransformedOverlap + rightValueLabelStaticHostOffset}px)`;
  710. }
  711. else if (leftValueLabelStaticHostOffset < 0 || rightValueLabelStaticHostOffset < 0) {
  712. // labels overlap host boundary on the right side
  713. let leftValueLabelTranslate = Math.abs(leftValueLabelStaticHostOffset) + labelTransformedOverlap - labelOffset;
  714. leftValueLabelTranslate =
  715. Math.sign(leftValueLabelTranslate) === -1
  716. ? Math.abs(leftValueLabelTranslate)
  717. : -leftValueLabelTranslate;
  718. leftValueLabel.style.transform = `translateX(${leftValueLabelTranslate}px)`;
  719. leftValueLabelTransformed.style.transform = `translateX(${leftValueLabelTranslate - labelOffset}px)`;
  720. }
  721. }
  722. else {
  723. hyphenLabel.classList.remove("hyphen", "hyphen--wrap");
  724. leftValueLabel.style.transform = `translateX(${leftValueLabelStaticHostOffset}px)`;
  725. leftValueLabelTransformed.style.transform = `translateX(${leftValueLabelStaticHostOffset}px)`;
  726. rightValueLabel.style.transform = `translateX(${rightValueLabelStaticHostOffset}px)`;
  727. rightValueLabelTransformed.style.transform = `translateX(${rightValueLabelStaticHostOffset}px)`;
  728. }
  729. }
  730. /**
  731. * Hides bounding tick labels that are obscured by either handle.
  732. */
  733. hideObscuredBoundingTickLabels() {
  734. const valueIsRange = isRange(this.value);
  735. if (!this.hasHistogram && !valueIsRange && !this.labelHandles && !this.precise) {
  736. return;
  737. }
  738. if (!this.hasHistogram && !valueIsRange && this.labelHandles && !this.precise) {
  739. return;
  740. }
  741. if (!this.hasHistogram && !valueIsRange && !this.labelHandles && this.precise) {
  742. return;
  743. }
  744. if (!this.hasHistogram && !valueIsRange && this.labelHandles && this.precise) {
  745. return;
  746. }
  747. if (!this.hasHistogram && valueIsRange && !this.precise) {
  748. return;
  749. }
  750. if (this.hasHistogram && !this.precise && !this.labelHandles) {
  751. return;
  752. }
  753. const minHandle = this.el.shadowRoot.querySelector(".thumb--minValue");
  754. const maxHandle = this.el.shadowRoot.querySelector(".thumb--value");
  755. const minTickLabel = this.el.shadowRoot.querySelector(".tick__label--min");
  756. const maxTickLabel = this.el.shadowRoot.querySelector(".tick__label--max");
  757. if (!minHandle && maxHandle && minTickLabel && maxTickLabel) {
  758. minTickLabel.style.opacity = this.isMinTickLabelObscured(minTickLabel, maxHandle) ? "0" : "1";
  759. maxTickLabel.style.opacity = this.isMaxTickLabelObscured(maxTickLabel, maxHandle) ? "0" : "1";
  760. }
  761. if (minHandle && maxHandle && minTickLabel && maxTickLabel) {
  762. minTickLabel.style.opacity =
  763. this.isMinTickLabelObscured(minTickLabel, minHandle) ||
  764. this.isMinTickLabelObscured(minTickLabel, maxHandle)
  765. ? "0"
  766. : "1";
  767. maxTickLabel.style.opacity =
  768. this.isMaxTickLabelObscured(maxTickLabel, minHandle) ||
  769. (this.isMaxTickLabelObscured(maxTickLabel, maxHandle) && this.hasHistogram)
  770. ? "0"
  771. : "1";
  772. }
  773. }
  774. /**
  775. * Returns an integer representing the number of pixels to offset on the left or right side based on desired position behavior.
  776. *
  777. * @param leftBounds
  778. * @param rightBounds
  779. * @internal
  780. */
  781. getHostOffset(leftBounds, rightBounds) {
  782. const hostBounds = this.el.getBoundingClientRect();
  783. const buffer = 7;
  784. if (leftBounds + buffer < hostBounds.left) {
  785. return hostBounds.left - leftBounds - buffer;
  786. }
  787. if (rightBounds - buffer > hostBounds.right) {
  788. return -(rightBounds - hostBounds.right) + buffer;
  789. }
  790. return 0;
  791. }
  792. /**
  793. * Returns an integer representing the number of pixels that the two given span elements are overlapping, taking into account
  794. * 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.
  795. *
  796. * @param leftLabel
  797. * @param rightLabel
  798. */
  799. getRangeLabelOverlap(leftLabel, rightLabel) {
  800. const leftLabelBounds = leftLabel.getBoundingClientRect();
  801. const rightLabelBounds = rightLabel.getBoundingClientRect();
  802. const leftLabelFontSize = this.getFontSizeForElement(leftLabel);
  803. const rangeLabelOverlap = leftLabelBounds.right + leftLabelFontSize - rightLabelBounds.left;
  804. return Math.max(rangeLabelOverlap, 0);
  805. }
  806. /**
  807. * Returns a boolean value representing if the minLabel span element is obscured (being overlapped) by the given handle div element.
  808. *
  809. * @param minLabel
  810. * @param handle
  811. */
  812. isMinTickLabelObscured(minLabel, handle) {
  813. const minLabelBounds = minLabel.getBoundingClientRect();
  814. const handleBounds = handle.getBoundingClientRect();
  815. return intersects(minLabelBounds, handleBounds);
  816. }
  817. /**
  818. * Returns a boolean value representing if the maxLabel span element is obscured (being overlapped) by the given handle div element.
  819. *
  820. * @param maxLabel
  821. * @param handle
  822. */
  823. isMaxTickLabelObscured(maxLabel, handle) {
  824. const maxLabelBounds = maxLabel.getBoundingClientRect();
  825. const handleBounds = handle.getBoundingClientRect();
  826. return intersects(maxLabelBounds, handleBounds);
  827. }
  828. get el() { return this; }
  829. static get watchers() { return {
  830. "histogram": ["histogramWatcher"],
  831. "value": ["valueHandler"],
  832. "minValue": ["minMaxValueHandler"],
  833. "maxValue": ["minMaxValueHandler"]
  834. }; }
  835. static get style() { return sliderCss; }
  836. }, [1, "calcite-slider", {
  837. "disabled": [516],
  838. "groupSeparator": [516, "group-separator"],
  839. "hasHistogram": [1540, "has-histogram"],
  840. "histogram": [16],
  841. "histogramStops": [16],
  842. "labelHandles": [516, "label-handles"],
  843. "labelTicks": [516, "label-ticks"],
  844. "max": [514],
  845. "maxLabel": [1, "max-label"],
  846. "maxValue": [1026, "max-value"],
  847. "min": [514],
  848. "minLabel": [1, "min-label"],
  849. "minValue": [1026, "min-value"],
  850. "mirrored": [516],
  851. "name": [513],
  852. "numberingSystem": [1, "numbering-system"],
  853. "pageStep": [514, "page-step"],
  854. "precise": [516],
  855. "required": [516],
  856. "snap": [516],
  857. "step": [514],
  858. "ticks": [514],
  859. "value": [1538],
  860. "scale": [513],
  861. "effectiveLocale": [32],
  862. "minMaxValueRange": [32],
  863. "minValueDragRange": [32],
  864. "maxValueDragRange": [32],
  865. "tickValues": [32],
  866. "setFocus": [64]
  867. }, [[0, "keydown", "keyDownHandler"], [1, "pointerdown", "pointerDownHandler"]]]);
  868. function defineCustomElement$1() {
  869. if (typeof customElements === "undefined") {
  870. return;
  871. }
  872. const components = ["calcite-slider", "calcite-graph"];
  873. components.forEach(tagName => { switch (tagName) {
  874. case "calcite-slider":
  875. if (!customElements.get(tagName)) {
  876. customElements.define(tagName, Slider);
  877. }
  878. break;
  879. case "calcite-graph":
  880. if (!customElements.get(tagName)) {
  881. defineCustomElement$2();
  882. }
  883. break;
  884. } });
  885. }
  886. defineCustomElement$1();
  887. const CalciteSlider = Slider;
  888. const defineCustomElement = defineCustomElement$1;
  889. export { CalciteSlider, defineCustomElement };