component.js 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316
  1. /**
  2. * @license
  3. * Copyright 2020 Google Inc.
  4. *
  5. * Permission is hereby granted, free of charge, to any person obtaining a copy
  6. * of this software and associated documentation files (the "Software"), to deal
  7. * in the Software without restriction, including without limitation the rights
  8. * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  9. * copies of the Software, and to permit persons to whom the Software is
  10. * furnished to do so, subject to the following conditions:
  11. *
  12. * The above copyright notice and this permission notice shall be included in
  13. * all copies or substantial portions of the Software.
  14. *
  15. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  16. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  17. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  18. * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  19. * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  20. * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  21. * THE SOFTWARE.
  22. */
  23. import { __assign, __extends } from "tslib";
  24. import { MDCComponent } from '@material/base/component';
  25. import { applyPassive } from '@material/dom/events';
  26. import { matches } from '@material/dom/ponyfill';
  27. import { MDCRipple } from '@material/ripple/component';
  28. import { MDCRippleFoundation } from '@material/ripple/foundation';
  29. import { cssClasses, events } from './constants';
  30. import { MDCSliderFoundation } from './foundation';
  31. import { Thumb, TickMark } from './types';
  32. /** Vanilla implementation of slider component. */
  33. var MDCSlider = /** @class */ (function (_super) {
  34. __extends(MDCSlider, _super);
  35. function MDCSlider() {
  36. var _this = _super !== null && _super.apply(this, arguments) || this;
  37. _this.skipInitialUIUpdate = false;
  38. // Function that maps a slider value to the value of the `aria-valuetext`
  39. // attribute on the thumb element.
  40. _this.valueToAriaValueTextFn = null;
  41. return _this;
  42. }
  43. MDCSlider.attachTo = function (root, options) {
  44. if (options === void 0) { options = {}; }
  45. return new MDCSlider(root, undefined, options);
  46. };
  47. MDCSlider.prototype.getDefaultFoundation = function () {
  48. var _this = this;
  49. // tslint:disable:object-literal-sort-keys Methods should be in the same
  50. // order as the adapter interface.
  51. var adapter = {
  52. hasClass: function (className) { return _this.root.classList.contains(className); },
  53. addClass: function (className) {
  54. _this.root.classList.add(className);
  55. },
  56. removeClass: function (className) {
  57. _this.root.classList.remove(className);
  58. },
  59. addThumbClass: function (className, thumb) {
  60. _this.getThumbEl(thumb).classList.add(className);
  61. },
  62. removeThumbClass: function (className, thumb) {
  63. _this.getThumbEl(thumb).classList.remove(className);
  64. },
  65. getAttribute: function (attribute) { return _this.root.getAttribute(attribute); },
  66. getInputValue: function (thumb) { return _this.getInput(thumb).value; },
  67. setInputValue: function (value, thumb) {
  68. _this.getInput(thumb).value = value;
  69. },
  70. getInputAttribute: function (attribute, thumb) {
  71. return _this.getInput(thumb).getAttribute(attribute);
  72. },
  73. setInputAttribute: function (attribute, value, thumb) {
  74. _this.getInput(thumb).setAttribute(attribute, value);
  75. },
  76. removeInputAttribute: function (attribute, thumb) {
  77. _this.getInput(thumb).removeAttribute(attribute);
  78. },
  79. focusInput: function (thumb) {
  80. _this.getInput(thumb).focus();
  81. },
  82. isInputFocused: function (thumb) {
  83. return _this.getInput(thumb) === document.activeElement;
  84. },
  85. shouldHideFocusStylesForPointerEvents: function () { return false; },
  86. getThumbKnobWidth: function (thumb) {
  87. return _this.getThumbEl(thumb)
  88. .querySelector("." + cssClasses.THUMB_KNOB)
  89. .getBoundingClientRect()
  90. .width;
  91. },
  92. getThumbBoundingClientRect: function (thumb) {
  93. return _this.getThumbEl(thumb).getBoundingClientRect();
  94. },
  95. getBoundingClientRect: function () { return _this.root.getBoundingClientRect(); },
  96. getValueIndicatorContainerWidth: function (thumb) {
  97. return _this.getThumbEl(thumb)
  98. .querySelector("." + cssClasses.VALUE_INDICATOR_CONTAINER)
  99. .getBoundingClientRect()
  100. .width;
  101. },
  102. isRTL: function () { return getComputedStyle(_this.root).direction === 'rtl'; },
  103. setThumbStyleProperty: function (propertyName, value, thumb) {
  104. _this.getThumbEl(thumb).style.setProperty(propertyName, value);
  105. },
  106. removeThumbStyleProperty: function (propertyName, thumb) {
  107. _this.getThumbEl(thumb).style.removeProperty(propertyName);
  108. },
  109. setTrackActiveStyleProperty: function (propertyName, value) {
  110. _this.trackActive.style.setProperty(propertyName, value);
  111. },
  112. removeTrackActiveStyleProperty: function (propertyName) {
  113. _this.trackActive.style.removeProperty(propertyName);
  114. },
  115. setValueIndicatorText: function (value, thumb) {
  116. var valueIndicatorEl = _this.getThumbEl(thumb).querySelector("." + cssClasses.VALUE_INDICATOR_TEXT);
  117. valueIndicatorEl.textContent = String(value);
  118. },
  119. getValueToAriaValueTextFn: function () { return _this.valueToAriaValueTextFn; },
  120. updateTickMarks: function (tickMarks) {
  121. var tickMarksContainer = _this.root.querySelector("." + cssClasses.TICK_MARKS_CONTAINER);
  122. if (!tickMarksContainer) {
  123. tickMarksContainer = document.createElement('div');
  124. tickMarksContainer.classList.add(cssClasses.TICK_MARKS_CONTAINER);
  125. var track = _this.root.querySelector("." + cssClasses.TRACK);
  126. track.appendChild(tickMarksContainer);
  127. }
  128. if (tickMarks.length !== tickMarksContainer.children.length) {
  129. while (tickMarksContainer.firstChild) {
  130. tickMarksContainer.removeChild(tickMarksContainer.firstChild);
  131. }
  132. _this.addTickMarks(tickMarksContainer, tickMarks);
  133. }
  134. else {
  135. _this.updateTickMarks(tickMarksContainer, tickMarks);
  136. }
  137. },
  138. setPointerCapture: function (pointerId) {
  139. _this.root.setPointerCapture(pointerId);
  140. },
  141. emitChangeEvent: function (value, thumb) {
  142. _this.emit(events.CHANGE, { value: value, thumb: thumb });
  143. },
  144. emitInputEvent: function (value, thumb) {
  145. _this.emit(events.INPUT, { value: value, thumb: thumb });
  146. },
  147. // tslint:disable-next-line:enforce-name-casing
  148. emitDragStartEvent: function (_, thumb) {
  149. // Emitting event is not yet implemented. See issue:
  150. // https://github.com/material-components/material-components-web/issues/6448
  151. _this.getRipple(thumb).activate();
  152. },
  153. // tslint:disable-next-line:enforce-name-casing
  154. emitDragEndEvent: function (_, thumb) {
  155. // Emitting event is not yet implemented. See issue:
  156. // https://github.com/material-components/material-components-web/issues/6448
  157. _this.getRipple(thumb).deactivate();
  158. },
  159. registerEventHandler: function (evtType, handler) {
  160. _this.listen(evtType, handler);
  161. },
  162. deregisterEventHandler: function (evtType, handler) {
  163. _this.unlisten(evtType, handler);
  164. },
  165. registerThumbEventHandler: function (thumb, evtType, handler) {
  166. _this.getThumbEl(thumb).addEventListener(evtType, handler);
  167. },
  168. deregisterThumbEventHandler: function (thumb, evtType, handler) {
  169. _this.getThumbEl(thumb).removeEventListener(evtType, handler);
  170. },
  171. registerInputEventHandler: function (thumb, evtType, handler) {
  172. _this.getInput(thumb).addEventListener(evtType, handler);
  173. },
  174. deregisterInputEventHandler: function (thumb, evtType, handler) {
  175. _this.getInput(thumb).removeEventListener(evtType, handler);
  176. },
  177. registerBodyEventHandler: function (evtType, handler) {
  178. document.body.addEventListener(evtType, handler);
  179. },
  180. deregisterBodyEventHandler: function (evtType, handler) {
  181. document.body.removeEventListener(evtType, handler);
  182. },
  183. registerWindowEventHandler: function (evtType, handler) {
  184. window.addEventListener(evtType, handler);
  185. },
  186. deregisterWindowEventHandler: function (evtType, handler) {
  187. window.removeEventListener(evtType, handler);
  188. },
  189. // tslint:enable:object-literal-sort-keys
  190. };
  191. return new MDCSliderFoundation(adapter);
  192. };
  193. /**
  194. * Initializes component, with the following options:
  195. * - `skipInitialUIUpdate`: Whether to skip updating the UI when initially
  196. * syncing with the DOM. This should be enabled when the slider position
  197. * is set before component initialization.
  198. */
  199. MDCSlider.prototype.initialize = function (_a) {
  200. var _b = _a === void 0 ? {} : _a, skipInitialUIUpdate = _b.skipInitialUIUpdate;
  201. this.inputs = Array.from(this.root.querySelectorAll("." + cssClasses.INPUT));
  202. this.thumbs = Array.from(this.root.querySelectorAll("." + cssClasses.THUMB));
  203. this.trackActive =
  204. this.root.querySelector("." + cssClasses.TRACK_ACTIVE);
  205. this.ripples = this.createRipples();
  206. if (skipInitialUIUpdate) {
  207. this.skipInitialUIUpdate = true;
  208. }
  209. };
  210. MDCSlider.prototype.initialSyncWithDOM = function () {
  211. this.foundation.layout({ skipUpdateUI: this.skipInitialUIUpdate });
  212. };
  213. /** Redraws UI based on DOM (e.g. element dimensions, RTL). */
  214. MDCSlider.prototype.layout = function () {
  215. this.foundation.layout();
  216. };
  217. MDCSlider.prototype.getValueStart = function () {
  218. return this.foundation.getValueStart();
  219. };
  220. MDCSlider.prototype.setValueStart = function (valueStart) {
  221. this.foundation.setValueStart(valueStart);
  222. };
  223. MDCSlider.prototype.getValue = function () {
  224. return this.foundation.getValue();
  225. };
  226. MDCSlider.prototype.setValue = function (value) {
  227. this.foundation.setValue(value);
  228. };
  229. /** @return Slider disabled state. */
  230. MDCSlider.prototype.getDisabled = function () {
  231. return this.foundation.getDisabled();
  232. };
  233. /** Sets slider disabled state. */
  234. MDCSlider.prototype.setDisabled = function (disabled) {
  235. this.foundation.setDisabled(disabled);
  236. };
  237. /**
  238. * Sets a function that maps the slider value to the value of the
  239. * `aria-valuetext` attribute on the thumb element.
  240. */
  241. MDCSlider.prototype.setValueToAriaValueTextFn = function (mapFn) {
  242. this.valueToAriaValueTextFn = mapFn;
  243. };
  244. MDCSlider.prototype.getThumbEl = function (thumb) {
  245. return thumb === Thumb.END ? this.thumbs[this.thumbs.length - 1] :
  246. this.thumbs[0];
  247. };
  248. MDCSlider.prototype.getInput = function (thumb) {
  249. return thumb === Thumb.END ? this.inputs[this.inputs.length - 1] :
  250. this.inputs[0];
  251. };
  252. MDCSlider.prototype.getRipple = function (thumb) {
  253. return thumb === Thumb.END ? this.ripples[this.ripples.length - 1] :
  254. this.ripples[0];
  255. };
  256. /** Adds tick mark elements to the given container. */
  257. MDCSlider.prototype.addTickMarks = function (tickMarkContainer, tickMarks) {
  258. var fragment = document.createDocumentFragment();
  259. for (var i = 0; i < tickMarks.length; i++) {
  260. var div = document.createElement('div');
  261. var tickMarkClass = tickMarks[i] === TickMark.ACTIVE ?
  262. cssClasses.TICK_MARK_ACTIVE :
  263. cssClasses.TICK_MARK_INACTIVE;
  264. div.classList.add(tickMarkClass);
  265. fragment.appendChild(div);
  266. }
  267. tickMarkContainer.appendChild(fragment);
  268. };
  269. /** Updates tick mark elements' classes in the given container. */
  270. MDCSlider.prototype.updateTickMarks = function (tickMarkContainer, tickMarks) {
  271. var tickMarkEls = Array.from(tickMarkContainer.children);
  272. for (var i = 0; i < tickMarkEls.length; i++) {
  273. if (tickMarks[i] === TickMark.ACTIVE) {
  274. tickMarkEls[i].classList.add(cssClasses.TICK_MARK_ACTIVE);
  275. tickMarkEls[i].classList.remove(cssClasses.TICK_MARK_INACTIVE);
  276. }
  277. else {
  278. tickMarkEls[i].classList.add(cssClasses.TICK_MARK_INACTIVE);
  279. tickMarkEls[i].classList.remove(cssClasses.TICK_MARK_ACTIVE);
  280. }
  281. }
  282. };
  283. /** Initializes thumb ripples. */
  284. MDCSlider.prototype.createRipples = function () {
  285. var ripples = [];
  286. var rippleSurfaces = Array.from(this.root.querySelectorAll("." + cssClasses.THUMB));
  287. var _loop_1 = function (i) {
  288. var rippleSurface = rippleSurfaces[i];
  289. // Use the corresponding input as the focus source for the ripple (i.e.
  290. // when the input is focused, the ripple is in the focused state).
  291. var input = this_1.inputs[i];
  292. var adapter = __assign(__assign({}, MDCRipple.createAdapter(this_1)), { addClass: function (className) {
  293. rippleSurface.classList.add(className);
  294. }, computeBoundingRect: function () { return rippleSurface.getBoundingClientRect(); }, deregisterInteractionHandler: function (evtType, handler) {
  295. input.removeEventListener(evtType, handler);
  296. }, isSurfaceActive: function () { return matches(input, ':active'); }, isUnbounded: function () { return true; }, registerInteractionHandler: function (evtType, handler) {
  297. input.addEventListener(evtType, handler, applyPassive());
  298. }, removeClass: function (className) {
  299. rippleSurface.classList.remove(className);
  300. }, updateCssVariable: function (varName, value) {
  301. rippleSurface.style.setProperty(varName, value);
  302. } });
  303. var ripple = new MDCRipple(rippleSurface, new MDCRippleFoundation(adapter));
  304. ripple.unbounded = true;
  305. ripples.push(ripple);
  306. };
  307. var this_1 = this;
  308. for (var i = 0; i < rippleSurfaces.length; i++) {
  309. _loop_1(i);
  310. }
  311. return ripples;
  312. };
  313. return MDCSlider;
  314. }(MDCComponent));
  315. export { MDCSlider };
  316. //# sourceMappingURL=component.js.map