tooltips ✔

This commit is contained in:
dragonwocky 2021-04-27 20:21:34 +10:00
parent 1d9785f804
commit 16ab3a4969
6 changed files with 104 additions and 76 deletions

View File

@ -406,9 +406,9 @@ registry.validate = async (mod, err, check) => {
), ),
check('option.label', option.label, typeof option.label === 'string'), check('option.label', option.label, typeof option.label === 'string'),
check( check(
'option.description', 'option.tooltip',
option.description, option.tooltip,
!option.description || typeof option.description === 'string' !option.tooltip || typeof option.tooltip === 'string'
), ),
]; ];
}) })

View File

@ -0,0 +1,2 @@
<!-- https://fontawesome.com/icons/question-circle?style=solid -->
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path fill="currentColor" d="M504 256c0 136.997-111.043 248-248 248S8 392.997 8 256C8 119.083 119.043 8 256 8s248 111.083 248 248zM262.655 90c-54.497 0-89.255 22.957-116.549 63.758-3.536 5.286-2.353 12.415 2.715 16.258l34.699 26.31c5.205 3.947 12.621 3.008 16.665-2.122 17.864-22.658 30.113-35.797 57.303-35.797 20.429 0 45.698 13.148 45.698 32.958 0 14.976-12.363 22.667-32.534 33.976C247.128 238.528 216 254.941 216 296v4c0 6.627 5.373 12 12 12h56c6.627 0 12-5.373 12-12v-1.333c0-28.462 83.186-29.647 83.186-106.667 0-58.002-60.165-102-116.531-102zM256 338c-25.365 0-46 20.635-46 46 0 25.364 20.635 46 46 46s46-20.636 46-46c0-25.365-20.635-46-46-46z"></path></svg>

After

Width:  |  Height:  |  Size: 795 B

View File

