calcite-graph_2.cjs.entry.js 60 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955
  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. 'use strict';
  7. Object.defineProperty(exports, '__esModule', { value: true });
  8. const index = require('./index-5c65e149.js');
  9. const guid = require('./guid-8b6d6cb4.js');
  10. const observers = require('./observers-d9fdf006.js');
  11. const dom = require('./dom-9ac0341c.js');
  12. const math = require('./math-9d6de52c.js');
  13. const label = require('./label-165fc611.js');
  14. const form = require('./form-11926121.js');
  15. const interactive = require('./interactive-e294111f.js');
  16. /**
  17. * Calculate slope of the tangents
  18. * uses Steffen interpolation as it's monotonic
  19. * http://jrwalsh1.github.io/posts/interpolations/
  20. */
  21. function slope(p0, p1, p2) {
  22. const dx = p1[0] - p0[0];
  23. const dx1 = p2[0] - p1[0];
  24. const dy = p1[1] - p0[1];
  25. const dy1 = p2[1] - p1[1];
  26. const m = dy / (dx || (dx1 < 0 && 0));
  27. const m1 = dy1 / (dx1 || (dx < 0 && 0));
  28. const p = (m * dx1 + m1 * dx) / (dx + dx1);
  29. return (Math.sign(m) + Math.sign(m1)) * Math.min(Math.abs(m), Math.abs(m1), 0.5 * Math.abs(p)) || 0;
  30. }
  31. /**
  32. * Calculate slope for just one tangent (single-sided)
  33. */
  34. function slopeSingle(p0, p1, m) {
  35. const dx = p1[0] - p0[0];
  36. const dy = p1[1] - p0[1];
  37. return dx ? ((3 * dy) / dx - m) / 2 : m;
  38. }
  39. /**
  40. * Given two points and their tangent slopes,
  41. * calculate the bezier handle coordinates and return draw command.
  42. *
  43. * Translates Hermite Spline to Beziér curve:
  44. * stackoverflow.com/questions/42574940/
  45. */
  46. function bezier(p0, p1, m0, m1, t) {
  47. const [x0, y0] = p0;
  48. const [x1, y1] = p1;
  49. const dx = (x1 - x0) / 3;
  50. const h1 = t([x0 + dx, y0 + dx * m0]).join(",");
  51. const h2 = t([x1 - dx, y1 - dx * m1]).join(",");
  52. const p = t([x1, y1]).join(",");
  53. return `C ${h1} ${h2} ${p}`;
  54. }
  55. /**
  56. * Generate a function which will translate a point
  57. * from the data coordinate space to svg viewbox oriented pixels
  58. */
  59. function translate({ width, height, min, max }) {
  60. const rangeX = max[0] - min[0];
  61. const rangeY = max[1] - min[1];
  62. return (point) => {
  63. const x = ((point[0] - min[0]) / rangeX) * width;
  64. const y = height - (point[1] / rangeY) * height;
  65. return [x, y];
  66. };
  67. }
  68. /**
  69. * Get the min and max values from the dataset
  70. */
  71. function range(data) {
  72. const [startX, startY] = data[0];
  73. const min = [startX, startY];
  74. const max = [startX, startY];
  75. return data.reduce(({ min, max }, [x, y]) => ({
  76. min: [Math.min(min[0], x), Math.min(min[1], y)],
  77. max: [Math.max(max[0], x), Math.max(max[1], y)]
  78. }), { min, max });
  79. }
  80. /**
  81. * Generate drawing commands for an area graph
  82. * returns a string can can be passed directly to a path element's `d` attribute
  83. */
  84. function area({ data, min, max, t }) {
  85. if (data.length === 0) {
  86. return "";
  87. }
  88. // important points for beginning and ending the path
  89. const [startX, startY] = t(data[0]);
  90. const [minX, minY] = t(min);
  91. const [maxX] = t(max);
  92. // keep track of previous slope/points
  93. let m;
  94. let p0;
  95. let p1;
  96. // iterate over data points, calculating command for each
  97. const commands = data.reduce((acc, point, i) => {
  98. p0 = data[i - 2];
  99. p1 = data[i - 1];
  100. if (i > 1) {
  101. const m1 = slope(p0, p1, point);
  102. const m0 = m === undefined ? slopeSingle(p0, p1, m1) : m;
  103. const command = bezier(p0, p1, m0, m1, t);
  104. m = m1;
  105. return `${acc} ${command}`;
  106. }
  107. return acc;
  108. }, `M ${minX},${minY} L ${minX},${startY} L ${startX},${startY}`);
  109. // close the path
  110. const last = data[data.length - 1];
  111. const end = bezier(p1, last, m, slopeSingle(p1, last, m), t);
  112. return `${commands} ${end} L ${maxX},${minY} Z`;
  113. }
  114. const graphCss = "@-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}:host{display:block}.svg{fill:currentColor;stroke:transparent;margin:0px;display:block;height:100%;width:100%;padding:0px}.svg .graph-path--highlight{fill:var(--calcite-ui-brand);opacity:0.5}";
  115. const Graph = class {
  116. constructor(hostRef) {
  117. index.registerInstance(this, hostRef);
  118. //--------------------------------------------------------------------------
  119. //
  120. // Properties
  121. //
  122. //--------------------------------------------------------------------------
  123. /**
  124. * Array of tuples describing a single data point ([x, y])
  125. * These data points should be sorted by x-axis value
  126. */
  127. this.data = [];
  128. //--------------------------------------------------------------------------
  129. //
  130. // Private State/Props
  131. //
  132. //--------------------------------------------------------------------------
  133. this.graphId = `calcite-graph-${guid.guid()}`;
  134. this.resizeObserver = observers.createObserver("resize", () => index.forceUpdate(this));
  135. }
  136. //--------------------------------------------------------------------------
  137. //
  138. // Lifecycle
  139. //
  140. //--------------------------------------------------------------------------
  141. connectedCallback() {
  142. var _a;
  143. (_a = this.resizeObserver) === null || _a === void 0 ? void 0 : _a.observe(this.el);
  144. }
  145. disconnectedCallback() {
  146. var _a;
  147. (_a = this.resizeObserver) === null || _a === void 0 ? void 0 : _a.disconnect();
  148. }
  149. render() {
  150. const { data, colorStops, el, highlightMax, highlightMin, min, max } = this;
  151. const id = this.graphId;
  152. const { clientHeight: height, clientWidth: width } = el;
  153. // if we have no data, return empty svg
  154. if (!data || data.length === 0) {
  155. return (index.h("svg", { class: "svg", height: height, preserveAspectRatio: "none", viewBox: `0 0 ${width} ${height}`, width: width }));
  156. }
  157. const { min: rangeMin, max: rangeMax } = range(data);
  158. let currentMin = rangeMin;
  159. let currentMax = rangeMax;
  160. if (min < rangeMin[0] || min > rangeMin[0]) {
  161. currentMin = [min, 0];
  162. }
  163. if (max > rangeMax[0] || max < rangeMax[0]) {
  164. currentMax = [max, rangeMax[1]];
  165. }
  166. const t = translate({ min: currentMin, max: currentMax, width, height });
  167. const [hMinX] = t([highlightMin, currentMax[1]]);
  168. const [hMaxX] = t([highlightMax, currentMax[1]]);
  169. const areaPath = area({ data, min: rangeMin, max: rangeMax, t });
  170. const fill = colorStops ? `url(#linear-gradient-${id})` : undefined;
  171. return (index.h("svg", { class: "svg", height: height, preserveAspectRatio: "none", viewBox: `0 0 ${width} ${height}`, width: width }, colorStops ? (index.h("defs", null, index.h("linearGradient", { id: `linear-gradient-${id}`, x1: "0", x2: "1", y1: "0", y2: "0" }, colorStops.map(({ offset, color, opacity }) => (index.h("stop", { offset: `${offset * 100}%`, "stop-color": color, "stop-opacity": opacity })))))) : null, highlightMin !== undefined ? ([
  172. index.h("mask", { height: "100%", id: `${id}1`, width: "100%", x: "0%", y: "0%" }, index.h("path", { d: `
  173. M 0,0
  174. L ${hMinX - 1},0
  175. L ${hMinX - 1},${height}
  176. L 0,${height}
  177. Z
  178. `, fill: "white" })),
  179. index.h("mask", { height: "100%", id: `${id}2`, width: "100%", x: "0%", y: "0%" }, index.h("path", { d: `
  180. M ${hMinX + 1},0
  181. L ${hMaxX - 1},0
  182. L ${hMaxX - 1},${height}
  183. L ${hMinX + 1}, ${height}
  184. Z
  185. `, fill: "white" })),
  186. index.h("mask", { height: "100%", id: `${id}3`, width: "100%", x: "0%", y: "0%" }, index.h("path", { d: `
  187. M ${hMaxX + 1},0
  188. L ${width},0
  189. L ${width},${height}
  190. L ${hMaxX + 1}, ${height}
  191. Z
  192. `, fill: "white" })),
  193. index.h("path", { class: "graph-path", d: areaPath, fill: fill, mask: `url(#${id}1)` }),
  194. index.h("path", { class: "graph-path--highlight", d: areaPath, fill: fill, mask: `url(#${id}2)` }),
  195. index.h("path", { class: "graph-path", d: areaPath, fill: fill, mask: `url(#${id}3)` })
  196. ]) : (index.h("path", { class: "graph-path", d: areaPath, fill: fill }))));
  197. }
  198. get el() { return index.getElement(this); }
  199. };
  200. Graph.style = graphCss;
  201. 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}";
  202. function isRange(value) {
  203. return Array.isArray(value);
  204. }
  205. const Slider = class {
  206. constructor(hostRef) {
  207. index.registerInstance(this, hostRef);
  208. this.calciteSliderInput = index.createEvent(this, "calciteSliderInput", 7);
  209. this.calciteSliderChange = index.createEvent(this, "calciteSliderChange", 7);
  210. this.calciteSliderUpdate = index.createEvent(this, "calciteSliderUpdate", 7);
  211. //--------------------------------------------------------------------------
  212. //
  213. // Properties
  214. //
  215. //--------------------------------------------------------------------------
  216. /** Disable and gray out the slider */
  217. this.disabled = false;
  218. /** Indicates if a histogram is present */
  219. this.hasHistogram = false;
  220. /** Label handles with their numeric value */
  221. this.labelHandles = false;
  222. /** Label tick marks with their numeric value. */
  223. this.labelTicks = false;
  224. /** Maximum selectable value */
  225. this.max = 100;
  226. /** Minimum selectable value */
  227. this.min = 0;
  228. /**
  229. * When true, the slider will display values from high to low.
  230. *
  231. * Note that this value will be ignored if the slider has an associated histogram.
  232. */
  233. this.mirrored = false;
  234. /** Use finer point for handles */
  235. this.precise = false;
  236. /**
  237. * When true, makes the component required for form-submission.
  238. */
  239. this.required = false;
  240. /** When true, enables snap selection along the step interval */
  241. this.snap = false;
  242. /** Interval to move on up/down keys */
  243. this.step = 1;
  244. /** Currently selected number (if single select) */
  245. this.value = 0;
  246. /**
  247. * Specify the scale of the slider, defaults to m
  248. */
  249. this.scale = "m";
  250. this.guid = `calcite-slider-${guid.guid()}`;
  251. this.activeProp = "value";
  252. this.minMaxValueRange = null;
  253. this.minValueDragRange = null;
  254. this.maxValueDragRange = null;
  255. this.tickValues = [];
  256. this.dragUpdate = (event) => {
  257. event.preventDefault();
  258. if (this.dragProp) {
  259. const value = this.translate(event.clientX || event.pageX);
  260. if (isRange(this.value) && this.dragProp === "minMaxValue") {
  261. if (this.minValueDragRange && this.maxValueDragRange && this.minMaxValueRange) {
  262. const newMinValue = value - this.minValueDragRange;
  263. const newMaxValue = value + this.maxValueDragRange;
  264. if (newMaxValue <= this.max &&
  265. newMinValue >= this.min &&
  266. newMaxValue - newMinValue === this.minMaxValueRange) {
  267. this.minValue = this.clamp(newMinValue, "minValue");
  268. this.maxValue = this.clamp(newMaxValue, "maxValue");
  269. }
  270. }
  271. else {
  272. this.minValueDragRange = value - this.minValue;
  273. this.maxValueDragRange = this.maxValue - value;
  274. this.minMaxValueRange = this.maxValue - this.minValue;
  275. }
  276. }
  277. else {
  278. this.setValue(this.dragProp, this.clamp(value, this.dragProp));
  279. }
  280. }
  281. };
  282. this.dragEnd = (event) => {
  283. this.removeDragListeners();
  284. this.focusActiveHandle(event.clientX);
  285. if (this.lastDragPropValue != this[this.dragProp]) {
  286. this.emitChange();
  287. }
  288. this.dragProp = null;
  289. this.lastDragPropValue = null;
  290. this.minValueDragRange = null;
  291. this.maxValueDragRange = null;
  292. this.minMaxValueRange = null;
  293. };
  294. /**
  295. * Set the reference of the track Element
  296. * @internal
  297. * @param node
  298. */
  299. this.storeTrackRef = (node) => {
  300. this.trackEl = node;
  301. };
  302. }
  303. histogramWatcher(newHistogram) {
  304. this.hasHistogram = !!newHistogram;
  305. }
  306. valueHandler() {
  307. this.setMinMaxFromValue();
  308. }
  309. minMaxValueHandler() {
  310. this.setValueFromMinMax();
  311. }
  312. //--------------------------------------------------------------------------
  313. //
  314. // Lifecycle
  315. //
  316. //--------------------------------------------------------------------------
  317. connectedCallback() {
  318. this.setMinMaxFromValue();
  319. this.setValueFromMinMax();
  320. label.connectLabel(this);
  321. form.connectForm(this);
  322. }
  323. disconnectedCallback() {
  324. label.disconnectLabel(this);
  325. form.disconnectForm(this);
  326. this.removeDragListeners();
  327. }
  328. componentWillLoad() {
  329. this.tickValues = this.generateTickValues();
  330. if (!isRange(this.value)) {
  331. this.value = this.clamp(this.value);
  332. }
  333. form.afterConnectDefaultValueSet(this, this.value);
  334. if (this.snap && !isRange(this.value)) {
  335. this.value = this.getClosestStep(this.value);
  336. }
  337. if (this.histogram) {
  338. this.hasHistogram = true;
  339. }
  340. }
  341. componentDidRender() {
  342. if (this.labelHandles) {
  343. this.adjustHostObscuredHandleLabel("value");
  344. if (isRange(this.value)) {
  345. this.adjustHostObscuredHandleLabel("minValue");
  346. if (!(this.precise && !this.hasHistogram)) {
  347. this.hyphenateCollidingRangeHandleLabels();
  348. }
  349. }
  350. }
  351. this.hideObscuredBoundingTickLabels();
  352. interactive.updateHostInteraction(this);
  353. }
  354. render() {
  355. const id = this.el.id || this.guid;
  356. const maxProp = isRange(this.value) ? "maxValue" : "value";
  357. const value = isRange(this.value) ? this.maxValue : this.value;
  358. const min = this.minValue || this.min;
  359. const useMinValue = this.shouldUseMinValue();
  360. const minInterval = this.getUnitInterval(useMinValue ? this.minValue : min) * 100;
  361. const maxInterval = this.getUnitInterval(value) * 100;
  362. const mirror = this.shouldMirror();
  363. const leftThumbOffset = `${mirror ? 100 - minInterval : minInterval}%`;
  364. const rightThumbOffset = `${mirror ? maxInterval : 100 - maxInterval}%`;
  365. const valueIsRange = isRange(this.value);
  366. const handle = (index.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: {
  367. thumb: true,
  368. "thumb--value": true,
  369. "thumb--active": this.lastDragProp !== "minMaxValue" && this.dragProp === maxProp
  370. }, onBlur: () => (this.activeProp = null), onFocus: () => (this.activeProp = maxProp), onPointerDown: () => this.dragStart(maxProp), ref: (el) => (this.maxHandle = el), role: "slider", style: { right: rightThumbOffset }, tabIndex: 0 }, index.h("div", { class: "handle" })));
  371. const labeledHandle = (index.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: {
  372. thumb: true,
  373. "thumb--value": true,
  374. "thumb--active": this.lastDragProp !== "minMaxValue" && this.dragProp === maxProp
  375. }, onBlur: () => (this.activeProp = null), onFocus: () => (this.activeProp = maxProp), onPointerDown: () => this.dragStart(maxProp), ref: (el) => (this.maxHandle = el), role: "slider", style: { right: rightThumbOffset }, tabIndex: 0 }, index.h("span", { "aria-hidden": "true", class: "handle__label handle__label--value" }, value ? value.toLocaleString() : value), index.h("span", { "aria-hidden": "true", class: "handle__label handle__label--value static" }, value ? value.toLocaleString() : value), index.h("span", { "aria-hidden": "true", class: "handle__label handle__label--value transformed" }, value ? value.toLocaleString() : value), index.h("div", { class: "handle" })));
  376. const histogramLabeledHandle = (index.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: {
  377. thumb: true,
  378. "thumb--value": true,
  379. "thumb--active": this.lastDragProp !== "minMaxValue" && this.dragProp === maxProp
  380. }, onBlur: () => (this.activeProp = null), onFocus: () => (this.activeProp = maxProp), onPointerDown: () => this.dragStart(maxProp), ref: (el) => (this.maxHandle = el), role: "slider", style: { right: rightThumbOffset }, tabIndex: 0 }, index.h("div", { class: "handle" }), index.h("span", { "aria-hidden": "true", class: "handle__label handle__label--value" }, value ? value.toLocaleString() : value), index.h("span", { "aria-hidden": "true", class: "handle__label handle__label--value static" }, value ? value.toLocaleString() : value), index.h("span", { "aria-hidden": "true", class: "handle__label handle__label--value transformed" }, value ? value.toLocaleString() : value)));
  381. const preciseHandle = (index.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: {
  382. thumb: true,
  383. "thumb--value": true,
  384. "thumb--active": this.lastDragProp !== "minMaxValue" && this.dragProp === maxProp,
  385. "thumb--precise": true
  386. }, onBlur: () => (this.activeProp = null), onFocus: () => (this.activeProp = maxProp), onPointerDown: () => this.dragStart(maxProp), ref: (el) => (this.maxHandle = el), role: "slider", style: { right: rightThumbOffset }, tabIndex: 0 }, index.h("div", { class: "handle" }), index.h("div", { class: "handle-extension" })));
  387. const histogramPreciseHandle = (index.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: {
  388. thumb: true,
  389. "thumb--value": true,
  390. "thumb--active": this.lastDragProp !== "minMaxValue" && this.dragProp === maxProp,
  391. "thumb--precise": true
  392. }, onBlur: () => (this.activeProp = null), onFocus: () => (this.activeProp = maxProp), onPointerDown: () => this.dragStart(maxProp), ref: (el) => (this.maxHandle = el), role: "slider", style: { right: rightThumbOffset }, tabIndex: 0 }, index.h("div", { class: "handle-extension" }), index.h("div", { class: "handle" })));
  393. const labeledPreciseHandle = (index.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: {
  394. thumb: true,
  395. "thumb--value": true,
  396. "thumb--active": this.lastDragProp !== "minMaxValue" && this.dragProp === maxProp,
  397. "thumb--precise": true
  398. }, onBlur: () => (this.activeProp = null), onFocus: () => (this.activeProp = maxProp), onPointerDown: () => this.dragStart(maxProp), ref: (el) => (this.maxHandle = el), role: "slider", style: { right: rightThumbOffset }, tabIndex: 0 }, index.h("span", { "aria-hidden": "true", class: "handle__label handle__label--value" }, value ? value.toLocaleString() : value), index.h("span", { "aria-hidden": "true", class: "handle__label handle__label--value static" }, value ? value.toLocaleString() : value), index.h("span", { "aria-hidden": "true", class: "handle__label handle__label--value transformed" }, value ? value.toLocaleString() : value), index.h("div", { class: "handle" }), index.h("div", { class: "handle-extension" })));
  399. const histogramLabeledPreciseHandle = (index.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: {
  400. thumb: true,
  401. "thumb--value": true,
  402. "thumb--active": this.lastDragProp !== "minMaxValue" && this.dragProp === maxProp,
  403. "thumb--precise": true
  404. }, onBlur: () => (this.activeProp = null), onFocus: () => (this.activeProp = maxProp), onPointerDown: () => this.dragStart(maxProp), ref: (el) => (this.maxHandle = el), role: "slider", style: { right: rightThumbOffset }, tabIndex: 0 }, index.h("div", { class: "handle-extension" }), index.h("div", { class: "handle" }), index.h("span", { "aria-hidden": "true", class: "handle__label handle__label--value" }, value ? value.toLocaleString() : value), index.h("span", { "aria-hidden": "true", class: "handle__label handle__label--value static" }, value ? value.toLocaleString() : value), index.h("span", { "aria-hidden": "true", class: "handle__label handle__label--value transformed" }, value ? value.toLocaleString() : value)));
  405. const minHandle = (index.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: {
  406. thumb: true,
  407. "thumb--minValue": true,
  408. "thumb--active": this.dragProp === "minValue"
  409. }, onBlur: () => (this.activeProp = null), onFocus: () => (this.activeProp = "minValue"), onPointerDown: () => this.dragStart("minValue"), ref: (el) => (this.minHandle = el), role: "slider", style: { left: leftThumbOffset }, tabIndex: 0 }, index.h("div", { class: "handle" })));
  410. const minLabeledHandle = (index.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: {
  411. thumb: true,
  412. "thumb--minValue": true,
  413. "thumb--active": this.dragProp === "minValue"
  414. }, onBlur: () => (this.activeProp = null), onFocus: () => (this.activeProp = "minValue"), onPointerDown: () => this.dragStart("minValue"), ref: (el) => (this.minHandle = el), role: "slider", style: { left: leftThumbOffset }, tabIndex: 0 }, index.h("span", { "aria-hidden": "true", class: "handle__label handle__label--minValue" }, this.minValue && this.minValue.toLocaleString()), index.h("span", { "aria-hidden": "true", class: "handle__label handle__label--minValue static" }, this.minValue && this.minValue.toLocaleString()), index.h("span", { "aria-hidden": "true", class: "handle__label handle__label--minValue transformed" }, this.minValue && this.minValue.toLocaleString()), index.h("div", { class: "handle" })));
  415. const minHistogramLabeledHandle = (index.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: {
  416. thumb: true,
  417. "thumb--minValue": true,
  418. "thumb--active": this.dragProp === "minValue"
  419. }, onBlur: () => (this.activeProp = null), onFocus: () => (this.activeProp = "minValue"), onPointerDown: () => this.dragStart("minValue"), ref: (el) => (this.minHandle = el), role: "slider", style: { left: leftThumbOffset }, tabIndex: 0 }, index.h("div", { class: "handle" }), index.h("span", { "aria-hidden": "true", class: "handle__label handle__label--minValue" }, this.minValue && this.minValue.toLocaleString()), index.h("span", { "aria-hidden": "true", class: "handle__label handle__label--minValue static" }, this.minValue && this.minValue.toLocaleString()), index.h("span", { "aria-hidden": "true", class: "handle__label handle__label--minValue transformed" }, this.minValue && this.minValue.toLocaleString())));
  420. const minPreciseHandle = (index.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: {
  421. thumb: true,
  422. "thumb--minValue": true,
  423. "thumb--active": this.dragProp === "minValue",
  424. "thumb--precise": true
  425. }, onBlur: () => (this.activeProp = null), onFocus: () => (this.activeProp = "minValue"), onPointerDown: () => this.dragStart("minValue"), ref: (el) => (this.minHandle = el), role: "slider", style: { left: leftThumbOffset }, tabIndex: 0 }, index.h("div", { class: "handle-extension" }), index.h("div", { class: "handle" })));
  426. const minLabeledPreciseHandle = (index.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: {
  427. thumb: true,
  428. "thumb--minValue": true,
  429. "thumb--active": this.dragProp === "minValue",
  430. "thumb--precise": true
  431. }, onBlur: () => (this.activeProp = null), onFocus: () => (this.activeProp = "minValue"), onPointerDown: () => this.dragStart("minValue"), ref: (el) => (this.minHandle = el), role: "slider", style: { left: leftThumbOffset }, tabIndex: 0 }, index.h("div", { class: "handle-extension" }), index.h("div", { class: "handle" }), index.h("span", { "aria-hidden": "true", class: "handle__label handle__label--minValue" }, this.minValue && this.minValue.toLocaleString()), index.h("span", { "aria-hidden": "true", class: "handle__label handle__label--minValue static" }, this.minValue && this.minValue.toLocaleString()), index.h("span", { "aria-hidden": "true", class: "handle__label handle__label--minValue transformed" }, this.minValue && this.minValue.toLocaleString())));
  432. return (index.h(index.Host, { id: id, onTouchStart: this.handleTouchStart }, index.h("div", { class: {
  433. ["container"]: true,
  434. ["container--range"]: valueIsRange,
  435. [`scale--${this.scale}`]: true
  436. } }, this.renderGraph(), index.h("div", { class: "track", ref: this.storeTrackRef }, index.h("div", { class: "track__range", onPointerDown: () => this.dragStart("minMaxValue"), style: {
  437. left: `${mirror ? 100 - maxInterval : minInterval}%`,
  438. right: `${mirror ? minInterval : 100 - maxInterval}%`
  439. } }), index.h("div", { class: "ticks" }, this.tickValues.map((tick) => {
  440. const tickOffset = `${this.getUnitInterval(tick) * 100}%`;
  441. let activeTicks = tick >= min && tick <= value;
  442. if (useMinValue) {
  443. activeTicks = tick >= this.minValue && tick <= this.maxValue;
  444. }
  445. return (index.h("span", { class: {
  446. tick: true,
  447. "tick--active": activeTicks
  448. }, style: {
  449. left: mirror ? "" : tickOffset,
  450. right: mirror ? tickOffset : ""
  451. } }, this.renderTickLabel(tick)));
  452. }))), index.h("div", { class: "thumb-container" }, !this.precise && !this.labelHandles && valueIsRange && minHandle, !this.hasHistogram &&
  453. !this.precise &&
  454. this.labelHandles &&
  455. valueIsRange &&
  456. minLabeledHandle, this.precise && !this.labelHandles && valueIsRange && minPreciseHandle, this.precise && this.labelHandles && valueIsRange && minLabeledPreciseHandle, this.hasHistogram &&
  457. !this.precise &&
  458. this.labelHandles &&
  459. valueIsRange &&
  460. 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 &&
  461. this.precise &&
  462. this.labelHandles &&
  463. histogramLabeledPreciseHandle, index.h(form.HiddenFormInputSlot, { component: this })))));
  464. }
  465. renderGraph() {
  466. return this.histogram ? (index.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;
  467. }
  468. renderTickLabel(tick) {
  469. const valueIsRange = isRange(this.value);
  470. const isMinTickLabel = tick === this.min;
  471. const isMaxTickLabel = tick === this.max;
  472. const tickLabel = (index.h("span", { class: {
  473. tick__label: true,
  474. "tick__label--min": isMinTickLabel,
  475. "tick__label--max": isMaxTickLabel
  476. } }, tick.toLocaleString()));
  477. if (this.labelTicks && !this.hasHistogram && !valueIsRange) {
  478. return tickLabel;
  479. }
  480. if (this.labelTicks &&
  481. !this.hasHistogram &&
  482. valueIsRange &&
  483. !this.precise &&
  484. !this.labelHandles) {
  485. return tickLabel;
  486. }
  487. if (this.labelTicks &&
  488. !this.hasHistogram &&
  489. valueIsRange &&
  490. !this.precise &&
  491. this.labelHandles) {
  492. return tickLabel;
  493. }
  494. if (this.labelTicks &&
  495. !this.hasHistogram &&
  496. valueIsRange &&
  497. this.precise &&
  498. (isMinTickLabel || isMaxTickLabel)) {
  499. return tickLabel;
  500. }
  501. if (this.labelTicks && this.hasHistogram && !this.precise && !this.labelHandles) {
  502. return tickLabel;
  503. }
  504. if (this.labelTicks &&
  505. this.hasHistogram &&
  506. this.precise &&
  507. !this.labelHandles &&
  508. (isMinTickLabel || isMaxTickLabel)) {
  509. return tickLabel;
  510. }
  511. if (this.labelTicks &&
  512. this.hasHistogram &&
  513. !this.precise &&
  514. this.labelHandles &&
  515. (isMinTickLabel || isMaxTickLabel)) {
  516. return tickLabel;
  517. }
  518. if (this.labelTicks &&
  519. this.hasHistogram &&
  520. this.precise &&
  521. this.labelHandles &&
  522. (isMinTickLabel || isMaxTickLabel)) {
  523. return tickLabel;
  524. }
  525. return null;
  526. }
  527. //--------------------------------------------------------------------------
  528. //
  529. // Event Listeners
  530. //
  531. //--------------------------------------------------------------------------
  532. keyDownHandler(event) {
  533. const mirror = this.shouldMirror();
  534. const { activeProp, max, min, pageStep, step } = this;
  535. const value = this[activeProp];
  536. const key = event.key;
  537. if (key === "Enter" || key === " ") {
  538. event.preventDefault();
  539. return;
  540. }
  541. let adjustment;
  542. if (key === "ArrowUp" || key === "ArrowRight") {
  543. const directionFactor = mirror && key === "ArrowRight" ? -1 : 1;
  544. adjustment = value + step * directionFactor;
  545. }
  546. else if (key === "ArrowDown" || key === "ArrowLeft") {
  547. const directionFactor = mirror && key === "ArrowLeft" ? -1 : 1;
  548. adjustment = value - step * directionFactor;
  549. }
  550. else if (key === "PageUp") {
  551. if (pageStep) {
  552. adjustment = value + pageStep;
  553. }
  554. }
  555. else if (key === "PageDown") {
  556. if (pageStep) {
  557. adjustment = value - pageStep;
  558. }
  559. }
  560. else if (key === "Home") {
  561. adjustment = min;
  562. }
  563. else if (key === "End") {
  564. adjustment = max;
  565. }
  566. if (isNaN(adjustment)) {
  567. return;
  568. }
  569. event.preventDefault();
  570. const fixedDecimalAdjustment = Number(adjustment.toFixed(math.decimalPlaces(step)));
  571. this.setValue(activeProp, this.clamp(fixedDecimalAdjustment, activeProp));
  572. }
  573. clickHandler(event) {
  574. this.focusActiveHandle(event.clientX);
  575. }
  576. pointerDownHandler(event) {
  577. const x = event.clientX || event.pageX;
  578. const position = this.translate(x);
  579. let prop = "value";
  580. if (isRange(this.value)) {
  581. const inRange = position >= this.minValue && position <= this.maxValue;
  582. if (inRange && this.lastDragProp === "minMaxValue") {
  583. prop = "minMaxValue";
  584. }
  585. else {
  586. const closerToMax = Math.abs(this.maxValue - position) < Math.abs(this.minValue - position);
  587. prop = closerToMax || position > this.maxValue ? "maxValue" : "minValue";
  588. }
  589. }
  590. this.lastDragPropValue = this[prop];
  591. this.dragStart(prop);
  592. const isThumbActive = this.el.shadowRoot.querySelector(".thumb:active");
  593. if (!isThumbActive) {
  594. this.setValue(prop, this.clamp(position, prop));
  595. }
  596. }
  597. handleTouchStart(event) {
  598. // needed to prevent extra click at the end of a handle drag
  599. event.preventDefault();
  600. }
  601. //--------------------------------------------------------------------------
  602. //
  603. // Public Methods
  604. //
  605. //--------------------------------------------------------------------------
  606. /** Sets focus on the component. */
  607. async setFocus() {
  608. const handle = this.minHandle ? this.minHandle : this.maxHandle;
  609. handle.focus();
  610. }
  611. //--------------------------------------------------------------------------
  612. //
  613. // Private Methods
  614. //
  615. //--------------------------------------------------------------------------
  616. setValueFromMinMax() {
  617. const { minValue, maxValue } = this;
  618. if (typeof minValue === "number" && typeof maxValue === "number") {
  619. this.value = [minValue, maxValue];
  620. }
  621. }
  622. setMinMaxFromValue() {
  623. const { value } = this;
  624. if (isRange(value)) {
  625. this.minValue = value[0];
  626. this.maxValue = value[1];
  627. }
  628. }
  629. onLabelClick() {
  630. this.setFocus();
  631. }
  632. shouldMirror() {
  633. return this.mirrored && !this.hasHistogram;
  634. }
  635. shouldUseMinValue() {
  636. if (!isRange(this.value)) {
  637. return false;
  638. }
  639. return ((this.hasHistogram && this.maxValue === 0) || (!this.hasHistogram && this.minValue === 0));
  640. }
  641. generateTickValues() {
  642. const ticks = [];
  643. let current = this.min;
  644. while (this.ticks && current < this.max + this.ticks) {
  645. ticks.push(Math.min(current, this.max));
  646. current = current + this.ticks;
  647. }
  648. return ticks;
  649. }
  650. dragStart(prop) {
  651. this.dragProp = prop;
  652. this.lastDragProp = this.dragProp;
  653. this.activeProp = prop;
  654. document.addEventListener("pointermove", this.dragUpdate);
  655. document.addEventListener("pointerup", this.dragEnd);
  656. document.addEventListener("pointercancel", this.dragEnd);
  657. }
  658. focusActiveHandle(valueX) {
  659. switch (this.dragProp) {
  660. case "minValue":
  661. this.minHandle.focus();
  662. break;
  663. case "maxValue":
  664. this.maxHandle.focus();
  665. break;
  666. case "minMaxValue":
  667. this.getClosestHandle(valueX).focus();
  668. break;
  669. }
  670. }
  671. emitInput() {
  672. this.calciteSliderInput.emit();
  673. this.calciteSliderUpdate.emit();
  674. }
  675. emitChange() {
  676. this.calciteSliderChange.emit();
  677. }
  678. removeDragListeners() {
  679. document.removeEventListener("pointermove", this.dragUpdate);
  680. document.removeEventListener("pointerup", this.dragEnd);
  681. document.removeEventListener("pointercancel", this.dragEnd);
  682. }
  683. /**
  684. * Set the prop value if changed at the component level
  685. * @param valueProp
  686. * @param value
  687. */
  688. setValue(valueProp, value) {
  689. const oldValue = this[valueProp];
  690. const valueChanged = oldValue !== value;
  691. if (!valueChanged) {
  692. return;
  693. }
  694. this[valueProp] = value;
  695. const dragging = this.dragProp;
  696. if (!dragging) {
  697. this.emitChange();
  698. }
  699. this.emitInput();
  700. }
  701. /**
  702. * If number is outside range, constrain to min or max
  703. * @internal
  704. */
  705. clamp(value, prop) {
  706. value = math.clamp(value, this.min, this.max);
  707. // ensure that maxValue and minValue don't swap positions
  708. if (prop === "maxValue") {
  709. value = Math.max(value, this.minValue);
  710. }
  711. if (prop === "minValue") {
  712. value = Math.min(value, this.maxValue);
  713. }
  714. return value;
  715. }
  716. /**
  717. * Translate a pixel position to value along the range
  718. * @internal
  719. */
  720. translate(x) {
  721. const range = this.max - this.min;
  722. const { left, width } = this.trackEl.getBoundingClientRect();
  723. const percent = (x - left) / width;
  724. const mirror = this.shouldMirror();
  725. const clampedValue = this.clamp(this.min + range * (mirror ? 1 - percent : percent));
  726. let value = Number(clampedValue.toFixed(math.decimalPlaces(this.step)));
  727. if (this.snap && this.step) {
  728. value = this.getClosestStep(value);
  729. }
  730. return value;
  731. }
  732. /**
  733. * Get closest allowed value along stepped values
  734. * @internal
  735. */
  736. getClosestStep(num) {
  737. num = Number(this.clamp(num).toFixed(math.decimalPlaces(this.step)));
  738. if (this.step) {
  739. const step = Math.round(num / this.step) * this.step;
  740. num = Number(this.clamp(step).toFixed(math.decimalPlaces(this.step)));
  741. }
  742. return num;
  743. }
  744. getClosestHandle(valueX) {
  745. return this.getDistanceX(this.maxHandle, valueX) > this.getDistanceX(this.minHandle, valueX)
  746. ? this.minHandle
  747. : this.maxHandle;
  748. }
  749. getDistanceX(el, valueX) {
  750. return Math.abs(el.getBoundingClientRect().left - valueX);
  751. }
  752. getFontSizeForElement(element) {
  753. return Number(window.getComputedStyle(element).getPropertyValue("font-size").match(/\d+/)[0]);
  754. }
  755. /**
  756. * Get position of value along range as fractional value
  757. * @return {number} number in the unit interval [0,1]
  758. * @internal
  759. */
  760. getUnitInterval(num) {
  761. num = this.clamp(num);
  762. const range = this.max - this.min;
  763. return (num - this.min) / range;
  764. }
  765. adjustHostObscuredHandleLabel(name) {
  766. const label = this.el.shadowRoot.querySelector(`.handle__label--${name}`);
  767. const labelStatic = this.el.shadowRoot.querySelector(`.handle__label--${name}.static`);
  768. const labelTransformed = this.el.shadowRoot.querySelector(`.handle__label--${name}.transformed`);
  769. const labelStaticBounds = labelStatic.getBoundingClientRect();
  770. const labelStaticOffset = this.getHostOffset(labelStaticBounds.left, labelStaticBounds.right);
  771. label.style.transform = `translateX(${labelStaticOffset}px)`;
  772. labelTransformed.style.transform = `translateX(${labelStaticOffset}px)`;
  773. }
  774. hyphenateCollidingRangeHandleLabels() {
  775. const { shadowRoot } = this.el;
  776. const mirror = this.shouldMirror();
  777. const leftModifier = mirror ? "value" : "minValue";
  778. const rightModifier = mirror ? "minValue" : "value";
  779. const leftValueLabel = shadowRoot.querySelector(`.handle__label--${leftModifier}`);
  780. const leftValueLabelStatic = shadowRoot.querySelector(`.handle__label--${leftModifier}.static`);
  781. const leftValueLabelTransformed = shadowRoot.querySelector(`.handle__label--${leftModifier}.transformed`);
  782. const leftValueLabelStaticHostOffset = this.getHostOffset(leftValueLabelStatic.getBoundingClientRect().left, leftValueLabelStatic.getBoundingClientRect().right);
  783. const rightValueLabel = shadowRoot.querySelector(`.handle__label--${rightModifier}`);
  784. const rightValueLabelStatic = shadowRoot.querySelector(`.handle__label--${rightModifier}.static`);
  785. const rightValueLabelTransformed = shadowRoot.querySelector(`.handle__label--${rightModifier}.transformed`);
  786. const rightValueLabelStaticHostOffset = this.getHostOffset(rightValueLabelStatic.getBoundingClientRect().left, rightValueLabelStatic.getBoundingClientRect().right);
  787. const labelFontSize = this.getFontSizeForElement(leftValueLabel);
  788. const labelTransformedOverlap = this.getRangeLabelOverlap(leftValueLabelTransformed, rightValueLabelTransformed);
  789. const hyphenLabel = leftValueLabel;
  790. const labelOffset = labelFontSize / 2;
  791. if (labelTransformedOverlap > 0) {
  792. hyphenLabel.classList.add("hyphen", "hyphen--wrap");
  793. if (rightValueLabelStaticHostOffset === 0 && leftValueLabelStaticHostOffset === 0) {
  794. // Neither handle overlaps the host boundary
  795. let leftValueLabelTranslate = labelTransformedOverlap / 2 - labelOffset;
  796. leftValueLabelTranslate =
  797. Math.sign(leftValueLabelTranslate) === -1
  798. ? Math.abs(leftValueLabelTranslate)
  799. : -leftValueLabelTranslate;
  800. const leftValueLabelTransformedHostOffset = this.getHostOffset(leftValueLabelTransformed.getBoundingClientRect().left +
  801. leftValueLabelTranslate -
  802. labelOffset, leftValueLabelTransformed.getBoundingClientRect().right +
  803. leftValueLabelTranslate -
  804. labelOffset);
  805. let rightValueLabelTranslate = labelTransformedOverlap / 2;
  806. const rightValueLabelTransformedHostOffset = this.getHostOffset(rightValueLabelTransformed.getBoundingClientRect().left + rightValueLabelTranslate, rightValueLabelTransformed.getBoundingClientRect().right + rightValueLabelTranslate);
  807. if (leftValueLabelTransformedHostOffset !== 0) {
  808. leftValueLabelTranslate += leftValueLabelTransformedHostOffset;
  809. rightValueLabelTranslate += leftValueLabelTransformedHostOffset;
  810. }
  811. if (rightValueLabelTransformedHostOffset !== 0) {
  812. leftValueLabelTranslate += rightValueLabelTransformedHostOffset;
  813. rightValueLabelTranslate += rightValueLabelTransformedHostOffset;
  814. }
  815. leftValueLabel.style.transform = `translateX(${leftValueLabelTranslate}px)`;
  816. leftValueLabelTransformed.style.transform = `translateX(${leftValueLabelTranslate - labelOffset}px)`;
  817. rightValueLabel.style.transform = `translateX(${rightValueLabelTranslate}px)`;
  818. rightValueLabelTransformed.style.transform = `translateX(${rightValueLabelTranslate}px)`;
  819. }
  820. else if (leftValueLabelStaticHostOffset > 0 || rightValueLabelStaticHostOffset > 0) {
  821. // labels overlap host boundary on the left side
  822. leftValueLabel.style.transform = `translateX(${leftValueLabelStaticHostOffset + labelOffset}px)`;
  823. rightValueLabel.style.transform = `translateX(${labelTransformedOverlap + rightValueLabelStaticHostOffset}px)`;
  824. rightValueLabelTransformed.style.transform = `translateX(${labelTransformedOverlap + rightValueLabelStaticHostOffset}px)`;
  825. }
  826. else if (leftValueLabelStaticHostOffset < 0 || rightValueLabelStaticHostOffset < 0) {
  827. // labels overlap host boundary on the right side
  828. let leftValueLabelTranslate = Math.abs(leftValueLabelStaticHostOffset) + labelTransformedOverlap - labelOffset;
  829. leftValueLabelTranslate =
  830. Math.sign(leftValueLabelTranslate) === -1
  831. ? Math.abs(leftValueLabelTranslate)
  832. : -leftValueLabelTranslate;
  833. leftValueLabel.style.transform = `translateX(${leftValueLabelTranslate}px)`;
  834. leftValueLabelTransformed.style.transform = `translateX(${leftValueLabelTranslate - labelOffset}px)`;
  835. }
  836. }
  837. else {
  838. hyphenLabel.classList.remove("hyphen", "hyphen--wrap");
  839. leftValueLabel.style.transform = `translateX(${leftValueLabelStaticHostOffset}px)`;
  840. leftValueLabelTransformed.style.transform = `translateX(${leftValueLabelStaticHostOffset}px)`;
  841. rightValueLabel.style.transform = `translateX(${rightValueLabelStaticHostOffset}px)`;
  842. rightValueLabelTransformed.style.transform = `translateX(${rightValueLabelStaticHostOffset}px)`;
  843. }
  844. }
  845. /**
  846. * Hides bounding tick labels that are obscured by either handle.
  847. */
  848. hideObscuredBoundingTickLabels() {
  849. const valueIsRange = isRange(this.value);
  850. if (!this.hasHistogram && !valueIsRange && !this.labelHandles && !this.precise) {
  851. return;
  852. }
  853. if (!this.hasHistogram && !valueIsRange && this.labelHandles && !this.precise) {
  854. return;
  855. }
  856. if (!this.hasHistogram && !valueIsRange && !this.labelHandles && this.precise) {
  857. return;
  858. }
  859. if (!this.hasHistogram && !valueIsRange && this.labelHandles && this.precise) {
  860. return;
  861. }
  862. if (!this.hasHistogram && valueIsRange && !this.precise) {
  863. return;
  864. }
  865. if (this.hasHistogram && !this.precise && !this.labelHandles) {
  866. return;
  867. }
  868. const minHandle = this.el.shadowRoot.querySelector(".thumb--minValue");
  869. const maxHandle = this.el.shadowRoot.querySelector(".thumb--value");
  870. const minTickLabel = this.el.shadowRoot.querySelector(".tick__label--min");
  871. const maxTickLabel = this.el.shadowRoot.querySelector(".tick__label--max");
  872. if (!minHandle && maxHandle && minTickLabel && maxTickLabel) {
  873. minTickLabel.style.opacity = this.isMinTickLabelObscured(minTickLabel, maxHandle) ? "0" : "1";
  874. maxTickLabel.style.opacity = this.isMaxTickLabelObscured(maxTickLabel, maxHandle) ? "0" : "1";
  875. }
  876. if (minHandle && maxHandle && minTickLabel && maxTickLabel) {
  877. minTickLabel.style.opacity =
  878. this.isMinTickLabelObscured(minTickLabel, minHandle) ||
  879. this.isMinTickLabelObscured(minTickLabel, maxHandle)
  880. ? "0"
  881. : "1";
  882. maxTickLabel.style.opacity =
  883. this.isMaxTickLabelObscured(maxTickLabel, minHandle) ||
  884. (this.isMaxTickLabelObscured(maxTickLabel, maxHandle) && this.hasHistogram)
  885. ? "0"
  886. : "1";
  887. }
  888. }
  889. /**
  890. * Returns an integer representing the number of pixels to offset on the left or right side based on desired position behavior.
  891. * @internal
  892. */
  893. getHostOffset(leftBounds, rightBounds) {
  894. const hostBounds = this.el.getBoundingClientRect();
  895. const buffer = 7;
  896. if (leftBounds + buffer < hostBounds.left) {
  897. return hostBounds.left - leftBounds - buffer;
  898. }
  899. if (rightBounds - buffer > hostBounds.right) {
  900. return -(rightBounds - hostBounds.right) + buffer;
  901. }
  902. return 0;
  903. }
  904. /**
  905. * Returns an integer representing the number of pixels that the two given span elements are overlapping, taking into account
  906. * 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.
  907. * @param leftLabel
  908. * @param rightLabel
  909. */
  910. getRangeLabelOverlap(leftLabel, rightLabel) {
  911. const leftLabelBounds = leftLabel.getBoundingClientRect();
  912. const rightLabelBounds = rightLabel.getBoundingClientRect();
  913. const leftLabelFontSize = this.getFontSizeForElement(leftLabel);
  914. const rangeLabelOverlap = leftLabelBounds.right + leftLabelFontSize - rightLabelBounds.left;
  915. return Math.max(rangeLabelOverlap, 0);
  916. }
  917. /**
  918. * Returns a boolean value representing if the minLabel span element is obscured (being overlapped) by the given handle div element.
  919. * @param minLabel
  920. * @param handle
  921. */
  922. isMinTickLabelObscured(minLabel, handle) {
  923. const minLabelBounds = minLabel.getBoundingClientRect();
  924. const handleBounds = handle.getBoundingClientRect();
  925. return dom.intersects(minLabelBounds, handleBounds);
  926. }
  927. /**
  928. * Returns a boolean value representing if the maxLabel span element is obscured (being overlapped) by the given handle div element.
  929. * @param maxLabel
  930. * @param handle
  931. */
  932. isMaxTickLabelObscured(maxLabel, handle) {
  933. const maxLabelBounds = maxLabel.getBoundingClientRect();
  934. const handleBounds = handle.getBoundingClientRect();
  935. return dom.intersects(maxLabelBounds, handleBounds);
  936. }
  937. get el() { return index.getElement(this); }
  938. static get watchers() { return {
  939. "histogram": ["histogramWatcher"],
  940. "value": ["valueHandler"],
  941. "minValue": ["minMaxValueHandler"],
  942. "maxValue": ["minMaxValueHandler"]
  943. }; }
  944. };
  945. Slider.style = sliderCss;
  946. exports.calcite_graph = Graph;
  947. exports.calcite_slider = Slider;