You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
78 lines
1.6 KiB
78 lines
1.6 KiB
<script lang="ts">
|
|
import type { Component } from 'svelte';
|
|
import type { IconSize, IconStrokeWidth } from '$uix/eidos/components/icon/types';
|
|
import { loadIconByName } from './icon-loaders';
|
|
|
|
type Props = {
|
|
name: string;
|
|
size?: IconSize;
|
|
strokeWidth?: IconStrokeWidth;
|
|
color?: string;
|
|
decorative?: boolean;
|
|
title?: string;
|
|
};
|
|
|
|
let {
|
|
name,
|
|
size = 'md',
|
|
strokeWidth = 'auto',
|
|
color = 'currentColor',
|
|
decorative = true,
|
|
title
|
|
}: Props = $props();
|
|
|
|
let host = $state<HTMLElement | null>(null);
|
|
let visible = $state(false);
|
|
let Glyph = $state<Component | undefined>();
|
|
let requestId = 0;
|
|
|
|
$effect(() => {
|
|
const el = host;
|
|
if (!el || visible) return;
|
|
if (typeof IntersectionObserver === 'undefined') {
|
|
visible = true;
|
|
return;
|
|
}
|
|
|
|
const observer = new IntersectionObserver(
|
|
(entries) => {
|
|
if (!entries.some((entry) => entry.isIntersecting)) return;
|
|
visible = true;
|
|
observer.disconnect();
|
|
},
|
|
{ rootMargin: '180px' }
|
|
);
|
|
observer.observe(el);
|
|
return () => observer.disconnect();
|
|
});
|
|
|
|
$effect(() => {
|
|
const iconName = name;
|
|
if (!visible || !iconName) {
|
|
Glyph = undefined;
|
|
return;
|
|
}
|
|
|
|
const current = ++requestId;
|
|
void loadIconByName(iconName).then((component) => {
|
|
if (current === requestId) Glyph = component;
|
|
});
|
|
});
|
|
</script>
|
|
|
|
<span bind:this={host} data-uix-lazy-icon>
|
|
{#if Glyph}
|
|
{@const Icon = Glyph}
|
|
<Icon {size} {strokeWidth} {color} {decorative} {title} />
|
|
{/if}
|
|
</span>
|
|
|
|
<style>
|
|
[data-uix-lazy-icon] {
|
|
display: inline-grid;
|
|
place-items: center;
|
|
min-inline-size: 1em;
|
|
min-block-size: 1em;
|
|
}
|
|
</style>
|