component.js 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141
  1. /**
  2. * @license
  3. * Copyright 2016 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 { __makeTemplateObject, __read, __spreadArray } from "tslib";
  24. import { safeAttrPrefix } from 'safevalues';
  25. import { safeElement } from 'safevalues/dom';
  26. import { MDCFoundation } from './foundation';
  27. function toCamelCase(str) {
  28. // tslint:disable-next-line:enforce-name-casing
  29. return String(str).replace(/\-([a-z])/g, function (_, match) { return match.toUpperCase(); });
  30. }
  31. /** MDC Component base */
  32. var MDCComponent = /** @class */ (function () {
  33. function MDCComponent(root, foundation) {
  34. var args = [];
  35. for (var _i = 2; _i < arguments.length; _i++) {
  36. args[_i - 2] = arguments[_i];
  37. }
  38. this.root = root;
  39. this.initialize.apply(this, __spreadArray([], __read(args)));
  40. // Note that we initialize foundation here and not within the constructor's
  41. // default param so that this.root is defined and can be used within the
  42. // foundation class.
  43. this.foundation =
  44. foundation === undefined ? this.getDefaultFoundation() : foundation;
  45. this.foundation.init();
  46. this.initialSyncWithDOM();
  47. }
  48. MDCComponent.attachTo = function (root) {
  49. // Subclasses which extend MDCBase should provide an attachTo() method that
  50. // takes a root element and returns an instantiated component with its root
  51. // set to that element. Also note that in the cases of subclasses, an
  52. // explicit foundation class will not have to be passed in; it will simply
  53. // be initialized from getDefaultFoundation().
  54. return new MDCComponent(root, new MDCFoundation({}));
  55. };
  56. /* istanbul ignore next: method param only exists for typing purposes; it does
  57. * not need to be unit tested */
  58. // tslint:disable-next-line:enforce-name-casing
  59. MDCComponent.prototype.initialize = function () {
  60. var _args = [];
  61. for (var _i = 0; _i < arguments.length; _i++) {
  62. _args[_i] = arguments[_i];
  63. }
  64. // Subclasses can override this to do any additional setup work that would
  65. // be considered part of a "constructor". Essentially, it is a hook into the
  66. // parent constructor before the foundation is initialized. Any additional
  67. // arguments besides root and foundation will be passed in here.
  68. };
  69. MDCComponent.prototype.getDefaultFoundation = function () {
  70. // Subclasses must override this method to return a properly configured
  71. // foundation class for the component.
  72. throw new Error('Subclasses must override getDefaultFoundation to return a properly configured ' +
  73. 'foundation class');
  74. };
  75. MDCComponent.prototype.initialSyncWithDOM = function () {
  76. // Subclasses should override this method if they need to perform work to
  77. // synchronize with a host DOM object. An example of this would be a form
  78. // control wrapper that needs to synchronize its internal state to some
  79. // property or attribute of the host DOM. Please note: this is *not* the
  80. // place to perform DOM reads/writes that would cause layout / paint, as
  81. // this is called synchronously from within the constructor.
  82. };
  83. MDCComponent.prototype.destroy = function () {
  84. // Subclasses may implement this method to release any resources /
  85. // deregister any listeners they have attached. An example of this might be
  86. // deregistering a resize event from the window object.
  87. this.foundation.destroy();
  88. };
  89. MDCComponent.prototype.listen = function (evtType, handler, options) {
  90. this.root.addEventListener(evtType, handler, options);
  91. };
  92. MDCComponent.prototype.unlisten = function (evtType, handler, options) {
  93. this.root.removeEventListener(evtType, handler, options);
  94. };
  95. /**
  96. * Fires a cross-browser-compatible custom event from the component root of
  97. * the given type, with the given data.
  98. */
  99. MDCComponent.prototype.emit = function (evtType, evtData, shouldBubble) {
  100. if (shouldBubble === void 0) { shouldBubble = false; }
  101. var evt;
  102. if (typeof CustomEvent === 'function') {
  103. evt = new CustomEvent(evtType, {
  104. bubbles: shouldBubble,
  105. detail: evtData,
  106. });
  107. }
  108. else {
  109. evt = document.createEvent('CustomEvent');
  110. evt.initCustomEvent(evtType, shouldBubble, false, evtData);
  111. }
  112. this.root.dispatchEvent(evt);
  113. };
  114. /**
  115. * This is a intermediate fix to allow components to use safevalues. This
  116. * limits setAttribute to setting tabindex, data attributes, and aria
  117. * attributes.
  118. *
  119. * TODO(b/263990206): remove this method and add these directly in each
  120. * component. This will remove this abstraction and make it clear that the
  121. * caller can't set any attribute.
  122. */
  123. MDCComponent.prototype.safeSetAttribute = function (element, attribute, value) {
  124. if (attribute.toLowerCase() === 'tabindex') {
  125. element.tabIndex = Number(value);
  126. }
  127. else if (attribute.indexOf('data-') === 0) {
  128. var dataKey = toCamelCase(attribute.replace(/^data-/, ''));
  129. element.dataset[dataKey] = value;
  130. }
  131. else {
  132. safeElement.setPrefixedAttribute([safeAttrPrefix(templateObject_1 || (templateObject_1 = __makeTemplateObject(["aria-"], ["aria-"]))), safeAttrPrefix(templateObject_2 || (templateObject_2 = __makeTemplateObject(["role"], ["role"])))], element, attribute, value);
  133. }
  134. };
  135. return MDCComponent;
  136. }());
  137. export { MDCComponent };
  138. // tslint:disable-next-line:no-default-export Needed for backward compatibility with MDC Web v0.44.0 and earlier.
  139. export default MDCComponent;
  140. var templateObject_1, templateObject_2;
  141. //# sourceMappingURL=component.js.map