Compare commits

...

23 Commits

Author SHA1 Message Date
Lea Verou
f753dc8e76 Merge branch 'icon-properties' of https://github.com/shoelace-style/webawesome into icon-properties 2025-01-07 17:55:42 -05:00
Lea Verou
4bee37f621 Remove docs from alpha 2025-01-07 17:55:32 -05:00
Lea Verou
4103cea723 Better docs 2025-01-07 17:55:32 -05:00
Lea Verou
f12de60739 Rename to cssAttributeProperties, follow property name if specified 2025-01-07 17:55:32 -05:00
Lea Verou
b32f4d2d13 Move css property reflection stuff to base class 2025-01-07 17:55:32 -05:00
Lea Verou
e94c8b34b8 CSS-set properties should not override JS-set properties 2025-01-07 17:55:32 -05:00
Lea Verou
5ca8517acf Update icon.md 2025-01-07 17:55:31 -05:00
Lea Verou
14ca7e2bd8 Update icon.md 2025-01-07 17:55:31 -05:00
Lea Verou
01834120c9 Fix deploy 2025-01-07 17:55:31 -05:00
Lea Verou
fe1cf2d72d Start work on --wa-icon-* properties 2025-01-07 17:55:31 -05:00
Lea Verou
a66a926ce4 Remove docs from alpha 2025-01-07 17:51:49 -05:00
Lea Verou
423067c7a7 Better docs 2025-01-07 17:44:22 -05:00
Lea Verou
f31d19c249 Rename to cssAttributeProperties, follow property name if specified 2025-01-07 17:41:22 -05:00
Lea Verou
711cf99cf8 Move css property reflection stuff to base class 2025-01-07 16:55:50 -05:00
Lea Verou
e4117512f8 Harmonize updated() definitions
- Use proper type
- Use same argument name
- Ensure `super.updated()` is called
2025-01-07 16:53:08 -05:00
Lea Verou
0784f39537 CSS-set properties should not override JS-set properties 2025-01-07 15:05:18 -05:00
Lea Verou
9d2bd6108d Update icon.md 2025-01-07 14:44:51 -05:00
Lea Verou
7b91d41768 Update icon.md 2025-01-07 14:37:15 -05:00
Lea Verou
a6847886e8 Merge branch 'next' into icon-properties 2025-01-07 14:23:56 -05:00
Lea Verou
5fdfc2f1fa Merge branch 'next' into icon-properties 2025-01-07 12:10:34 -05:00
Lea Verou
cd5c8046a0 Fix deploy 2025-01-07 10:23:53 -05:00
Lea Verou
2db378cfae Merge branch 'next' into icon-properties 2025-01-07 10:17:39 -05:00
Lea Verou
7c4dc7e051 Start work on --wa-icon-* properties 2025-01-06 22:02:48 -05:00
3 changed files with 132 additions and 5 deletions

View File

@@ -24,6 +24,49 @@ Many Font Awesome Pro icon families have variants such as `thin`, `light`, `regu
<wa-icon family="brands" name="web-awesome"></wa-icon>
```
<div data-alpha="remove">
### 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):
```html {.example}
<wa-callout>
<!-- Look ma, no attributes! -->
<wa-icon slot="icon"></wa-icon>
This is a callout.
</wa-callout>
<wa-callout variant="danger">
<wa-icon slot="icon" name="dumpster-fire" variant="solid"></wa-icon>
This is a callout with an explicit icon.
</wa-callout>
<wa-callout variant="warning">
<!-- Look ma, no attributes! -->
<wa-icon slot="icon"></wa-icon>
Here be dragons.
</wa-callout>
<style>
wa-callout {
--wa-icon-variant: regular;
--wa-icon-name: info-circle;
&[variant="warning"] {
--wa-icon-name: triangle-exclamation;
}
}
</style>
```
Notes:
- If you specify attributes, they will override the CSS custom properties, which provides a way to set defaults and then override them as needed.
- CSS custom properties inherit — so if you set a `--wa-icon-*` custom property on an element, it will affect *all* icons within it that dont override these values (either via attributes or CSS custom properties).
- These CSS properties are currently not reactive and will only be read when the component is first connected.
</div>
### Colors
Icons inherit their color from the current text color. Thus, you can set the `color` property on the `<wa-icon>` element or an ancestor to change the color.

View File

