From 56092905384e4095ba99838565556f7bbe08b967 Mon Sep 17 00:00:00 2001 From: Vjacheslav Trushkin Date: Sun, 21 Jan 2024 10:00:10 +0200 Subject: [PATCH] chore: keep innerHTML in web component, export functions for building icon --- iconify-icon/icon/README.md | 4 +- iconify-icon/icon/demo/usage.html | 46 +++++++++++++++ iconify-icon/icon/package.json | 5 +- iconify-icon/icon/src/component.ts | 89 ++++++++++++++++++++++++------ iconify-icon/icon/src/functions.ts | 11 ++++ 5 files changed, 136 insertions(+), 19 deletions(-) diff --git a/iconify-icon/icon/README.md b/iconify-icon/icon/README.md index 735f341..7b7d250 100644 --- a/iconify-icon/icon/README.md +++ b/iconify-icon/icon/README.md @@ -20,13 +20,13 @@ Iconify Icon web component renders icons. Add this line to your page to load IconifyIcon (you can add it to `` section of the page or before ``): ```html - + ``` or ```html - + ``` or, if you are building a project with a bundler, you can include the script by installing `iconify-icon` as a dependency and importing it in your project: diff --git a/iconify-icon/icon/demo/usage.html b/iconify-icon/icon/demo/usage.html index 3a37da6..f2f4933 100644 --- a/iconify-icon/icon/demo/usage.html +++ b/iconify-icon/icon/demo/usage.html @@ -103,6 +103,16 @@ }, }, }); + + setTimeout(() => { + const span = document.querySelector('.test-2sec'); + if (span) { + const icon = + span.parentElement.querySelector('iconify-icon'); + span.remove(); + icon.setAttribute('icon', 'test:icon'); + } + }, 2000); })(); @@ -322,6 +332,42 @@ mode="style" >

+ +

Icon with innerHTML

+

+ Keeping innerHTML without icon attribute: + + + + + +

+

+ Keeping innerHTML, + updating after 2 seconds...: + + + + + +

+

Scale icon

Using height="none" and CSS, animating width/height and color

diff --git a/iconify-icon/icon/package.json b/iconify-icon/icon/package.json index 9a880cf..c6f63a4 100644 --- a/iconify-icon/icon/package.json +++ b/iconify-icon/icon/package.json @@ -2,7 +2,10 @@ "name": "iconify-icon", "description": "Icon web component that loads icon data on demand. Over 150,000 icons to choose from", "author": "Vjacheslav Trushkin (https://iconify.design)", - "version": "1.0.8", + "version": "1.1.0-beta.1", + "publishConfig": { + "tag": "next" + }, "license": "MIT", "main": "./dist/iconify-icon.cjs", "types": "./dist/iconify-icon.d.ts", diff --git a/iconify-icon/icon/src/component.ts b/iconify-icon/icon/src/component.ts index ddc9a75..0eeac32 100644 --- a/iconify-icon/icon/src/component.ts +++ b/iconify-icon/icon/src/component.ts @@ -103,7 +103,10 @@ export function defineIconifyIcon( // Root _shadowRoot: ShadowRoot; - // State + // Initialised + _initialised = false; + + // Icon state _state: IconState; // Attributes check queued @@ -115,27 +118,76 @@ export function defineIconifyIcon( constructor() { super(); - // Attach shadow DOM - const root = (this._shadowRoot = this.attachShadow({ - mode: 'open', - })); + // Render old content if no icon attribute is set + if (!this.getAttribute('icon')) { + try { + if (document.readyState == 'complete') { + // DOM already loaded + const html = this.innerHTML; + if (html) { + this._createShadowRoot().innerHTML = html; + } + } else { + // Do it when DOM is loaded + window.onload = () => { + if (!this.getAttribute('icon')) { + const html = this.innerHTML; + if (html) { + this._createShadowRoot().innerHTML = html; + } + } + }; + } + } catch (err) { + // + } + return; + } - // Add style - const inline = getInline(this); - updateStyle(root, inline); - - // Create empty state - this._state = setPendingState( - { - value: '', - }, - inline - ); + // Init DOM + this._init(); // Queue icon render this._queueCheck(); } + /** + * Create shadow root + */ + _createShadowRoot() { + // Attach shadow DOM + if (!this._shadowRoot) { + this._shadowRoot = this.attachShadow({ + mode: 'open', + }); + } + return this._shadowRoot; + } + + /** + * Init state + */ + _init() { + if (!this._initialised) { + this._initialised = true; + + // Create root + const root = this._createShadowRoot(); + + // Add style + const inline = getInline(this); + updateStyle(root, inline); + + // Create empty state + this._state = setPendingState( + { + value: '', + }, + inline + ); + } + } + /** * Observed attributes */ @@ -165,6 +217,8 @@ export function defineIconifyIcon( * Attribute has changed */ attributeChangedCallback(name: string) { + this._init(); + if (name === 'inline') { // Update immediately: not affected by other attributes const newInline = getInline(this); @@ -221,6 +275,8 @@ export function defineIconifyIcon( * Restart animation */ restartAnimation() { + this._init(); + const state = this._state; if (state.rendered) { const root = this._shadowRoot; @@ -241,6 +297,7 @@ export function defineIconifyIcon( * Get status */ get status(): IconifyIconStatus { + this._init(); const state = this._state; return state.rendered ? 'rendered' diff --git a/iconify-icon/icon/src/functions.ts b/iconify-icon/icon/src/functions.ts index 43b43ec..bd4a936 100644 --- a/iconify-icon/icon/src/functions.ts +++ b/iconify-icon/icon/src/functions.ts @@ -16,6 +16,11 @@ import type { IconifyBuilderFunctions } from '@iconify/core/lib/builder/function import { iconToSVG as buildIcon } from '@iconify/utils/lib/svg/build'; import { calculateSize } from '@iconify/utils/lib/svg/size'; +// Custom additions used for building icons that are used by component +// Can be reused for building icons in SSR and assigning it as content of component +import { iconToHTML } from '@iconify/utils/lib/svg/html'; +import { svgToURL } from '@iconify/utils/lib/svg/url'; + // API import type { IconifyAPIFunctions, @@ -58,6 +63,10 @@ export interface IconifyExportedFunctions // Append custom style to all components appendCustomStyle: (value: string) => void; + + // Render HTML + iconToHTML: typeof iconToHTML; + svgToURL: typeof svgToURL; } /** @@ -167,6 +176,8 @@ export function exportFunctions(): IconifyExportedFunctions { addCollection, calculateSize, buildIcon, + iconToHTML, + svgToURL, loadIcons, loadIcon, addAPIProvider,