diff --git a/repo/icon-sets/app.css b/repo/icon-sets/app.css deleted file mode 100644 index 6ee9ebb..0000000 --- a/repo/icon-sets/app.css +++ /dev/null @@ -1,411 +0,0 @@ -/* - * notion-icons - * (c) 2019 jayhxmo (https://jaymo.io/) - * (c) 2020 dragonwocky (https://dragonwocky.me/) - * (c) 2020 CloudHill - * under the MIT license - */ - -/* tab */ - -[hide-active-bar] > :nth-child(2) { - display: none; -} - -.notion-icons--tab, -.notion-icons--tab > div { - color: var(--theme--text) !important; -} - -#notion-icons--active-bar { - border-bottom: 2px solid var(--theme--text); - position: absolute; - bottom: -1px; - left: 8px; - right: 8px; -} - -.notion-icons--restore-button svg { - width: 16px; - height: 16px; - fill: var(--theme--text_ui_info); -} - -/* interactive hover */ - -.notion-icons--tab > div:hover, -.notion-icons--icon:hover, -.notion-icons--toggle:hover, -.notion-icons--restore-button:hover, -.notion-icons--removed-set:hover { - background: var(--theme--interactive_hover); - box-shadow: 0 0 0 0.5px var(--theme--interactive_hover-border) !important; -} - -/* container */ - -#notion-icons { - position: absolute; - top: 1px; - left: 0; - right: 0; - bottom: 0; - z-index: 9999; - display: flex; - flex-direction: column; - align-items: stretch; - background: var(--theme--card); - border-radius: 3px; - overflow: hidden; -} -/* search */ - -.notion-icons--search { - flex-shrink: 0; - height: 28px; - min-width: 0px; - margin: 9px 14px 10px; - padding: 3px 6px; - border-radius: 3px; - display: flex; - align-items: center; - position: relative; - font-size: 14px; - line-height: 1.2; - background: var(--theme--tag_input); - box-shadow: rgba(15, 15, 15, 0.2) 0px 0px 0px 1px inset; - user-select: none; - cursor: text; -} -.notion-dark-theme .notion-icons--search { - background: rgba(15, 15, 15, 0.3); - box-shadow: rgba(15, 15, 15, 0.1) 0px 0px 0px 1px inset; -} - -.notion-icons--search input { - font-size: inherit; - line-height: inherit; - border: none; - background: none; - width: 100%; - display: block; - resize: none; - padding: 0px; -} - -.notion-icons--search svg { - flex-grow: 0; - flex-shrink: 0; - width: 14px; - height: 14px; - display: block; - fill: inherit; - backface-visibility: hidden; - margin-right: 6px; - color: rgba(55, 53, 47, 0.8); -} -.notion-dark-theme .notion-icons--search svg { - color: rgb(202, 204, 206); -} - -/* scroller */ - -.notion-icons--scroller { - padding: 8px 12px; - overflow: hidden auto; - display: flex; - flex-direction: column; -} - -/* divider */ - -.notion-icons--divider { - height: 1px; - margin-bottom: 9px; - border-bottom: 1px solid var(--theme--table-border); -} - -/* icon set */ - -.notion-icons--icon-set { - margin-bottom: 8px; - color: var(--theme--text); - font-size: 11px; - line-height: 1.5; - letter-spacing: 1px; - font-weight: 600; - border-radius: 2px; -} - -.notion-icons--icon-set.error { - color: var(--theme--text_red); - background: var(--theme--block_red); - border: 1px solid var(--theme--tag_red); - padding: 8px 16px; -} -.notion-icons--icon-set.error::after { - content: '!'; - display: block; - font-size: 1.6em; - line-height: 0.9; - float: right; -} - -/* icon set header/toggle */ - -.notion-icons--toggle { - display: flex; - align-items: center; - margin-bottom: 8px; - padding: 0.25em; - border-radius: 2px; - text-transform: uppercase; - user-select: none; - cursor: pointer; - transition: background 200ms, margin-bottom 200ms ease-in; -} -.notion-icons--icon-set.alert .notion-icons--toggle { - color: var(--theme--block_yellow-text); - background: var(--theme--block_yellow); - border: 1px solid var(--theme--tag_yellow); - margin-left: -1px; - margin-right: -1px; -} -.notion-icons--icon-set.alert .notion-icons--toggle:hover { - background: var(--theme--tag_yellow); -} - -.notion-icons--toggle .triangle { - flex-grow: 0; - flex-shrink: 0; - width: 0.9em; - height: 1em; - margin: 0 0.75em 0 0.5em; - transition: transform 200ms ease-out 0s; - transform: rotateZ(180deg); -} - -.notion-icons--author { - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; -} -.notion-icons--author span, -.notion-icons--author a { - color: var(--theme--text_ui_info); - transition: color 20ms ease-in; -} -.notion-icons--toggle a:hover { - color: var(--theme--primary); -} - -/* icon set body */ - -.notion-icons--body { - display: flex; - flex-wrap: wrap; - align-items: flex-start; - flex-grow: 1; - margin-left: 1.2em; - overflow: hidden; - transition: height 200ms ease-in, opacity 200ms ease-in; -} - -/* hidden icon set */ - -.notion-icons--icon-set[hidden-set] { - padding-bottom: 0; -} -.notion-icons--icon-set[hidden-set] .notion-icons--toggle { - margin-bottom: 0; -} -.notion-icons--icon-set[hidden-set] .triangle { - transform: rotateZ(90deg); -} -.notion-icons--icon-set[hidden-set] .notion-icons--body { - opacity: 0; -} - -/* icons */ - -.notion-icons--icon { - width: 40px; - height: 40px; - padding: 4px; - border-radius: 3px; - display: flex; - align-items: center; - justify-content: center; - position: relative; - user-select: none; - cursor: pointer; - transition: background 20ms ease-in; -} - -.notion-icons--icon img { - width: 100%; - height: 100%; - pointer-events: none; -} -/* spritesheet */ -.notion-icons--icon div { - width: 32px; - height: 32px; - background-size: 32px; - background-repeat: no-repeat; - pointer-events: none; -} - -.notion-icons--icon.error { - font-size: 20px; - background: var(--theme--block_yellow); - border: 1px solid var(--theme--tag_yellow); - color: var(--theme--text_yellow); -} -.notion-icons--icon.error:hover { - background: var(--theme--tag_yellow); -} - -/* tooltip */ - -.notion-icons--tooltip { - position: fixed; - pointer-events: none; - z-index: 99; -} - -.notion-icons--tooltip > div { - position: absolute; - top: 0px; - left: 0px; - width: 40px; - height: 40px; - display: flex; - flex-direction: column; - justify-content: flex-end; - align-items: center; -} - -.notion-icons--tooltip-text { - bottom: calc(100% + 6px); - padding: 4px 8px; - border-radius: 3px; - display: flex; - align-items: center; - flex-direction: column; - position: relative; - max-width: calc(100vw - 24px); - background: rgb(15, 15, 15); - color: rgba(255, 255, 255, 0.9); - box-shadow: rgba(0, 0, 0, 0.3) 0px 1px 4px; - font-size: 12px; - line-height: 1.4; - font-weight: 500; - white-space: nowrap; - overflow: hidden; -} - -.notion-dark-theme .notion-icons--tooltip-text { - background: rgb(202, 204, 206); - color: rgb(15, 15, 15); -} - -/* actions */ - -.notion-icons--actions { - flex-grow: 0; - flex-shrink: 0; - margin-left: auto; - display: flex; - align-items: center; -} - -/* spinner */ - -.notion-icons--spinner { - width: 12px; - height: 12px; -} -.notion-icons--spinner img { - width: 100%; - height: 100%; - animation: rotation 1.3s infinite linear; -} - -/* remove button */ - -.notion-icons--remove-button { - display: flex; - justify-content: center; - align-items: center; - margin-left: 8px; - width: 16px; - height: 16px; - position: relative; -} -.notion-icons--remove-button::before { - content: 'Hide icon set'; - position: absolute; - right: -3px; - padding: 4px 22px 4px 6px; - background: var(--theme--main); - box-shadow: var(--theme--box-shadow); - white-space: nowrap; - opacity: 0; - pointer-events: none; - transition: opacity 50ms ease-in; -} -.notion-icons--remove-button:hover::before { - opacity: 1; - pointer-events: auto; -} -.notion-icons--remove-button svg { - width: 100%; - height: 100%; - fill: var(--theme--text_ui_info); - z-index: 1; -} - -/* restore icon sets modal */ - -.notion-icons--overlay-container { - position: fixed; - top: 0; - bottom: 0; - left: 0; - right: 0; - z-index: 999; - overflow: hidden; -} - -.notion-icons--restore { - max-width: 320px; - max-height: 320px; - position: relative; - border-radius: 3px; - padding: 8px 0; - box-shadow: var(--theme--box-shadow_strong); - background: var(--theme--card); - overflow: hidden auto; -} - -.notion-icons--removed-set { - display: flex; - align-items: center; - width: 100%; - padding: 8px 14px; - user-select: none; - cursor: pointer; - transition: background 0.4s ease; -} - -/* animation */ - -@keyframes rotation { - from { - transform: rotate(0deg); - } - to { - transform: rotate(359deg); - } -} diff --git a/repo/icon-sets/client.css b/repo/icon-sets/client.css index 1ab2fad..53b9ff7 100644 --- a/repo/icon-sets/client.css +++ b/repo/icon-sets/client.css @@ -33,7 +33,7 @@ background: var(--theme--ui_interactive-active); } -.icon_sets--scroller { +.icon_sets--view { padding: 0; overflow: hidden; display: flex; @@ -46,6 +46,9 @@ padding: 10px 14px; } +.icon_sets--actions > .notion-focusable-within { + flex-grow: 1; +} .icon_sets--link_input { flex-grow: 1; font-size: 14px; @@ -105,36 +108,74 @@ } .icon_sets--list { + /* scroller */ height: 100%; word-break: break-all; overflow: hidden auto; padding: 0 14px 10px 14px; } -.icon_sets--title { +.icon_sets--error { + color: var(--theme--accent_red); +} +.icon_sets--title, +.icon_sets--error { margin: 6px 0 8px 0; - color: var(--theme--text_secondary); font-size: 11px; font-weight: 500; line-height: 1.2; user-select: none; text-transform: uppercase; - cursor: pointer; border-radius: 2px; padding: 0.25em; display: flex; align-items: center; } + +.icon_sets--title { + cursor: pointer; + color: var(--theme--text_secondary); +} .icon_sets--title:hover { background: var(--theme--ui_interactive-hover); } .icon_sets--title:active { background: var(--theme--ui_interactive-active); } + .icon_sets--title .info { + /* tooltips */ height: 1em; margin-left: 0.5em; } + +.icon_sets--spinner { + margin-left: 0.5em; + height: 1em; + width: 1em; +} +.icon_sets--spinner img { + width: 100%; + height: 100%; + animation: rotation 1.3s infinite linear; +} +@keyframes rotation { + from { + transform: rotate(0deg); + } + to { + transform: rotate(359deg); + } +} + +.icon_sets--title a { + color: currentColor; + transition: color 100ms ease-in; +} +.icon_sets--title a:hover { + color: var(--theme--accent_blue); +} + .icon_sets--title .triangle { height: 1em; width: 0.9em; @@ -177,3 +218,16 @@ max-width: 24px; max-height: 24px; } +.icon_sets--sprite { + width: 24px; + height: 24px; + background-size: 24px; + background-repeat: no-repeat; + pointer-events: none; +} + +.icon_sets--divider { + height: 1px; + margin: 1em 0; + border-bottom: 1px solid var(--theme--ui_divider); +} diff --git a/repo/icon-sets/client.mjs b/repo/icon-sets/client.mjs index 47507ee..c21886d 100644 --- a/repo/icon-sets/client.mjs +++ b/repo/icon-sets/client.mjs @@ -6,12 +6,26 @@ * (https://notion-enhancer.github.io/) under the MIT license */ -export default async function ({ web, components, notion }, db) { +export default async function ({ web, fs, components, notion }, db) { const recentUploads = await db.get(['recent_uploads'], []), $triangleSvg = web.html` `; + const customIconSets = [], + customIconsFile = await db.get(['json']); + if (customIconsFile?.content) { + const iconsData = JSON.parse(customIconsFile.content); + customIconSets.push(...(iconsData.icons || iconsData)); + } + + const enhancerIconSets = [], + enhancerIconsUrl = 'https://raw.githubusercontent.com/notion-enhancer/icons/main/'; + if (await db.get(['default_sets'])) { + const iconsData = await fs.getJSON(`${enhancerIconsUrl}/icons.json`); + enhancerIconSets.push(...(iconsData.icons || iconsData)); + } + const mediaMenuSelector = '.notion-media-menu', mediaScrollerSelector = '.notion-media-menu > .notion-scroller', mediaFilterSelector = '.notion-media-menu > :first-child > :last-child', @@ -19,25 +33,35 @@ export default async function ({ web, components, notion }, db) { tabBtnSelector = (n) => `.notion-media-menu > :first-child > :first-child > :nth-child(${n})`; - const renderSetTitle = async (id, title, $tooltip = undefined) => { - const isCollapsed = await db.get(['collapsed', id], false), + const renderSetTitle = async (title, loadPromises = [], $tooltip = undefined) => { + const isCollapsed = await db.get(['collapsed', title], false), $title = web.html`

