calcite-slider.js 51 KB

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