-
Notifications
You must be signed in to change notification settings - Fork 12
feat(core): add icon style control #219
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -27,6 +27,7 @@ declare global { | |
| * @cssprop --color | ||
| * @cssprop --width | ||
| * @cssprop --height | ||
| * @attr appearance - Selects the outline or solid form of a named icon. | ||
| * @slot - Custom SVG content to override the named icon | ||
| * @aria https://developer.mozilla.org/en-US/docs/Web/HTML/Element/img | ||
| */ | ||
|
|
@@ -46,6 +47,12 @@ export class Icon extends LitElement { | |
| */ | ||
| @property({ type: String, reflect: true }) direction?: 'up' | 'down' | 'left' | 'right'; | ||
|
|
||
| /** | ||
| * Selects the outline or solid form of the named icon. Solid icons use an optional `-solid` asset and fall back to | ||
| * the outline form when that asset is unavailable. | ||
| */ | ||
| @property({ type: String }) appearance?: 'outline' | 'solid'; | ||
|
Comment on lines
+50
to
+54
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟠 Major | 🏗️ Heavy lift Implement the public API specified by the PR objective. The PR objective requires
📍 Affects 3 files
🤖 Prompt for AI Agents |
||
|
|
||
| /** | ||
| * The name of the icon SVG sprite to render. | ||
| */ | ||
|
|
@@ -80,12 +87,26 @@ export class Icon extends LitElement { | |
| /** @private */ | ||
| declare _internals: ElementInternals; | ||
|
|
||
| get #resolvedIconName() { | ||
| if (!this.name || this.name.endsWith('.svg') || this.appearance !== 'solid' || this.name.endsWith('-solid')) { | ||
| return this.name; | ||
| } | ||
|
|
||
| const solidName = `${this.name}-solid`; | ||
| return Icon._iconsRegistry[solidName] ? solidName : this.name; | ||
| } | ||
|
|
||
| get #iconString() { | ||
| return isServer && globalThis._NVE_SSR_ICON_REGISTRY ? globalThis._NVE_SSR_ICON_REGISTRY[this.name!] : this.svg; | ||
| const iconName = this.#resolvedIconName; | ||
| return isServer && globalThis._NVE_SSR_ICON_REGISTRY && iconName | ||
| ? globalThis._NVE_SSR_ICON_REGISTRY[iconName] | ||
| : this.svg; | ||
| } | ||
|
|
||
| #iconRegistryEventName?: string; | ||
|
|
||
| #renderRequest = 0; | ||
|
|
||
| #onIconRegistryUpdate = (event: Event) => this.#asyncRender(event as CustomEvent<IconSVG>); | ||
|
|
||
| render() { | ||
|
|
@@ -133,16 +154,17 @@ export class Icon extends LitElement { | |
|
|
||
| async updated(props: PropertyValues<this>) { | ||
| super.updated(props); | ||
| if (props.has('name')) { | ||
| if (props.has('name') || props.has('appearance')) { | ||
| this.#removeIconRegistryListener(); | ||
| this.#addIconRegistryListener(); | ||
| } | ||
| await this.#render(); | ||
| } | ||
|
|
||
| #addIconRegistryListener() { | ||
| if (!this.isConnected || !this.name || this.#iconRegistryEventName) return; | ||
| this.#iconRegistryEventName = `${Icon.metadata.tag}-${this.name}`; | ||
| const iconName = this.#resolvedIconName; | ||
| if (!this.isConnected || !iconName || this.#iconRegistryEventName) return; | ||
| this.#iconRegistryEventName = `${Icon.metadata.tag}-${iconName}`; | ||
| globalThis.document?.addEventListener(this.#iconRegistryEventName, this.#onIconRegistryUpdate); | ||
|
Comment on lines
164
to
168
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟠 Major | 🏗️ Heavy lift Listen for a filled asset that becomes available after fallback. If Subscribe filled-variant instances to both the stroke and filled asset events, or dispatch a base-name refresh event when a filled asset is added. Add coverage for this late-registration flow. 🤖 Prompt for AI Agents
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. For context I think we can skip this for now as There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Consumers can register and use the explicit filled asset name when they require that behavior. I will keep this as a deferred edge case rather than require a change here. 🐇 ✏️ Learnings added
You are interacting with an AI system. |
||
| } | ||
|
|
||
|
|
@@ -159,11 +181,14 @@ export class Icon extends LitElement { | |
| } | ||
|
|
||
| async #render() { | ||
| if (!this.name) return; | ||
| const svg = await (this.name.endsWith('.svg') | ||
| ? fetch(this.name).then(res => res.text()) | ||
| : (Icon._iconsRegistry[this.name]?.svg() ?? Promise.resolve(''))); | ||
| Icon._iconsRegistry[this.name] = { svg: () => svg, ...Icon._iconsRegistry[this.name] }; | ||
| const renderRequest = ++this.#renderRequest; | ||
| const iconName = this.#resolvedIconName; | ||
| if (!iconName) return; | ||
| const svg = await (iconName.endsWith('.svg') | ||
| ? fetch(iconName).then(res => res.text()) | ||
| : (Icon._iconsRegistry[iconName]?.svg() ?? Promise.resolve(''))); | ||
| if (renderRequest !== this.#renderRequest) return; | ||
| Icon._iconsRegistry[iconName] = { svg: () => svg, ...Icon._iconsRegistry[iconName] }; | ||
| this.svg = svg; | ||
| } | ||
| } | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win
Restore
window.fetchwhen the test fails.If an awaited operation or assertion fails, line 251 does not run. Later tests then use this mock unexpectedly. Put the test body in
try/finally.Proposed fix
🤖 Prompt for AI Agents