`; + ${isCollapsed ? 'data-collapsed="true"' : ''}>

`, + $spinner = web.html` + + `; web.render( $title, $triangleSvg.cloneNode(true), - web.html`${web.escape(title)}` + web.html`${title}`, + $spinner ); $title.addEventListener('click', () => { const newState = $title.dataset.collapsed !== 'true'; - db.set(['collapsed', id], newState); + db.set(['collapsed', title], newState); $title.dataset.collapsed = newState; }); - if ($tooltip) { - const $infoSvg = web.html`${await components.feather('info', { class: 'info' })}`; - components.setTooltip($infoSvg, $tooltip); - web.render($title, $infoSvg); - } + // hide spinner after all icons finish loading + // doesn't need to be waited on by renderers + (async () => { + await Promise.all(loadPromises); + $spinner.remove(); + if ($tooltip) { + const $infoSvg = web.html`${await components.feather('info', { class: 'info' })}`; + components.setTooltip($infoSvg, $tooltip); + web.render($title, $infoSvg); + } + })(); return $title; }; @@ -57,26 +81,23 @@ export default async function ({ web, components, notion }, db) { ), // sets $setsList = web.html`
`, - $recentUploadsTitle = await renderSetTitle( - 'recent_uploads', - 'Recent', - web.html`

