Compare commits

..

4 Commits

Author SHA1 Message Date
Lea Verou
2c42521fc3 Update webawesome-element.ts 2025-01-08 10:29:45 -05:00
Lea Verou
9772192d23 Failed attempt to use @bramus/style-observer 2025-01-08 10:24:30 -05:00
Lea Verou
f601b8aaf4 Add dynamic docs since this is not in alpha 2025-01-08 10:23:45 -05:00
Lea Verou
d18edcc941 CSS properties to set component attributes
- Base class abstraction
- Use in `<wa-icon>` (docs excluded from alpha)
2025-01-07 18:25:39 -05:00
5 changed files with 73 additions and 6 deletions

View File

@@ -28,7 +28,8 @@ Many Font Awesome Pro icon families have variants such as `thin`, `light`, `regu
### Setting icon info via CSS
You can set the icon's family, name, and variant via CSS custom properties. This can be useful when you want to set <!-- the icon dynamically or set --> defaults for a group of icons (e.g. icons inside callouts or all icons for a given theme):
You can also set the icon's family, name, and variant via CSS custom properties.
This can be useful when you want to set the icon dynamically (e.g. in response to a CSS pseudo-class or media query) or set defaults for a group of icons (e.g. icons inside callouts or all icons for a given theme).
```html {.example}
<wa-callout>
@@ -46,6 +47,7 @@ You can set the icon's family, name, and variant via CSS custom properties. This
<!-- Look ma, no attributes! -->
<wa-icon slot="icon"></wa-icon>
Here be dragons.
<button id="toggle_icon">Toggle&nbsp;<wa-icon name="circle-exclamation"></wa-icon></button>
</wa-callout>
<style>
@@ -58,6 +60,18 @@ wa-callout {
}
}
</style>
<script>
toggle_icon.addEventListener('click', e => {
let callout = e.target.closest('wa-callout');
let value = callout.style.getPropertyValue('--wa-icon-name').trim();
if (value) {
callout.style.removeProperty('--wa-icon-name');
}
else {
callout.style.setProperty('--wa-icon-name', 'circle-exclamation');
}
});
</script>
```
Notes:

10
package-lock.json generated
View File

@@ -9,6 +9,7 @@
"version": "3.0.0-alpha.7",
"license": "MIT",
"dependencies": {
"@bramus/style-observer": "^1.3.0",
"@ctrl/tinycolor": "^4.1.0",
"@floating-ui/dom": "^1.6.12",
"@shoelace-style/animations": "^1.2.0",
@@ -676,6 +677,15 @@
"node": ">=4"
}
},
"node_modules/@bramus/style-observer": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/@bramus/style-observer/-/style-observer-1.3.0.tgz",
"integrity": "sha512-IQjYId9X7xgz0NeKGatC37lqsdS+cE5bfdB9jKh7+zJnA9BqENee2C48boDIRQrTED4JxleRZGhTY86S1/l7QA==",
"license": "MIT",
"engines": {
"node": ">=10"
}
},
"node_modules/@cspell/cspell-bundled-dicts": {
"version": "6.31.3",
"resolved": "https://registry.npmjs.org/@cspell/cspell-bundled-dicts/-/cspell-bundled-dicts-6.31.3.tgz",

View File

@@ -64,6 +64,7 @@
"node": ">=14.17.0"
},
"dependencies": {
"@bramus/style-observer": "^1.3.0",
"@ctrl/tinycolor": "^4.1.0",
"@floating-ui/dom": "^1.6.12",
"@shoelace-style/animations": "^1.2.0",

View File

@@ -31,3 +31,24 @@ svg {
width: 1em;
justify-content: center;
}
@property --wa-icon-family {
syntax: '<custom-ident> | auto';
inherits: true;
initial-value: 'auto';
}
@property --wa-icon-variant {
syntax: '<custom-ident> | auto';
inherits: true;
initial-value: 'auto';
}
@property --wa-icon-library {
syntax: '<custom-ident> | auto';
inherits: true;
initial-value: 'auto';
}
@property --wa-icon-name {
syntax: '<custom-ident> | auto';
inherits: true;
initial-value: 'auto';
}

View File

@@ -1,3 +1,4 @@
import { CSSStyleObserver } from '@bramus/style-observer';
import type { CSSResult, CSSResultGroup, PropertyDeclaration, PropertyValues } from 'lit';
import { LitElement, isServer, unsafeCSS } from 'lit';
import { property } from 'lit/decorators.js';
@@ -70,6 +71,8 @@ export default class WebAwesomeElement extends LitElement {
connectedCallback() {
super.connectedCallback();
// FIXME this is currently static.
// It will only update when the element is connected, not when a relevant CSS property changes.
this.updateCSSProperties();
}
@@ -191,19 +194,36 @@ export default class WebAwesomeElement extends LitElement {
return;
}
if (!Self.styleObserver) {
// First time, init stuff
// First, replace `true` with actual CSS property names
for (let [name, cssProperty] of Self.cssAttributeProperties) {
if (cssProperty === true) {
// Default name
cssProperty = `--${this.tagName.toLowerCase()}-${name}`;
Self.cssAttributeProperties.set(name, cssProperty);
}
}
// Then we observe them
let cssProperties = [...Self.cssAttributeProperties.values()] as string[];
Self.styleObserver = new CSSStyleObserver(cssProperties, (...args) => {
console.log(...args);
this.updateCSSProperties();
});
}
this.#computedStyle ??= getComputedStyle(this);
// FIXME this is currently static. It will only update when the element is connected, and not when the CSS property changes.
const tagName = this.tagName.toLowerCase();
for (let [name, cssProperty] of Self.cssAttributeProperties) {
// FIXME currently this means that CSS properties will override JS properties. This is not ideal.
if (typeof name === 'string' && !this.hasAttribute(name) && this.#setVia[name] !== 'js') {
// Check if supplied as a CSS custom property
// TODO !important should override attribute values
cssProperty = cssProperty === true ? `--${tagName}-${name}` : cssProperty;
const value = this.#computedStyle?.getPropertyValue(cssProperty);
if (value) {
if (value && value !== 'auto') {
this.#setVia[name] = 'css';
this.#setting.add(name);
// @ts-ignore
@@ -216,15 +236,16 @@ export default class WebAwesomeElement extends LitElement {
}
}
// Subclasses will override this
// Subclasses will get their own copy automagically (see below)
protected static cssAttributeProperties = new Map<PropertyKey, true | string>();
protected static styleObserver: CSSStyleObserver | undefined;
static createProperty(name: PropertyKey, options?: PropertyDeclaration): void {
super.createProperty(name, options);
if (options?.cssProperty) {
if (this.cssAttributeProperties === WebAwesomeElement.cssAttributeProperties) {
// Each class needs its own, otherwise they'd share the same Set
// Each class needs its own, otherwise they'd share the same object
this.cssAttributeProperties = new Map();
}