@ -26,6 +26,7 @@ body {
background: var(--theme--sidebar); background: var(--theme--sidebar);
color: var(--theme--text); color: var(--theme--text);
font-family: var(--theme--font_sans); font-family: var(--theme--font_sans);
min-height: 100vh;
} }
body a { body a {
color: inherit; color: inherit;
@ -66,7 +67,7 @@ img[data-view-target='notion'] {
cursor: pointer; cursor: pointer;
} }
[data-container] { main {
display: grid; display: grid;
grid-gap: 1.25em; grid-gap: 1.25em;
grid-template-columns: 1fr; grid-template-columns: 1fr;
@ -74,62 +75,62 @@ img[data-view-target='notion'] {
transition: opacity 200ms ease-out; transition: opacity 200ms ease-out;
} }
@media (min-width: 550px) { @media (min-width: 550px) {
[data-container] { main {
grid-template-columns: 1fr 1fr; grid-template-columns: 1fr 1fr;
} }
[data-container='mod'] > .documentation--buttons { [data-view='mod'] main > .documentation--buttons {
grid-column: span 2; grid-column: span 2;
} }
[data-container='mod'] .library--card, [data-view='mod'] main .library--card,
.documentation--body { .documentation--body {
max-height: calc(100vh - 10rem); max-height: calc(100vh - 10rem);
overflow-y: auto; overflow-y: auto;
} }
[data-view='mod'] {
overflow: hidden;
height: 100vh;
}
} }
@media (min-width: 850px) { @media (min-width: 850px) {
[data-container] { main {
grid-template-columns: 1fr 1fr 1fr; grid-template-columns: 1fr 1fr 1fr;
} }
[data-container='mod'] > .documentation--buttons { [data-view='mod'] main > .documentation--buttons {
grid-column: span 3; grid-column: span 3;
} }
[data-container='mod'] > .documentation--body { [data-view='mod'] main > .documentation--body {
grid-column: span 2; grid-column: span 2;
} }
[data-container='mod'] {
overflow: hidden;
max-height: calc(100vh);
}
} }
@media (min-width: 1350px) { @media (min-width: 1350px) {
[data-container] { main {
grid-template-columns: 1fr 1fr 1fr 1fr; grid-template-columns: 1fr 1fr 1fr 1fr;
} }
[data-container='mod'] > .documentation--buttons { [data-view='mod'] main > .documentation--buttons {
grid-column: span 4; grid-column: span 4;
} }
[data-container='mod'] > .documentation--body { [data-view='mod'] main > .documentation--body {
grid-column: span 3; grid-column: span 3;
} }
} }
@media (min-width: 2050px) { @media (min-width: 2050px) {
[data-container] { main {
grid-template-columns: 1fr 1fr 1fr 1fr 1fr; grid-template-columns: 1fr 1fr 1fr 1fr 1fr;
} }
[data-container='mod'] > .documentation--buttons { [data-view='mod'] main > .documentation--buttons {
grid-column: span 5; grid-column: span 5;
} }
[data-container='mod'] > .documentation--body { [data-view='mod'] main > .documentation--body {
grid-column: span 4; grid-column: span 4;
} }
} }
[data-container] article { main article {
border-radius: 5px; border-radius: 5px;
box-shadow: rgb(0 0 0 / 10%) 0px 20px 25px -5px, rgb(0 0 0 / 4%) 0px 10px 10px -5px; box-shadow: rgb(0 0 0 / 10%) 0px 20px 25px -5px, rgb(0 0 0 / 4%) 0px 10px 10px -5px;
border: 1px solid var(--theme--divider); border: 1px solid var(--theme--divider);
background: var(--theme--page); background: var(--theme--page);
} }
[data-container] article img { main article img {
max-width: 100%; max-width: 100%;
} }
@ -244,6 +245,12 @@ label p > span:not([class]),
label > span:not([class]) { label > span:not([class]) {
font-size: 1rem; font-size: 1rem;
} }
label [data-icon='fa/question-circle'] {
height: var(--theme--font_ui_small-size);
width: var(--theme--font_ui_small-size);
margin-left: 0.25em;
margin-bottom: -1px;
}
.library--toggle_label > p, .library--toggle_label > p,
.library--toggle_label > p, .library--toggle_label > p,
.library--select_label > p, .library--select_label > p,
@ -255,7 +262,6 @@ label > span:not([class]) {
.library--toggle_label > :not(input) { .library--toggle_label > :not(input) {
align-items: center; align-items: center;
display: flex; display: flex;
cursor: pointer;
--toggle--bg: var(--theme--toggle_off); --toggle--bg: var(--theme--toggle_off);
--toggle--translation: 0%; --toggle--translation: 0%;
} }
@ -267,6 +273,7 @@ label > span:not([class]) {
display: block; display: block;
border-radius: 40px; border-radius: 40px;
background: var(--toggle--bg); background: var(--toggle--bg);
cursor: pointer;
} }
.library--toggle_label > :not(input) .library--toggle::after { .library--toggle_label > :not(input) .library--toggle::after {
content: ''; content: '';
@ -319,6 +326,7 @@ label > span:not([class]) {
} }
.library--select_label .library--select select { .library--select_label .library--select select {
outline: none; outline: none;
cursor: pointer;
} }
.library--select_label .library--select select option { .library--select_label .library--select select option {
background: var(--theme--tag_select); background: var(--theme--tag_select);
@ -327,6 +335,7 @@ label > span:not([class]) {
.library--file_label .library--file { .library--file_label .library--file {
padding: 0; padding: 0;
display: flex; display: flex;
cursor: pointer;
} }
.library--select_label .library--select > :first-child, .library--select_label .library--select > :first-child,
.library--file_label .library--file > :first-child { .library--file_label .library--file > :first-child {
@ -365,7 +374,7 @@ label > span:not([class]) {
.library--card > div { .library--card > div {
padding: 1rem; padding: 1rem;
} }
[data-container='mod'] .library--card { [data-view='mod'] main .library--card {
overflow-x: auto; overflow-x: auto;
} }
.documentation--body { .documentation--body {
@ -374,18 +383,18 @@ label > span:not([class]) {
overflow-x: auto; overflow-x: auto;
} }
.tooltip--list > div { .tooltip {
position: absolute; position: absolute;
background: var(--theme--tooltip); background: var(--theme--tooltip);
color: var(--theme--tooltip-text); color: var(--theme--tooltip-text);
font-size: var(--theme--font_ui_small-size); font-size: var(--theme--font_ui_small-size);
padding: 0.5rem; padding: 0.15rem 0.4rem;
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06) !important; box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06) !important;
border-radius: 3px; border-radius: 3px;
max-width: 20rem; max-width: 20rem;
display: none; display: none;
} }
.tooltip--list p { .tooltip p {
margin: 0.25rem 0; margin: 0.25rem 0;
} }

View File

@ -5,7 +5,7 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>notion-enhancer</title> <title>notion-enhancer</title>
</head> </head>
<body> <body data-view>
<header> <header>
<h1> <h1>
<img data-view-target="notion" alt="" width="24" src="../../icons/colour.svg" /> <img data-view-target="notion" alt="" width="24" src="../../icons/colour.svg" />
@ -20,8 +20,8 @@
</h1> </h1>
<h1><i data-icon="fa/discord"></i><a href="https://discord.gg/sFWPXtA">support</a></h1> <h1><i data-icon="fa/discord"></i><a href="https://discord.gg/sFWPXtA">support</a></h1>
</header> </header>
<main data-container></main> <main></main>
<section class="tooltip--list"></section> <section class="tooltip"></section>
<footer class="notification--list"></footer> <footer class="notification--list"></footer>
<script src="./menu.js" type="module"></script> <script src="./menu.js" type="module"></script>
</body> </body>

View File

@ -20,21 +20,25 @@ document
.addEventListener('click', env.focusNotion); .addEventListener('click', env.focusNotion);
web.hotkeyListener(['Ctrl', 'Alt', 'E'], env.focusNotion); web.hotkeyListener(['Ctrl', 'Alt', 'E'], env.focusNotion);
const tooltips = { const hovertip = {
$list: document.querySelector('.tooltip--list'), $el: document.querySelector('.tooltip'),
_generate($target, text) { add($parent, selector, text) {
const $tooltip = web.createElement(web.html`<div>${fmt.md.render(text)}</div>`); text = fmt.md.render(text);
this.$list.appendChild($tooltip); $parent.addEventListener('mouseover', (event) => {
$target.addEventListener('mouseover', (event) => { if (event.target.matches(selector) || event.target.matches(`${selector} *`)) {
$tooltip.style.display = 'block'; this.$el.innerHTML = text;
this.$el.style.display = 'block';
}
}); });
$target.addEventListener('mousemove', (event) => { $parent.addEventListener('mousemove', (event) => {
$tooltip.style.top = event.clientY - $tooltip.clientHeight + 'px'; this.$el.style.top = event.clientY - this.$el.clientHeight + 'px';
$tooltip.style.left = this.$el.style.left =
event.clientX < window.innerWidth / 2 ? event.clientX + 20 + 'px' : ''; event.clientX < window.innerWidth / 2 ? event.clientX + 20 + 'px' : '';
}); });
$target.addEventListener('mouseout', (event) => { $parent.addEventListener('mouseout', (event) => {
$tooltip.style.display = ''; if (event.target.matches(selector) || event.target.matches(`${selector} *`)) {
this.$el.style.display = '';
}
}); });
}, },
}; };
@ -108,7 +112,7 @@ components.card = {
}, },
}; };
components.options = { components.options = {
async toggle(id, { key, label, description }) { async toggle(id, { key, label, tooltip }) {
const state = await storage.get(id, key), const state = await storage.get(id, key),
opt = web.createElement(web.html`<label opt = web.createElement(web.html`<label
for="toggle--${web.escapeHtml(`${id}.${key}`)}" for="toggle--${web.escapeHtml(`${id}.${key}`)}"
@ -116,19 +120,24 @@ components.options = {
> >
<input type="checkbox" id="toggle--${web.escapeHtml(`${id}.${key}`)}" <input type="checkbox" id="toggle--${web.escapeHtml(`${id}.${key}`)}"
${state ? 'checked' : ''}/> ${state ? 'checked' : ''}/>
<p><span>${label}</span><span class="library--toggle"></span></p <p>
></label>`); <span data-tooltip>${web.escapeHtml(label)}
<i data-icon="fa/question-circle"></i></span>
<span class="library--toggle"></span>
</p>
</label>`);
opt.addEventListener('change', (event) => storage.set(id, key, event.target.checked)); opt.addEventListener('change', (event) => storage.set(id, key, event.target.checked));
tooltips._generate(opt, description); hovertip.add(opt, '[data-tooltip]', tooltip);
return opt; return opt;
}, },
async select(id, { key, label, description, values }) { async select(id, { key, label, tooltip, values }) {
const state = await storage.get(id, key), const state = await storage.get(id, key),
opt = web.createElement(web.html`<label opt = web.createElement(web.html`<label
for="select--${web.escapeHtml(`${id}.${key}`)}" for="select--${web.escapeHtml(`${id}.${key}`)}"
class="library--select_label" class="library--select_label"
> >
<p>${label}</p> <p><span data-tooltip>${web.escapeHtml(label)}
<i data-icon="fa/question-circle"></i></span></p>
<p class="library--select"> <p class="library--select">
<span><i data-icon="fa/caret-down"></i></span> <span><i data-icon="fa/caret-down"></i></span>
<select id="select--${web.escapeHtml(`${id}.${key}`)}"> <select id="select--${web.escapeHtml(`${id}.${key}`)}">
@ -142,40 +151,44 @@ components.options = {
</p> </p>
</label>`); </label>`);
opt.addEventListener('change', (event) => storage.set(id, key, event.target.value)); opt.addEventListener('change', (event) => storage.set(id, key, event.target.value));
tooltips._generate(opt, description); hovertip.add(opt, '[data-tooltip]', tooltip);
return opt; return opt;
}, },
async text(id, { key, label, description }) { async text(id, { key, label, tooltip }) {
const state = await storage.get(id, key), const state = await storage.get(id, key),
opt = web.createElement(web.html`<label opt = web.createElement(web.html`<label
for="text--${web.escapeHtml(`${id}.${key}`)}" for="text--${web.escapeHtml(`${id}.${key}`)}"
class="library--text_label" class="library--text_label"
> >
<p>${label}</p> <p><span data-tooltip>${web.escapeHtml(label)}
<textarea id="text--${web.escapeHtml(`${id}.${key}`)}" rows="1">${state}</textarea> <i data-icon="fa/question-circle"></i></span></p>
<textarea id="text--${web.escapeHtml(`${id}.${key}`)}"
rows="1">${web.escapeHtml(state)}</textarea>
</label>`); </label>`);
opt.querySelector('textarea').addEventListener('input', (event) => { opt.querySelector('textarea').addEventListener('input', (event) => {
event.target.style.removeProperty('--txt--scroll-height'); event.target.style.removeProperty('--txt--scroll-height');
event.target.style.setProperty('--txt--scroll-height', event.target.scrollHeight + 'px'); event.target.style.setProperty('--txt--scroll-height', event.target.scrollHeight + 'px');
}); });
opt.addEventListener('change', (event) => storage.set(id, key, event.target.value)); opt.addEventListener('change', (event) => storage.set(id, key, event.target.value));
tooltips._generate(opt, description); hovertip.add(opt, '[data-tooltip]', tooltip);
return opt; return opt;
}, },
async number(id, { key, label, description }) { async number(id, { key, label, tooltip }) {
const state = await storage.get(id, key), const state = await storage.get(id, key),
opt = web.createElement(web.html`<label opt = web.createElement(web.html`<label
for="number--${web.escapeHtml(`${id}.${key}`)}" for="number--${web.escapeHtml(`${id}.${key}`)}"
class="library--number_label" class="library--number_label"
> >
<p>${web.escapeHtml(label)}</p> <p><span data-tooltip>${web.escapeHtml(label)}
<input id="number--${web.escapeHtml(`${id}.${key}`)}" type="number" value="${state}"/> <i data-icon="fa/question-circle"></i></span></p>
<input id="number--${web.escapeHtml(`${id}.${key}`)}"
type="number" value="${web.escapeHtml(state.toString())}"/>
</label>`); </label>`);
opt.addEventListener('change', (event) => storage.set(id, key, event.target.value)); opt.addEventListener('change', (event) => storage.set(id, key, event.target.value));
tooltips._generate(opt, description); hovertip.add(opt, '[data-tooltip]', tooltip);
return opt; return opt;
}, },
async file(id, { key, label, description, extensions }) { async file(id, { key, label, tooltip, extensions }) {
const state = await storage.get(id, key), const state = await storage.get(id, key),
opt = web.createElement(web.html`<label opt = web.createElement(web.html`<label
for="file--${web.escapeHtml(`${id}.${key}`)}" for="file--${web.escapeHtml(`${id}.${key}`)}"
@ -190,10 +203,11 @@ components.options = {
: '' : ''
)} )}
/> />
<p>${web.escapeHtml(label)}</p> <p><span data-tooltip>${web.escapeHtml(label)}
<i data-icon="fa/question-circle"></i></span></p>
<p class="library--file"> <p class="library--file">
<span><i data-icon="fa/file"></i></span> <span><i data-icon="fa/file"></i></span>
<span class="library--file_path">${state || 'choose file...'}</span> <span class="library--file_path">${web.escapeHtml(state || 'choose file...')}</span>
</p> </p>
</label>`); </label>`);
opt.addEventListener('change', (event) => { opt.addEventListener('change', (event) => {
@ -209,9 +223,10 @@ components.options = {
opt.addEventListener('click', (event) => { opt.addEventListener('click', (event) => {
document.documentElement.scrollTop = 0; document.documentElement.scrollTop = 0;
}); });
tooltips._generate( hovertip.add(
opt, opt,
`${description}\n\n**warning:** browser extensions do not have true filesystem access, '[data-tooltip]',
`${tooltip}\n\n**warning:** browser extensions do not have true filesystem access,
so the content of the file is saved on selection. after editing it, so the content of the file is saved on selection. after editing it,
the file will need to be re-selected.` the file will need to be re-selected.`
); );
@ -258,7 +273,7 @@ components.documentation = {
}, },
}; };
const views = { const views = {
$container: document.querySelector('[data-container]'), $container: document.querySelector('main'),
_router(event) { _router(event) {
event.preventDefault(); event.preventDefault();
let anchor, let anchor,
@ -295,7 +310,7 @@ const views = {
setTimeout(() => { setTimeout(() => {
this.$container.innerHTML = ''; this.$container.innerHTML = '';
this.$container.style.opacity = ''; this.$container.style.opacity = '';
this.$container.dataset.container = ''; document.body.dataset.view = '';
document document
.querySelector('[data-view-target][data-view-active]') .querySelector('[data-view-target][data-view-active]')
?.removeAttribute('data-view-active'); ?.removeAttribute('data-view-active');
@ -347,20 +362,22 @@ const views = {
document.querySelectorAll('[data-icon]').forEach((icon) => document.querySelectorAll('[data-icon]').forEach((icon) =>
fs.getText(`icons/${icon.dataset.icon}.svg`).then((svg) => { fs.getText(`icons/${icon.dataset.icon}.svg`).then((svg) => {
svg = web.createElement(svg); svg = web.createElement(svg);
svg.dataset.icon = icon.dataset.icon; for (const attr of icon.attributes) {
svg.setAttribute(attr.name, attr.value);
}
icon.replaceWith(svg); icon.replaceWith(svg);
}) })
); );
}, },
async mod(mod) { async mod(mod) {
this.$container.dataset.container = 'mod'; document.body.dataset.view = 'mod';
document.querySelector('header [data-view-target="library"]').dataset.active = true; document.querySelector('header [data-view-target="library"]').dataset.active = true;
this.$container.append(await components.documentation.buttons(mod)); this.$container.append(await components.documentation.buttons(mod));
this.$container.append(await components.options._generate(mod)); this.$container.append(await components.options._generate(mod));
this.$container.append(await components.documentation.readme(mod)); this.$container.append(await components.documentation.readme(mod));
}, },
async library() { async library() {
this.$container.dataset.container = 'library'; document.body.dataset.view = 'library';
document.querySelector('header [data-view-target="library"]').dataset.active = true; document.querySelector('header [data-view-target="library"]').dataset.active = true;
for (let mod of await registry.get()) for (let mod of await registry.get())
this.$container.append(await components.card._generate(mod)); this.$container.append(await components.card._generate(mod));
@ -413,7 +430,7 @@ const notifications = {
}, 100); }, 100);
return $notif; return $notif;
}, },
async load() { async fetch() {
const notifications = { const notifications = {
list: await fs.getJSON('https://notion-enhancer.github.io/notifications.json'), list: await fs.getJSON('https://notion-enhancer.github.io/notifications.json'),
dismissed: await storage.get(_id, 'notifications', []), dismissed: await storage.get(_id, 'notifications', []),
@ -441,7 +458,7 @@ const notifications = {
} }
}, },
}; };
notifications.load(); notifications.fetch();
async function theme() { async function theme() {
document.documentElement.className = `notion-${ document.documentElement.className = `notion-${

View File

@ -26,35 +26,35 @@
"key": "toggle", "key": "toggle",
"label": "toggle", "label": "toggle",
"value": true, "value": true,
"description": "a toggle" "tooltip": "a toggle"
}, },
{ {
"type": "select", "type": "select",
"key": "select", "key": "select",
"label": "select", "label": "select",
"values": ["option a", "option b", "option c"], "values": ["option a", "option b", "option c"],
"description": "a select" "tooltip": "a select"
}, },
{ {
"type": "text", "type": "text",
"key": "text", "key": "text",
"label": "text", "label": "text",
"value": "default", "value": "default",
"description": "a text input" "tooltip": "a text input"
}, },
{ {
"type": "number", "type": "number",
"key": "number", "key": "number",
"label": "number", "label": "number",
"value": 0, "value": 0,
"description": "a number input" "tooltip": "a number input"
}, },
{ {
"type": "file", "type": "file",
"key": "file", "key": "file",
"label": "file picker (.css only)", "label": "file picker (.css only)",
"extensions": [".css"], "extensions": [".css"],
"description": "a file picker" "tooltip": "a file picker"
} }
] ]
} }