Click to reuse an icon
Shift-click to remove it

` - ), - $recentUploads = web.html`
`, // container - $iconsScroller = web.render( - web.html``, + $iconsView = web.render( + web.html``, web.render( web.html`
`, - $iconsLinkInput, - $iconsLinkSubmit, + web.render( + web.html`
`, + $iconsLinkInput, + $iconsLinkSubmit + ), $iconsUploadSubmit ), - web.render($setsList, $recentUploadsTitle, $recentUploads) + web.render($setsList) ); let $mediaMenu, $activeTabUnderline; - const insertIconsTab = async (event) => { + const insertIconsTab = async () => { if (document.contains($mediaMenu)) return; // prevent injection into file upload menus @@ -94,10 +115,11 @@ export default async function ({ web, components, notion }, db) { $activeTabUnderline = $emojiTab.children[1] || $uploadTab.children[1] || $linkTab.children[1]; $emojiTab.after($iconsTab); - $emojiScroller.after($iconsScroller); + $emojiScroller.after($iconsView); - const renderRecentUploads = () => { - web.empty($recentUploads); + const renderRecentUploads = async () => { + const $recentUploads = web.html`
`, + loadPromises = []; for (let i = recentUploads.length - 1; i >= 0; i--) { const { signed, url } = recentUploads[i], $icon = web.html` @@ -111,23 +133,117 @@ export default async function ({ web, components, notion }, db) { $icon.remove(); } else setIcon(url); }); + loadPromises.push( + new Promise((res, rej) => { + $icon.firstElementChild.onload = res; + $icon.firstElementChild.onerror = res; + }) + ); } - $recentUploads.style.height = `${$recentUploads.scrollHeight}px`; - }, - renderSets = async () => {}; - const displayIconsTab = (force = false) => { + const $recentUploadsTitle = await renderSetTitle( + 'Recent', + loadPromises, + web.html`

Click to reuse an icon
Shift-click to remove it

` + ); + web.render($setsList, $recentUploadsTitle, $recentUploads); + }, + renderIconSet = async (iconData, enhancerSet = false) => { + try { + const $set = web.html`
`; + if (iconData.sourceUrl?.endsWith?.('/')) { + iconData.sourceUrl = iconData.sourceUrl.slice(0, -1); + } + + const loadPromises = []; + for (let i = 0; i < (iconData.count || iconData.source.length); i++) { + const iconUrl = iconData.sourceUrl + ? Array.isArray(iconData.source) + ? `${iconData.sourceUrl}/${iconData.source[i]}.${iconData.extension}` + : `${iconData.sourceUrl}/${iconData.source}_${i}.${iconData.extension}` + : iconData.source[i], + sprite = enhancerSet + ? `style=" + background-image: url(${enhancerIconsUrl}${iconData.source}/sprite.png); + background-position: 0 -${i * 24}px; + "` + : '', + $img = sprite + ? web.html`
` + : web.html``, + $icon = web.render(web.html``, $img); + web.render($set, $icon); + $icon.addEventListener('click', (event) => { + if (!event.shiftKey) setIcon(iconUrl); + }); + if (!sprite) { + loadPromises.push( + new Promise((res, rej) => { + $img.onload = res; + $img.onerror = res; + }) + ); + } + } + + const author = iconData.author + ? iconData.authorUrl + ? web.raw`by + ${iconData.author} + ` + : web.raw`by ${web.escape(iconData.author)}` + : '', + $title = await renderSetTitle( + `${web.escape(iconData.name)} ${author}`, + loadPromises + ); + web.render($setsList, $title, $set); + } catch (err) { + console.log(err); + web.render( + $setsList, + web.html`
+ Invalid set: ${web.escape(iconData?.name || 'Unknown')} +
` + ); + } + }, + renderCustomIconSets = async () => { + if (customIconSets.length) { + web.render($setsList, web.html`
`); + } + await Promise.all(customIconSets.map((set) => renderIconSet(set))); + }, + renderEnhancerIconSets = async () => { + if (enhancerIconSets.length) { + web.render($setsList, web.html`
`); + } + await Promise.all( + enhancerIconSets.map((set) => { + set.sourceUrl = set.sourceUrl || enhancerIconsUrl + set.source; + return renderIconSet(set, true); + }) + ); + }; + + const displayIconsTab = async (force = false) => { if ($iconsTab.contains($activeTabUnderline) && !force) return; web.render($iconsTab, $activeTabUnderline); - $iconsScroller.style.display = ''; + $iconsView.style.display = ''; $emojiScroller.style.display = 'none'; $emojiFilter.style.display = 'none'; - renderRecentUploads(); + web.empty($setsList); + await renderRecentUploads(); + await renderCustomIconSets(); + await renderEnhancerIconSets(); + $iconsView.querySelectorAll('.icon_sets--set').forEach(($set) => { + $set.style.height = `${$set.scrollHeight}px`; + }); }, displayEmojiTab = (force = false) => { if ($emojiTab.contains($activeTabUnderline) && !force) return; web.render($emojiTab, $activeTabUnderline); - $iconsScroller.style.display = 'none'; + $iconsView.style.display = 'none'; $emojiScroller.style.display = ''; $emojiFilter.style.display = ''; }; diff --git a/repo/icon-sets/icons/remove.svg b/repo/icon-sets/icons/remove.svg deleted file mode 100644 index b6a57a6..0000000 --- a/repo/icon-sets/icons/remove.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - \ No newline at end of file diff --git a/repo/icon-sets/icons/restore.svg b/repo/icon-sets/icons/restore.svg deleted file mode 100644 index 2e41e4c..0000000 --- a/repo/icon-sets/icons/restore.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - \ No newline at end of file diff --git a/repo/icon-sets/icons/search.svg b/repo/icon-sets/icons/search.svg deleted file mode 100644 index e8c0216..0000000 --- a/repo/icon-sets/icons/search.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - \ No newline at end of file diff --git a/repo/icon-sets/icons/triangle.svg b/repo/icon-sets/icons/triangle.svg deleted file mode 100644 index 8615536..0000000 --- a/repo/icon-sets/icons/triangle.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/repo/icon-sets/mod.js b/repo/icon-sets/mod.js deleted file mode 100644 index d41d9bd..0000000 --- a/repo/icon-sets/mod.js +++ /dev/null @@ -1,185 +0,0 @@ -// source => icon data -const enhancerIconSets = new Map(); -getAsync(notionIconsUrl + 'icons.json', (iconsData) => { - const data = JSON.parse(iconsData); - (data.icons || data).forEach((set) => { - enhancerIconSets.set(set.source, set); - }); -}); - -// array -let customIconSets; -if (store().json) { - const customData = JSON.parse(fs.readFileSync(store().json)); - customIconSets = customData.icons || customData; -} - -// convert icons data into renderable -function loadIconSets() { - const iconSets = new DocumentFragment(); - - if (customIconSets) { - customIconSets.forEach((i) => { - iconSets.appendChild(renderIconSet(i)); - }); - - // divider - iconSets.appendChild(createElement('
')); - } - - if (enhancerIconSets.size > 0) { - enhancerIconSets.forEach((i, source) => { - // ignore removed icon sets - if (store().removedSets?.includes(source)) return; - - i.sourceUrl = i.sourceUrl || notionIconsUrl + source; - iconSets.appendChild(renderIconSet(i, true)); - }); - } - - return iconSets; -} - -// returns icon set element -function renderIconSet(iconData, enhancerSet = false) { - const iconSet = createElement('
'); - - try { - const author = iconData.author - ? iconData.authorUrl - ? ` by ${iconData.author}` - : ` by ${iconData.author}` - : ''; - - const toggle = createElement(` -
- ${menuIcons.triangle} -
${iconData.name}${author}
-
-
- -
-
-
- `); - - const iconSetBody = createElement('
'); - - iconSet.append(toggle, iconSetBody); - - const promiseArray = []; - // render icons - for (let i = 0; i < (iconData.count || iconData.source.length); i++) { - const iconUrl = iconData.sourceUrl - ? Array.isArray(iconData.source) - ? `${iconData.sourceUrl}/${iconData.source[i]}.${iconData.extension}` - : `${iconData.sourceUrl}/${iconData.source}_${i}.${iconData.extension}` - : iconData.source[i]; - - const icon = createElement(`
`); - icon.innerHTML = enhancerSet - ? // load sprite sheet - `
` - : ``; - - // add filters to filterMap - const filters = []; - - if (iconData.filter) { - if (iconData.filter === 'source') { - const filename = iconUrl.match(/.*\/(.+?)\./); - if (filename?.length > 1) { - filters.push(...filename[1].split(/[ \-_]/)); - } - } else if (Array.isArray(iconData.filter)) { - filters.push(...iconData.filter[i]); - } - icon.setAttribute('filter', filters.join(' ')); - } - - // add set name and author to filters - filters.push(...iconData.name.toLowerCase().split(' ')); - if (iconData.author) filters.push(...iconData.author.toLowerCase().split(' ')); - - filterMap.set(icon, filters); - - // make sure icons load - if (!enhancerSet) { - promiseArray.push( - new Promise((resolve, reject) => { - icon.firstChild.onload = resolve; - icon.firstChild.onerror = () => { - reject(); - icon.classList.add('error'); - icon.innerHTML = '!'; - }; - }) - ); - } - - garbageCollector.push(icon); - icon.addEventListener('click', () => setPageIcon(iconUrl)); - iconSetBody.appendChild(icon); - } - - // hide spinner after all icons finish loading - (async () => { - const spinner = toggle.querySelector('.notion-icons--spinner'), - loadPromise = Promise.all(promiseArray); - loadPromise.then( - () => spinner.remove(), - () => { - iconSet.classList.add('alert'); - spinner.remove(); - } - ); - })(); - - // add remove icon set button - if (enhancerSet) { - const removeButton = createElement( - `
${menuIcons.remove}
` - ); - removeButton.addEventListener('click', (e) => { - e.stopPropagation(); - removeIconSet(iconData); - }); - iconSet.querySelector('.notion-icons--actions').appendChild(removeButton); - } - - // set up toggle - toggle.addEventListener('click', (e) => { - if (e.target.nodeName === 'A') return; - toggleIconSet(iconSet); - }); - - // hide by default? - if (store().hide) requestAnimationFrame(() => toggleIconSet(iconSet)); - - // tooltip - let timeout; - iconSetBody.addEventListener('mouseover', (e) => { - const el = e.target; - if (!el.hasAttribute('filter')) return; - - document.querySelector('.notion-icons--tooltip')?.remove(); - timeout = setTimeout(() => { - renderTooltip(el, el.getAttribute('filter')); - }, 300); - }); - iconSetBody.addEventListener('mouseout', (e) => { - const el = e.target; - if (!el.hasAttribute('filter')) return; - - document.querySelector('.notion-icons--tooltip')?.remove(); - clearTimeout(timeout); - }); - } catch (err) { - iconSet.classList.add('error'); - iconSet.innerHTML = `Invalid Icon Set: ${iconData.name}`; - } - - return iconSet; -}