flotilla/src/lib/components/Icon.svelte

92 lines
2 KiB
Svelte
Raw Normal View History

2024-08-05 22:28:46 +00:00
<style>
div {
2026-02-19 20:21:24 +00:00
mask-repeat: no-repeat;
2024-08-05 22:28:46 +00:00
mask-size: 100% 100%;
}
</style>
2024-08-05 21:45:16 +00:00
<script lang="ts">
2026-02-19 20:53:10 +00:00
import {maybe} from "@welshman/lib"
const {
icon,
size = 5,
...restProps
}: {
2025-02-04 00:37:14 +00:00
icon: string
size?: number
class?: string
style?: string
} = $props()
2024-08-05 21:45:16 +00:00
const px = size * 4
2026-02-19 20:53:10 +00:00
// The ios wkwebview has no "Safari" token in its user agent, so detect the engine instead.
const isWebkit =
2026-02-19 20:53:10 +00:00
typeof navigator !== "undefined" &&
/applewebkit/i.test(navigator.userAgent) &&
!/chrome|chromium|android|firefox/i.test(navigator.userAgent)
// Strip any query string/fragment - hosted icons are often served with one.
const isRemoteSvg = (value: string) =>
!value.startsWith("data:") && /\.svg$/i.test(value.split(/[?#]/)[0]!)
2026-02-19 20:53:10 +00:00
let objectUrl = $state(maybe<string>())
const src = $derived(objectUrl || icon)
// Primal issues 302 redirects from blossom, which breaks mask-image on webkit.
const fetchSvg = async (url: string) => {
2026-02-19 20:53:10 +00:00
try {
const response = await fetch(url, {
2026-02-19 20:53:10 +00:00
mode: "cors",
credentials: "omit",
})
if (response.ok) {
return URL.createObjectURL(await response.blob())
2026-02-19 20:53:10 +00:00
}
} catch {
// pass
}
}
// Re-fetch when icon changes, revoking the previous blob url.
$effect(() => {
const url = icon
let canceled = false
let created: string | undefined
if (isWebkit && isRemoteSvg(url)) {
fetchSvg(url).then(blobUrl => {
2026-09-21 16:48:35 +00:00
if (!blobUrl) {
return
}
if (canceled) {
URL.revokeObjectURL(blobUrl)
} else {
created = blobUrl
objectUrl = blobUrl
}
})
2026-02-19 20:53:10 +00:00
}
return () => {
canceled = true
if (created) {
URL.revokeObjectURL(created)
objectUrl = undefined
2026-02-19 20:53:10 +00:00
}
}
})
2024-08-05 21:45:16 +00:00
</script>
<div
2025-02-04 00:37:14 +00:00
class="inline-block {restProps.class}"
style="mask-image: url({src}); width: {px}px; height: {px}px; min-width: {px}px; min-height: {px}px; background-color: currentcolor; {restProps.style ??
''}">
2025-02-03 23:01:42 +00:00
</div>