class-map.js 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990
  1. /**
  2. * @license
  3. * Copyright 2018 Google LLC
  4. * SPDX-License-Identifier: BSD-3-Clause
  5. */
  6. import { noChange } from '../lit-html.js';
  7. import { directive, Directive, PartType, } from '../directive.js';
  8. class ClassMapDirective extends Directive {
  9. constructor(partInfo) {
  10. var _a;
  11. super(partInfo);
  12. if (partInfo.type !== PartType.ATTRIBUTE ||
  13. partInfo.name !== 'class' ||
  14. ((_a = partInfo.strings) === null || _a === void 0 ? void 0 : _a.length) > 2) {
  15. throw new Error('`classMap()` can only be used in the `class` attribute ' +
  16. 'and must be the only part in the attribute.');
  17. }
  18. }
  19. render(classInfo) {
  20. // Add spaces to ensure separation from static classes
  21. return (' ' +
  22. Object.keys(classInfo)
  23. .filter((key) => classInfo[key])
  24. .join(' ') +
  25. ' ');
  26. }
  27. update(part, [classInfo]) {
  28. var _a, _b;
  29. // Remember dynamic classes on the first render
  30. if (this._previousClasses === undefined) {
  31. this._previousClasses = new Set();
  32. if (part.strings !== undefined) {
  33. this._staticClasses = new Set(part.strings
  34. .join(' ')
  35. .split(/\s/)
  36. .filter((s) => s !== ''));
  37. }
  38. for (const name in classInfo) {
  39. if (classInfo[name] && !((_a = this._staticClasses) === null || _a === void 0 ? void 0 : _a.has(name))) {
  40. this._previousClasses.add(name);
  41. }
  42. }
  43. return this.render(classInfo);
  44. }
  45. const classList = part.element.classList;
  46. // Remove old classes that no longer apply
  47. // We use forEach() instead of for-of so that we don't require down-level
  48. // iteration.
  49. this._previousClasses.forEach((name) => {
  50. if (!(name in classInfo)) {
  51. classList.remove(name);
  52. this._previousClasses.delete(name);
  53. }
  54. });
  55. // Add or remove classes based on their classMap value
  56. for (const name in classInfo) {
  57. // We explicitly want a loose truthy check of `value` because it seems
  58. // more convenient that '' and 0 are skipped.
  59. const value = !!classInfo[name];
  60. if (value !== this._previousClasses.has(name) &&
  61. !((_b = this._staticClasses) === null || _b === void 0 ? void 0 : _b.has(name))) {
  62. if (value) {
  63. classList.add(name);
  64. this._previousClasses.add(name);
  65. }
  66. else {
  67. classList.remove(name);
  68. this._previousClasses.delete(name);
  69. }
  70. }
  71. }
  72. return noChange;
  73. }
  74. }
  75. /**
  76. * A directive that applies dynamic CSS classes.
  77. *
  78. * This must be used in the `class` attribute and must be the only part used in
  79. * the attribute. It takes each property in the `classInfo` argument and adds
  80. * the property name to the element's `classList` if the property value is
  81. * truthy; if the property value is falsey, the property name is removed from
  82. * the element's `class`.
  83. *
  84. * For example `{foo: bar}` applies the class `foo` if the value of `bar` is
  85. * truthy.
  86. *
  87. * @param classInfo
  88. */
  89. export const classMap = directive(ClassMapDirective);
  90. //# sourceMappingURL=class-map.js.map