@@ -48,21 +48,21 @@ export default class WaIcon extends WebAwesomeElement {
@state() private svg: SVGElement | HTMLTemplateResult | null = null;
/** The name of the icon to draw. Available names depend on the icon library being used. */
@property({ reflect: true }) name?: string;
@property({ cssProperty: true }) name?: string;
/**
* The family of icons to choose from. For Font Awesome Free (default), valid options include `classic` and `brands`.
* For Font Awesome Pro subscribers, valid options include, `classic`, `sharp`, `duotone`, and `brands`. Custom icon
* libraries may or may not use this property.
*/
@property({ reflect: true }) family: string;
@property({ cssProperty: true }) family: string;
/**
* The name of the icon's variant. For Font Awesome, valid options include `thin`, `light`, `regular`, and `solid` for
* the `classic` and `sharp` families. Some variants require a Font Awesome Pro subscription. Custom icon libraries
* may or may not use this property.
*/
@property({ reflect: true }) variant: string;
@property({ cssProperty: true }) variant: string;
/** Draws the icon in a fixed-width both. */
@property({ attribute: 'fixed-width', type: Boolean, reflect: true }) fixedWidth: false;
@@ -80,10 +80,11 @@ export default class WaIcon extends WebAwesomeElement {
@property() label = '';
/** The name of a registered custom icon library. */
@property({ reflect: true }) library = 'default';
@property({ cssProperty: true }) library = 'default';
connectedCallback() {
super.connectedCallback();
watchIcon(this);
}

View File

@@ -1,7 +1,18 @@
import type { CSSResult, CSSResultGroup, PropertyValues } from 'lit';
import type { CSSResult, CSSResultGroup, PropertyDeclaration, PropertyValues } from 'lit';
import { LitElement, isServer, unsafeCSS } from 'lit';
import { property } from 'lit/decorators.js';
import componentStyles from '../styles/shadow/component.css';
import { getComputedStyle } from './computedStyle.js';
// Augment Lit's module
declare module 'lit' {
interface PropertyDeclaration {
/**
* Indicates whether the property should reflect to a CSS custom property.
*/
cssProperty?: true | string;
}
}
export default class WebAwesomeElement extends LitElement {
constructor() {
@@ -52,6 +63,16 @@ export default class WebAwesomeElement extends LitElement {
internals: ElementInternals;
#computedStyle: CSSStyleDeclaration | null;
#setVia: Record<PropertyKey, 'css' | 'attribute' | 'js'> = {};
#setting = new Set<PropertyKey>();
connectedCallback() {
super.connectedCallback();
this.updateCSSProperties();
}
attributeChangedCallback(name: string, oldValue: string | null, newValue: string | null) {
if (!this.#hasRecordedInitialProperties) {
(this.constructor as typeof WebAwesomeElement).elementProperties.forEach(
@@ -111,6 +132,21 @@ export default class WebAwesomeElement extends LitElement {
}
}
updated(changedProperties: PropertyValues<this>) {
super.updated(changedProperties);
let Self = this.constructor as typeof WebAwesomeElement;
if (Self.cssAttributeProperties.size > 0) {
for (let [name] of changedProperties) {
if (typeof name === 'string' && this.#setVia[name] === 'css' && !this.#setting.has(name)) {
// A property is being set via JS and its NOT because we're reflecting a CSS property
this.#setVia[name] = 'js';
}
}
}
}
/** Checks if states are supported by the element */
private hasStatesSupport(): boolean {
return Boolean(this.internals?.states);
@@ -148,4 +184,51 @@ export default class WebAwesomeElement extends LitElement {
hasCustomState(state: string): boolean {
return this.hasStatesSupport() ? this.internals.states.has(state) : false;
}
protected updateCSSProperties() {
const Self = this.constructor as typeof WebAwesomeElement;
if (Self.cssAttributeProperties.size === 0) {
return;
}
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) {
this.#setVia[name] = 'css';
this.#setting.add(name);
// @ts-ignore
this[name] = value.trim();
this.updateComplete.then(() => {
this.#setting.delete(name);
});
}
}
}
}
// Subclasses will override this
protected static cssAttributeProperties = new Map<PropertyKey, true | string>();
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
this.cssAttributeProperties = new Map();
}
this.cssAttributeProperties.set(name, options.cssProperty);
}
}
}