UNPKG

@rhds/elements

Version:
222 lines 9.19 kB
var _RhIcon_instances, _a, _RhIcon_intersecting, _RhIcon_logger, _RhIcon_internals, _RhIcon_getContent, _RhIcon_lazyLoad, _RhIcon_dispatchLoad, _RhIcon_load; var RhIcon_1; import { __classPrivateFieldGet, __classPrivateFieldSet, __decorate } from "tslib"; import { LitElement, html, isServer } from 'lit'; import { customElement } from 'lit/decorators/custom-element.js'; import { property } from 'lit/decorators/property.js'; import { state } from 'lit/decorators/state.js'; import { classMap } from 'lit/directives/class-map.js'; import { unsafeHTML } from 'lit/directives/unsafe-html.js'; import { observes } from '@patternfly/pfe-core/decorators/observes.js'; import { Logger } from '@patternfly/pfe-core/controllers/logger.js'; import { InternalsController } from '@patternfly/pfe-core/controllers/internals-controller.js'; import { css } from "lit"; const style = css `:host{line-height:0;aspect-ratio:1/1;display:inline-flex;place-content:center}#container{display:contents}svg{width:var(--rh-icon-size,var(--rh-size-icon-01,16px));fill:currentcolor;aspect-ratio:1/1}.standard svg{width:var(--rh-icon-size,var(--rh-size-icon-04,40px))}.microns svg{min-width:8px;max-width:12px;width:var(--rh-icon-size,12px)}`; if (isServer) { await import('./ssr.js'); } /** * requestIdleCallback when available, requestAnimationFrame when not * @param f callback */ const ric = globalThis.requestIdleCallback ?? globalThis.requestAnimationFrame ?? (async (f) => Promise.resolve().then(f)); /** Fired when an icon fails to load */ export class IconResolveErrorEvent extends ErrorEvent { constructor(set, icon, /** The original error when importing the icon module */ originalError) { super('error', { message: `Could not load icon "${icon}" from set "${set}".` }); this.originalError = originalError; } } /** * Use to display Red Hat brand icons as decorative or informational elements. * Hidden from assistive technology by default (role="presentation"). When * `accessible-label` is set, gains role="img" and aria-label for screen * readers. Must not be the sole interactive element; wrap in a button or * link for keyboard access. Supports lazy, idle, and eager loading. Avoid * setting aria-hidden manually. * * @summary Displays Red Hat brand icons with configurable size and loading * * @alias icon * * @fires load - Fired when icon SVG content is loaded and rendered. Bubbles. * No detail payload; check the element's `icon` and `set` properties. * @fires error - Fired when icon fails to load. Instance of * {@link IconResolveErrorEvent} with `originalError` containing the * import failure and `message` describing the icon and set. * * @cssprop [--rh-icon-size] - Controls both width and height (square aspect * ratio). Defaults vary by set: ui icons use `--rh-size-icon-01` * (16px), standard icons use `--rh-size-icon-04` (40px), microns * use 12px (range 8-12px). */ let RhIcon = RhIcon_1 = _a = class RhIcon extends LitElement { constructor() { super(...arguments); _RhIcon_instances.add(this); /** * Icon set to load from. Accepts 'standard' | 'ui' | 'microns' or any * registered custom set name. Controls default sizing: standard=40px, * ui=16px, microns=12px. Defaults to 'standard'. */ this.set = 'standard'; /** * Controls when the icon data is fetched. * - `eager`: loads immediately, blocking the main thread * - `idle`: waits for browser idle via requestIdleCallback * - `lazy` (default): waits for the element to enter the viewport * via IntersectionObserver. Defaults to 'lazy'. */ this.loading = 'lazy'; _RhIcon_intersecting.set(this, false); _RhIcon_logger.set(this, new Logger(this)); _RhIcon_internals.set(this, InternalsController.of(this)); } connectedCallback() { super.connectedCallback(); RhIcon_1.instances.add(this); } render() { const { set } = this; const content = __classPrivateFieldGet(this, _RhIcon_instances, "m", _RhIcon_getContent).call(this); return html ` <div id="container" aria-hidden="${String(!!content)}" class="${classMap({ [set]: true })}">${!isServer ? content : unsafeHTML(content)}<!-- Container for the fallback (i.e. slotted) content --><span part="fallback" ?hidden="${content}"><!-- summary: Fallback content when icon fails to load description: | Displayed only when the icon SVG cannot be resolved. Should contain meaningful text or an alternative image for screen readers if the icon conveys information. Hidden automatically when icon loads successfully. --><slot></slot></span> </div> `; } updated() { // this is a workaround for an apparent webkit / lit-ssr bug const [, ...duplicateContainers] = this.shadowRoot?.querySelectorAll('#container') ?? []; for (const dupe of duplicateContainers) { dupe.remove(); } } disconnectedCallback() { super.disconnectedCallback(); RhIcon_1.io.unobserve(this); RhIcon_1.instances.delete(this); } iconChanged() { if (!this.icon) { this.content = null; return; } __classPrivateFieldGet(this, _RhIcon_instances, "m", _RhIcon_dispatchLoad).call(this); } accessibleLabelChanged() { __classPrivateFieldGet(this, _RhIcon_internals, "f").ariaLabel = this.accessibleLabel ?? null; if (this.accessibleLabel) { __classPrivateFieldGet(this, _RhIcon_internals, "f").role = 'img'; } else { __classPrivateFieldGet(this, _RhIcon_internals, "f").role = 'presentation'; } } async contentChanged(old) { if (old !== this.content) { await this.updateComplete; this.dispatchEvent(new Event('load', { bubbles: true })); } } }; _RhIcon_intersecting = new WeakMap(); _RhIcon_logger = new WeakMap(); _RhIcon_internals = new WeakMap(); _RhIcon_instances = new WeakSet(); _RhIcon_getContent = function _RhIcon_getContent() { if (isServer) { const { set = 'standard', icon } = this; if (!icon) { return ''; } return globalThis.RH_ICONS.get(set)?.get(icon) ?? ''; } else { return this.content ?? ''; } }; _RhIcon_lazyLoad = function _RhIcon_lazyLoad(shouldObserve = true) { if (shouldObserve) { RhIcon_1.io.observe(this); } if (__classPrivateFieldGet(this, _RhIcon_intersecting, "f")) { __classPrivateFieldGet(this, _RhIcon_instances, "m", _RhIcon_load).call(this); } }; _RhIcon_dispatchLoad = function _RhIcon_dispatchLoad(shouldObserve = true) { switch (this.loading) { case 'idle': return void ric(() => __classPrivateFieldGet(this, _RhIcon_instances, "m", _RhIcon_load).call(this)); case 'lazy': return void __classPrivateFieldGet(this, _RhIcon_instances, "m", _RhIcon_lazyLoad).call(this, shouldObserve); case 'eager': return void __classPrivateFieldGet(this, _RhIcon_instances, "m", _RhIcon_load).call(this); } }; _RhIcon_load = async function _RhIcon_load() { const { set = 'standard', icon } = this; if (set && icon) { try { this.content = await RhIcon_1.resolve?.(set, icon); } catch (error) { if (error instanceof Error) { __classPrivateFieldGet(this, _RhIcon_logger, "f").error(error.message); this.dispatchEvent(new IconResolveErrorEvent(set, icon, error)); } } } }; RhIcon.styles = [style]; RhIcon.onIntersect = records => records.forEach(({ isIntersecting, target }) => { const icon = target; __classPrivateFieldSet(icon, _RhIcon_intersecting, isIntersecting, "f"); __classPrivateFieldGet(icon, _RhIcon_instances, "m", _RhIcon_dispatchLoad).call(icon, false); }); RhIcon.io = new IntersectionObserver(RhIcon_1.onIntersect); RhIcon.instances = new Set(); RhIcon.resolve = (set, icon) => import(`@rhds/icons/${set}/${icon}.js`) .then(mod => mod.default.cloneNode(true)); __decorate([ property({ type: String, reflect: true }) ], RhIcon.prototype, "set", void 0); __decorate([ property({ type: String, reflect: true }) ], RhIcon.prototype, "icon", void 0); __decorate([ property({ attribute: 'accessible-label' }) ], RhIcon.prototype, "accessibleLabel", void 0); __decorate([ property() ], RhIcon.prototype, "loading", void 0); __decorate([ state() ], RhIcon.prototype, "content", void 0); __decorate([ observes('icon'), observes('set') ], RhIcon.prototype, "iconChanged", null); __decorate([ observes('accessibleLabel') ], RhIcon.prototype, "accessibleLabelChanged", null); __decorate([ observes('content') ], RhIcon.prototype, "contentChanged", null); RhIcon = RhIcon_1 = __decorate([ customElement('rh-icon') ], RhIcon); export { RhIcon }; //# sourceMappingURL=rh-icon.js.map