86 lines
3.1 KiB
JavaScript
86 lines
3.1 KiB
JavaScript
|
/**
|
||
|
* @license
|
||
|
* Copyright 2018 Google LLC
|
||
|
* SPDX-License-Identifier: BSD-3-Clause
|
||
|
*/
|
||
|
import { noChange } from '../lit-html.js';
|
||
|
import { directive, Directive, PartType, } from '../directive.js';
|
||
|
class ClassMapDirective extends Directive {
|
||
|
constructor(partInfo) {
|
||
|
super(partInfo);
|
||
|
if (partInfo.type !== PartType.ATTRIBUTE ||
|
||
|
partInfo.name !== 'class' ||
|
||
|
partInfo.strings?.length > 2) {
|
||
|
throw new Error('`classMap()` can only be used in the `class` attribute ' +
|
||
|
'and must be the only part in the attribute.');
|
||
|
}
|
||
|
}
|
||
|
render(classInfo) {
|
||
|
// Add spaces to ensure separation from static classes
|
||
|
return (' ' +
|
||
|
Object.keys(classInfo)
|
||
|
.filter((key) => classInfo[key])
|
||
|
.join(' ') +
|
||
|
' ');
|
||
|
}
|
||
|
update(part, [classInfo]) {
|
||
|
// Remember dynamic classes on the first render
|
||
|
if (this._previousClasses === undefined) {
|
||
|
this._previousClasses = new Set();
|
||
|
if (part.strings !== undefined) {
|
||
|
this._staticClasses = new Set(part.strings
|
||
|
.join(' ')
|
||
|
.split(/\s/)
|
||
|
.filter((s) => s !== ''));
|
||
|
}
|
||
|
for (const name in classInfo) {
|
||
|
if (classInfo[name] && !this._staticClasses?.has(name)) {
|
||
|
this._previousClasses.add(name);
|
||
|
}
|
||
|
}
|
||
|
return this.render(classInfo);
|
||
|
}
|
||
|
const classList = part.element.classList;
|
||
|
// Remove old classes that no longer apply
|
||
|
for (const name of this._previousClasses) {
|
||
|
if (!(name in classInfo)) {
|
||
|
classList.remove(name);
|
||
|
this._previousClasses.delete(name);
|
||
|
}
|
||
|
}
|
||
|
// Add or remove classes based on their classMap value
|
||
|
for (const name in classInfo) {
|
||
|
// We explicitly want a loose truthy check of `value` because it seems
|
||
|
// more convenient that '' and 0 are skipped.
|
||
|
const value = !!classInfo[name];
|
||
|
if (value !== this._previousClasses.has(name) &&
|
||
|
!this._staticClasses?.has(name)) {
|
||
|
if (value) {
|
||
|
classList.add(name);
|
||
|
this._previousClasses.add(name);
|
||
|
}
|
||
|
else {
|
||
|
classList.remove(name);
|
||
|
this._previousClasses.delete(name);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
return noChange;
|
||
|
}
|
||
|
}
|
||
|
/**
|
||
|
* A directive that applies dynamic CSS classes.
|
||
|
*
|
||
|
* This must be used in the `class` attribute and must be the only part used in
|
||
|
* the attribute. It takes each property in the `classInfo` argument and adds
|
||
|
* the property name to the element's `classList` if the property value is
|
||
|
* truthy; if the property value is falsey, the property name is removed from
|
||
|
* the element's `class`.
|
||
|
*
|
||
|
* For example `{foo: bar}` applies the class `foo` if the value of `bar` is
|
||
|
* truthy.
|
||
|
*
|
||
|
* @param classInfo
|
||
|
*/
|
||
|
export const classMap = directive(ClassMapDirective);
|
||
|
//# sourceMappingURL=class-map.js.map
|