From 805cbb836d86d82e9e99550bc0529edc2db7aed2 Mon Sep 17 00:00:00 2001
From: dragonwocky
Date: Sat, 30 Oct 2021 22:31:49 +1100
Subject: [PATCH] extension: icon sets part 2 (enhancer + custom sets, sprites
& spinners)
---
repo/icon-sets/app.css | 411 ------------------------------
repo/icon-sets/client.css | 62 ++++-
repo/icon-sets/client.mjs | 182 ++++++++++---
repo/icon-sets/icons/remove.svg | 3 -
repo/icon-sets/icons/restore.svg | 3 -
repo/icon-sets/icons/search.svg | 3 -
repo/icon-sets/icons/triangle.svg | 1 -
repo/icon-sets/mod.js | 185 --------------
8 files changed, 207 insertions(+), 643 deletions(-)
delete mode 100644 repo/icon-sets/app.css
delete mode 100644 repo/icon-sets/icons/remove.svg
delete mode 100644 repo/icon-sets/icons/restore.svg
delete mode 100644 repo/icon-sets/icons/search.svg
delete mode 100644 repo/icon-sets/icons/triangle.svg
delete mode 100644 repo/icon-sets/mod.js
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;
-}