add new colour picker option

This commit is contained in:
dragonwocky 2021-05-13 10:59:08 +10:00
parent 6f3c1691c4
commit 99c166a1ac
7 changed files with 118 additions and 41 deletions

View File

@ -394,6 +394,9 @@ web.addTooltip = ($element, text) => {
* @namespace fmt
*/
export const fmt = {};
import './dep/jscolor.min.js';
/** color picker with alpha channel using https://jscolor.com/ */
fmt.JSColor = JSColor;
import './dep/prism.js';
/** syntax highlighting using https://prismjs.com/ */
fmt.Prism = Prism;
@ -525,6 +528,18 @@ regexers.url = (str, err = () => {}) => {
err(`invalid url ${str}`);
return env.ERROR;
};
/**
* check for a valid color (https://regexr.com/39cgj)
* @param {string} str - the string to test
* @param {function} err - a callback to execute if the test fails
* @returns {boolean | env.ERROR} true or the env.ERROR constant
*/
regexers.color = (str, err = () => {}) => {
const match = str.match(/^(?:#|0x)(?:[a-f0-9]{3}|[a-f0-9]{6})\b|(?:rgb|hsl)a?\([^\)]*\)$/i);
if (match && match.length) return true;
err(`invalid color ${str}`);
return env.ERROR;
};
/**
* an api for interacting with the enhancer's repository of mods
@ -704,6 +719,13 @@ registry.validate = async (mod, err, check) => {
check('option.value', option.value, typeof option.value === 'number')
);
break;
case 'color':
conditions.push(
check('option.value', option.value, typeof option.value === 'string').then(
(color) => (color === env.ERROR ? env.ERROR : regexers.color(color, err))
)
);
break;
case 'file':
conditions.push(
check(
@ -778,17 +800,14 @@ registry.defaults = async (id) => {
for (const opt of mod.options) {
switch (opt.type) {
case 'toggle':
case 'text':
case 'number':
case 'color':
defaults[opt.key] = opt.value;
break;
case 'select':
defaults[opt.key] = opt.values[0];
break;
case 'text':
defaults[opt.key] = opt.value;
break;
case 'number':
defaults[opt.key] = opt.value;
break;
case 'file':
defaults[opt.key] = undefined;
break;

1
extension/dep/jscolor.min.js vendored Normal file

File diff suppressed because one or more lines are too long

View File

@ -16,5 +16,10 @@ import(chrome.runtime.getURL('api.js')).then(({ web, registry }) => {
import(chrome.runtime.getURL(`repo/${mod._dir}/${script}`));
}
}
const errors = await registry.errors();
if (errors.length) {
console.log('notion-enhancer errors:');
console.table(errors);
}
});
});

View File

@ -14,7 +14,6 @@
- documentation e.g. \_file
- complete/bugfix theming variables
- color pickers
#### app-specific

View File

@ -25,7 +25,6 @@ web.whenReady([sidebarSelector]).then(async () => {
list: await fs.getJSON('https://notion-enhancer.github.io/notifications.json'),
dismissed: await storage.get(_id, 'notifications', []),
};
console.log($enhancerSidebarElement);
notifications.waiting = notifications.list.filter(
({ id }) => !notifications.dismissed.includes(id)
);

View File

@ -250,6 +250,7 @@ main article img {
.library--select_label,
.library--text_label,
.library--number_label,
.library--color_label,
.library--file_label {
margin: 0.6rem 0;
display: block;
@ -260,6 +261,7 @@ main article img {
.library--select_label *,
.library--text_label *,
.library--number_label *,
.library--color_label *,
.library--file_label * {
appearance: none;
font-family: var(--theme--font_sans);
@ -282,6 +284,7 @@ label [data-icon='fa/solid/question-circle'] {
.library--select_label > p,
.library--text_label > p,
.library--number_label > p,
.library--color_label > p,
.library--file_label > p {
margin: 0.6rem 0;
}
@ -320,10 +323,11 @@ label [data-icon='fa/solid/question-circle'] {
}
.library--toggle_label:focus-within .library--toggle,
.library--file_label:focus-within .library--file,
.library--color_label:focus-within .library--color,
.library--select_label .library--select:focus-within {
outline: solid thin;
outline: -webkit-focus-ring-color auto 1px;
}
.library--toggle_label input[type='checkbox'],
.library--toggle_label input,
.library--file_label input {
position: absolute;
width: 1px;
@ -343,6 +347,7 @@ label [data-icon='fa/solid/question-circle'] {
.library--text_label textarea,
.library--number_label input,
.library--select_label .library--select,
.library--color_label .library--color,
.library--file_label .library--file {
width: 100%;
padding: 6px 8px;
@ -351,35 +356,44 @@ label [data-icon='fa/solid/question-circle'] {
border: none;
box-shadow: var(--theme--input-border) 0px 0px 0px 1px inset;
}
.library--select_label .library--select select {
.library--select_label .library--select select,
.library--color_label .library--color input {
outline: none;
cursor: pointer;
}
.library--color_label .library--color input {
border-radius: 0;
}
.library--select_label .library--select select option {
background: var(--theme--tag_select);
}
.library--select_label .library--select,
.library--color_label .library--color,
.library--file_label .library--file {
padding: 0;
display: flex;
cursor: pointer;
}
.library--select_label .library--select > :first-child,
.library--color_label .library--color > :first-child,
.library--file_label .library--file > :first-child {
display: flex;
padding: 6px 8px;
background: var(--theme--input-border);
}
.library--select_label .library--select > :first-child svg,
.library--color_label .library--color > :first-child svg,
.library--file_label .library--file > :first-child svg {
width: 0.9em;
margin: auto 0;
}
.library--select_label .library--select > :first-child svg *,
.library--color_label .library--color > :first-child svg *,
.library--file_label .library--file > :first-child svg * {
color: var(--theme--input_icon);
}
.library--select_label .library--select > :last-child,
.library--color_label .library--color > :last-child,
.library--file_label .library--file > :last-child {
margin: auto 0;
padding: 6px 8px;

View File

@ -7,7 +7,7 @@
'use strict';
const _id = 'a6621988-551d-495a-97d8-3c568bca2e9e';
import { env, storage, web, fmt, fs, registry } from '../../api.js';
import { env, storage, web, fmt, fs, registry, regexers } from '../../api.js';
for (const mod of await registry.get((mod) => registry.isEnabled(mod.id))) {
for (const sheet of mod.css?.menu || []) {
@ -232,6 +232,46 @@ components.options = {
if (tooltip) web.addTooltip(opt.querySelector('[data-tooltip]'), tooltip);
return opt;
},
async color(id, { key, label, tooltip }) {
const state = await storage.get(id, key),
opt = web.createElement(web.html`
<label for="color--${web.escapeHtml(`${id}.${key}`)}" class="library--color_label">
<p class="library--color_title">
<span data-tooltip>${web.escapeHtml(label)}
<i data-icon="fa/solid/question-circle"></i>
</span>
<p class="library--color">
<span><i data-icon="fa/solid/eye-dropper"></i></span>
<input type="text" id="color--${web.escapeHtml(`${id}.${key}`)}"/>
</p>
</label>`);
const $fill = opt.querySelector('input'),
paintInput = () => {
$fill.style.background = picker.toBackground();
$fill.style.color = picker.isLight() ? '#000' : '#fff';
},
picker = new fmt.JSColor($fill, {
value: state,
previewSize: 0,
borderRadius: 3,
borderColor: 'var(--theme--divider)',
controlBorderColor: 'var(--theme--divider)',
backgroundColor: 'var(--theme--page)',
onInput() {
paintInput();
},
onChange() {
paintInput();
storage.set(id, key, this.toRGBAString());
},
});
paintInput();
opt.addEventListener('click', (event) => {
picker.show();
});
if (tooltip) web.addTooltip(opt.querySelector('[data-tooltip]'), tooltip);
return opt;
},
async file(id, { key, label, tooltip, extensions }) {
const state = await storage.get(id, key),
opt = web.createElement(web.html`
@ -295,15 +335,13 @@ const actionButtons = {
_reloadTriggered: false,
async reload($fragment = document) {
let $reload = $fragment.querySelector('[data-reload]');
if (!$reload) {
if (!$reload && this._reloadTriggered) {
$reload = web.createElement(web.html`
<button class="action--alert" data-reload>
<span><i data-icon="fa/solid/redo"></i></span>
<span>reload tabs to apply changes</span>
</button>`);
$reload.addEventListener('click', env.reloadTabs);
}
if (this._reloadTriggered) {
$fragment.querySelector('.action--buttons').append($reload);
await new Promise((res, rej) => requestAnimationFrame(res));
$reload.dataset.triggered = true;
@ -311,19 +349,19 @@ const actionButtons = {
},
async clearFilters($fragment = document) {
let $clearFilters = $fragment.querySelector('[data-clear-filters]');
if (!$clearFilters) {
$clearFilters = web.createElement(web.html`
<a class="action--alert" href="?view=library" data-clear-filters>
<span><i data-icon="fa/solid/times"></i></span>
<span>clear filters</span>
</a>`);
}
const search = router.getSearch();
if (search.get('tag') || search.has('enabled') || search.has('disabled')) {
$fragment.querySelector('.action--buttons').append($clearFilters);
await new Promise((res, rej) => requestAnimationFrame(res));
$clearFilters.dataset.triggered = true;
} else $clearFilters.remove();
if (!$clearFilters) {
$clearFilters = web.createElement(web.html`
<a class="action--alert" href="?view=library" data-clear-filters>
<span><i data-icon="fa/solid/times"></i></span>
<span>clear filters</span>
</a>`);
$fragment.querySelector('.action--buttons').append($clearFilters);
await new Promise((res, rej) => requestAnimationFrame(res));
$clearFilters.dataset.triggered = true;
}
} else if ($clearFilters) $clearFilters.remove();
},
};
storage.addChangeListener(async (event) => {
@ -395,22 +433,24 @@ router.addView(
.querySelector(`.action--buttons > [href="?view=library&${filter}"]`)
.classList[active ? 'add' : 'remove']('action--active');
}
for (const card of document.querySelectorAll('main > .library--card')) {
const { tags } = (await registry.get()).find((mod) => mod.id === card.dataset.mod),
isEnabled = await registry.isEnabled(card.dataset.mod);
if (
(search.has('tag') ? tags.includes(search.get('tag')) : true) &&
(search.has('enabled') && search.has('disabled')
? true
: search.has('enabled')
? isEnabled
: search.has('disabled')
? !isEnabled
: true)
) {
card.style.display = '';
} else card.style.display = 'none';
const visible = new Set();
for (const mod of await registry.get()) {
const isEnabled = await registry.isEnabled(mod.id),
filterConditions =
(search.has('tag') ? mod.tags.includes(search.get('tag')) : true) &&
(search.has('enabled') && search.has('disabled')
? true
: search.has('enabled')
? isEnabled
: search.has('disabled')
? !isEnabled
: true);
if (filterConditions) visible.add(mod.id);
}
for (const card of document.querySelectorAll('main > .library--card'))
card.style.display = 'none';
for (const card of document.querySelectorAll('main > .library--card'))
if (visible.has(card.dataset.mod)) card.style.display = '';
actionButtons.clearFilters();
}
);