import { addCollection } from '@iconify/vue/offline' import { LOCAL_ICON_COLLECTIONS } from '../../plugins/iconify.collections.js' const FULL_ICON_COLLECTION_LOADERS = Object.freeze({ fluent: () => import('@iconify-json/fluent').then((module) => module.icons), 'icon-park-outline': () => import('@iconify-json/icon-park-outline').then((module) => module.icons), iconamoon: () => import('@iconify-json/iconamoon').then((module) => module.icons), ix: () => import('@iconify-json/ix').then((module) => module.icons), 'line-md': () => import('@iconify-json/line-md').then((module) => module.icons), ri: () => import('@iconify-json/ri').then((module) => module.icons), solar: () => import('@iconify-json/solar').then((module) => module.icons), 'svg-spinners': () => import('@iconify-json/svg-spinners').then((module) => module.icons), 'system-uicons': () => import('@iconify-json/system-uicons').then((module) => module.icons), vaadin: () => import('@iconify-json/vaadin').then((module) => module.icons) }) const fullyRegisteredPrefixes = new Set() const pendingPrefixLoads = new Map() function parseIconName(icon) { if (typeof icon !== 'string') { return null } const normalizedIcon = icon.trim() if (!normalizedIcon) { return null } const separatorIndex = normalizedIcon.indexOf(':') if (separatorIndex <= 0 || separatorIndex >= normalizedIcon.length - 1) { return null } return { prefix: normalizedIcon.slice(0, separatorIndex), name: normalizedIcon.slice(separatorIndex + 1) } } function hasBundledIcon(icon) { const parsedIcon = parseIconName(icon) if (!parsedIcon) { return false } const collection = LOCAL_ICON_COLLECTIONS[parsedIcon.prefix] if (!collection) { return false } return Boolean(collection.icons?.[parsedIcon.name] || collection.aliases?.[parsedIcon.name]) } function loadFullIconCollection(prefix) { if (fullyRegisteredPrefixes.has(prefix)) { return Promise.resolve(true) } const currentTask = pendingPrefixLoads.get(prefix) if (currentTask) { return currentTask } const loader = FULL_ICON_COLLECTION_LOADERS[prefix] if (!loader) { return Promise.resolve(false) } const loadTask = loader() .then((collection) => { addCollection(collection) fullyRegisteredPrefixes.add(prefix) pendingPrefixLoads.delete(prefix) return true }) .catch((error) => { pendingPrefixLoads.delete(prefix) throw error }) pendingPrefixLoads.set(prefix, loadTask) return loadTask } function collectRuntimeIcons(source, iconNames = new Set()) { if (!Array.isArray(source)) { return iconNames } source.forEach((item) => { if (!item || typeof item !== 'object') { return } const icon = item.meta?.icon || item.icon if (typeof icon === 'string' && icon.includes(':') && !hasBundledIcon(icon)) { iconNames.add(icon) } if (Array.isArray(item.children) && item.children.length > 0) { collectRuntimeIcons(item.children, iconNames) } }) return iconNames } function scheduleIdleTask(task, delay = 0) { const invoke = () => { if (globalThis.requestIdleCallback) { globalThis.requestIdleCallback(task, { timeout: 1000 }) return } setTimeout(task, 0) } if (delay > 0) { setTimeout(invoke, delay) return } invoke() } async function ensureIconRegistered(icon) { const parsedIcon = parseIconName(icon) if (!parsedIcon) { return false } if (hasBundledIcon(icon) || fullyRegisteredPrefixes.has(parsedIcon.prefix)) { return true } return loadFullIconCollection(parsedIcon.prefix) } function warmRuntimeIcons(iconNames, delay = 120) { const icons = [...new Set(Array.isArray(iconNames) ? iconNames : [])].filter(Boolean) if (icons.length === 0) { return } scheduleIdleTask(() => { icons.forEach((icon) => { void ensureIconRegistered(icon) }) }, delay) } function warmMenuIcons(menuList, delay = 120) { warmRuntimeIcons([...collectRuntimeIcons(menuList)], delay) } export { collectRuntimeIcons, ensureIconRegistered, hasBundledIcon, warmMenuIcons, warmRuntimeIcons }