/* * notion-enhancer core: menu * (c) 2021 dragonwocky (https://dragonwocky.me/) * (https://notion-enhancer.github.io/) under the MIT license */ 'use strict'; const _id = 'a6621988-551d-495a-97d8-3c568bca2e9e'; import { env, storage, web, fmt, fs, registry, regexers } from '../../api/_.mjs'; import * as router from './router.js'; const components = {}; components.card = async (mod) => { const $card = web.createElement(web.html`
${ mod.preview ? web.html`` : '' }
    ${mod.tags.map((tag) => web.html`
  • #${web.escapeHtml(tag)}
  • `).join('')}

${fmt.md.renderInline(mod.description)}

settings & documentation

`); $card.querySelector('.library--title input').addEventListener('change', async (event) => { storage.set('_mods', mod.id, event.target.checked); }); return $card; }; components.options = { async toggle(id, { key, label, tooltip }) { const state = await storage.get(id, key), opt = web.createElement(web.html` `); opt.addEventListener('change', (event) => storage.set(id, key, event.target.checked)); if (tooltip) web.addTooltip(opt.querySelector('[data-tooltip]'), tooltip); return opt; }, async select(id, { key, label, tooltip, values }) { const state = await storage.get(id, key), opt = web.createElement(web.html` `); opt.addEventListener('change', (event) => storage.set(id, key, event.target.value)); if (tooltip) web.addTooltip(opt.querySelector('[data-tooltip]'), tooltip); return opt; }, async text(id, { key, label, tooltip }) { const state = await storage.get(id, key), opt = web.createElement(web.html` `); opt.querySelector('textarea').addEventListener('input', (event) => { event.target.style.removeProperty('--txt--scroll-height'); event.target.style.setProperty( '--txt--scroll-height', event.target.scrollHeight + 1 + 'px' ); }); opt.addEventListener('change', (event) => storage.set(id, key, event.target.value)); if (tooltip) web.addTooltip(opt.querySelector('[data-tooltip]'), tooltip); return opt; }, async number(id, { key, label, tooltip }) { const state = await storage.get(id, key), opt = web.createElement(web.html` `); opt.addEventListener('change', (event) => storage.set(id, key, event.target.value)); 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` `); 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` `); opt.addEventListener('change', (event) => { const file = event.target.files[0], reader = new FileReader(); opt.querySelector('.library--file_path').innerText = file.name; reader.onload = (progress) => { storage.set(id, key, file.name); storage.set(id, `_file.${key}`, progress.currentTarget.result); }; reader.readAsText(file); }); opt.querySelector('.library--file_remove').addEventListener( 'click', (event) => { event.preventDefault(); opt.querySelector('input').value = ''; opt.querySelector('.library--file_path').innerText = 'choose file...'; storage.set(id, key, undefined); storage.set(id, `_file.${key}`, undefined); }, false ); opt.addEventListener('click', (event) => { document.documentElement.scrollTop = 0; }); web.addTooltip( opt.querySelector('[data-tooltip]'), `${tooltip ? `${tooltip}\n\n` : ''}**warning:** ${ 'browser extensions do not have true filesystem access, ' + 'so file content is only saved on selection. re-select files to apply edits.' }` ); return opt; }, }; const actionButtons = { _reloadTriggered: false, async reload($fragment = document) { let $reload = $fragment.querySelector('[data-reload]'); if (!$reload && this._reloadTriggered) { $reload = web.createElement(web.html` `); $reload.addEventListener('click', env.reload); $fragment.querySelector('.action--buttons').append($reload); await new Promise((res, rej) => requestAnimationFrame(res)); $reload.dataset.triggered = true; } }, async clearFilters($fragment = document) { let $clearFilters = $fragment.querySelector('[data-clear-filters]'); const search = router.getSearch(); if (search.get('tag') || search.has('enabled') || search.has('disabled')) { if (!$clearFilters) { $clearFilters = web.createElement(web.html` clear filters `); $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) => { actionButtons._reloadTriggered = true; actionButtons.reload(); router.load(); if (event.namespace === '_mods' && event.new === true) { const enabledTheme = (await registry.get()).find((mod) => mod.id === event.key); if ( enabledTheme.tags.includes('theme') && (await storage.get(_id, 'themes.autoresolve', true)) ) { for (const theme of await registry.get( (mod) => mod.tags.includes('theme') && mod.id !== enabledTheme.id && ((mod.tags.includes('dark') && enabledTheme.tags.includes('dark')) || (mod.tags.includes('light') && enabledTheme.tags.includes('light'))) )) { if (document.body.dataset.view === 'library') { const $toggle = document.getElementById(`enable--${theme.id}`); if ($toggle.checked) $toggle.click(); } else storage.set('_mods', theme.id, false); } } } }); router.addView( 'library', async () => { const $fragment = web.createFragment(web.html`

themes extensions enabled disabled

`); for (const mod of await registry.get( (mod) => !mod.environments || mod.environments.includes(env.name) )) { $fragment.append(await components.card(mod)); } actionButtons.reload($fragment); actionButtons.clearFilters($fragment); return $fragment; }, async (search = router.getSearch()) => { for (const [filter, active] of [ ['tag=theme', search.get('tag') === 'theme'], ['tag=extension', search.get('tag') === 'extension'], ['enabled', search.has('enabled')], ['disabled', search.has('disabled')], ]) { document .querySelector(`.action--buttons > [href="?view=library&${filter}"]`) .classList[active ? 'add' : 'remove']('action--active'); } 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(); } ); router.addView( 'mod', async () => { const mod = (await registry.get()).find((mod) => mod.id === router.getSearch().get('id')); if (!mod) return false; const $fragment = web.createFragment(web.html`

back to library view source code

`); const $card = await components.card(mod); $card.querySelector('.library--expand').remove(); if (mod.options && mod.options.length) { const options = web.createElement(web.html`
`); mod.options .filter((opt) => !opt.environments || opt.environments.includes(env.name)) .forEach(async (opt) => options.append(await components.options[opt.type](mod.id, opt)) ); $card.append(options); } $fragment.append( $card, web.createElement(web.html`
${ (await fs.isFile(`repo/${mod._dir}/README.md`)) ? fmt.md.render(await fs.getText(`repo/${mod._dir}/README.md`)) : '' }
`) ); fmt.Prism.highlightAllUnder($fragment); actionButtons.reload($fragment); return $fragment; }, () => { if (document.querySelector('[data-mod]').dataset.mod !== router.getSearch().get('id')) router.load(true); } ); router.setDefaultView('library'); router.load();