mirror of
https://github.com/notion-enhancer/notion-enhancer.git
synced 2025-04-09 15:09:02 +00:00
side panel switcher
This commit is contained in:
parent
bc38f5d972
commit
6656b639d7
@ -72,61 +72,81 @@
|
|||||||
transform: translateX(calc(-1 * var(--component--panel-width)));
|
transform: translateX(calc(-1 * var(--component--panel-width)));
|
||||||
}
|
}
|
||||||
|
|
||||||
#enhancer--panel-header {
|
.enhancer--panel-view-title {
|
||||||
font-size: 1.35rem;
|
|
||||||
font-weight: bold;
|
|
||||||
display: flex;
|
|
||||||
padding: 0.75rem 1rem;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
#enhancer--panel-content {
|
|
||||||
font-size: 1rem;
|
|
||||||
padding: 0.75rem 1rem;
|
|
||||||
}
|
|
||||||
#enhancer--panel-header-title {
|
|
||||||
padding-left: 0.5em;
|
|
||||||
padding-bottom: 0.1em;
|
|
||||||
}
|
|
||||||
#enhancer--panel-header-title > p {
|
|
||||||
margin: 0;
|
margin: 0;
|
||||||
height: 1em;
|
height: 1em;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
font-size: 1.1rem;
|
||||||
|
font-weight: 600;
|
||||||
}
|
}
|
||||||
#enhancer--panel-header-title > p svg,
|
.enhancer--panel-view-title svg,
|
||||||
#enhancer--panel-header-title > p img {
|
.enhancer--panel-view-title img {
|
||||||
height: 1em;
|
height: 1em;
|
||||||
width: 1em;
|
width: 1em;
|
||||||
}
|
}
|
||||||
#enhancer--panel-header-title > p span {
|
.enhancer--panel-view-title span {
|
||||||
font-size: 0.9em;
|
font-size: 0.9em;
|
||||||
margin-left: 0.5em;
|
margin-left: 0.5em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#enhancer--panel-header {
|
||||||
|
font-size: 1.2rem;
|
||||||
|
font-weight: 600;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
cursor: pointer;
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
#enhancer--panel-header .enhancer--panel-view-title {
|
||||||
|
font-size: 1.2rem;
|
||||||
|
}
|
||||||
|
#enhancer--panel-content {
|
||||||
|
padding: 0.75rem 1rem;
|
||||||
|
font-size: 1rem;
|
||||||
|
}
|
||||||
|
#enhancer--panel-header-title {
|
||||||
|
padding: 0 0.5em 0.1em 1rem;
|
||||||
|
}
|
||||||
|
#enhancer--panel-header-switcher {
|
||||||
|
padding: 4px;
|
||||||
|
margin: 0.75rem 0;
|
||||||
|
}
|
||||||
#enhancer--panel-header-toggle {
|
#enhancer--panel-header-toggle {
|
||||||
margin-left: auto;
|
margin-left: auto;
|
||||||
|
padding-right: 1rem;
|
||||||
|
height: 100%;
|
||||||
|
width: 2.5em;
|
||||||
|
opacity: 0;
|
||||||
|
display: flex;
|
||||||
}
|
}
|
||||||
#enhancer--panel-header-toggle,
|
#enhancer--panel-header-toggle > div {
|
||||||
#enhancer--panel-header-switcher {
|
margin: auto 0 auto auto;
|
||||||
|
}
|
||||||
|
#enhancer--panel-header-switcher,
|
||||||
|
#enhancer--panel-header-toggle > div {
|
||||||
|
color: var(--theme--icon_secondary);
|
||||||
height: 1em;
|
height: 1em;
|
||||||
width: 1em;
|
width: 1em;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
opacity: 0;
|
|
||||||
transition: 300ms ease-in-out;
|
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
border-radius: 3px;
|
||||||
|
transition: 300ms ease-in-out;
|
||||||
}
|
}
|
||||||
#enhancer--panel-header-switcher svg {
|
#enhancer--panel-header-switcher:hover,
|
||||||
width: 0.5em;
|
#enhancer--panel-header-switcher:focus,
|
||||||
height: 0.5em;
|
#enhancer--panel-header-toggle > div:hover,
|
||||||
display: block;
|
#enhancer--panel-header-toggle > div:focus {
|
||||||
margin: auto;
|
background: var(--theme--ui_interactive-hover);
|
||||||
}
|
}
|
||||||
|
#enhancer--panel #enhancer--panel-header-toggle svg {
|
||||||
#enhancer--panel:not([data-enhancer-panel-pinned]) #enhancer--panel-header-toggle {
|
transition: 300ms ease-in-out;
|
||||||
|
}
|
||||||
|
#enhancer--panel:not([data-enhancer-panel-pinned]) #enhancer--panel-header-toggle svg {
|
||||||
transform: rotateZ(-180deg);
|
transform: rotateZ(-180deg);
|
||||||
}
|
}
|
||||||
#enhancer--panel:hover #enhancer--panel-header-toggle,
|
#enhancer--panel:hover #enhancer--panel-header-toggle {
|
||||||
#enhancer--panel:hover #enhancer--panel-header-switcher {
|
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -149,3 +169,39 @@
|
|||||||
#enhancer--panel[data-enhancer-panel-pinned] #enhancer--panel-resize:hover div {
|
#enhancer--panel[data-enhancer-panel-pinned] #enhancer--panel-resize:hover div {
|
||||||
background: var(--theme--ui_divider);
|
background: var(--theme--ui_divider);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#enhancer--panel-switcher-overlay-container {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
z-index: 999;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
#enhancer--panel-switcher {
|
||||||
|
max-width: 320px;
|
||||||
|
position: relative;
|
||||||
|
right: 14px;
|
||||||
|
border-radius: 3px;
|
||||||
|
padding: 8px 0;
|
||||||
|
background: var(--theme--bg_popup);
|
||||||
|
box-shadow: var(--theme--ui_shadow, rgba(15, 15, 15, 0.05)) 0px 0px 0px 1px,
|
||||||
|
var(--theme--ui_shadow, rgba(15, 15, 15, 0.1)) 0px 3px 6px,
|
||||||
|
var(--theme--ui_shadow, rgba(15, 15, 15, 0.2)) 0px 9px 24px !important;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
.enhancer--panel-switcher-item {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
width: 100%;
|
||||||
|
padding: 8px 14px;
|
||||||
|
user-select: none;
|
||||||
|
cursor: pointer;
|
||||||
|
overflow: hidden;
|
||||||
|
transition: background 300ms ease;
|
||||||
|
}
|
||||||
|
.enhancer--panel-switcher-item:hover,
|
||||||
|
.enhancer--panel-switcher-item:focus {
|
||||||
|
background: var(--theme--ui_interactive-hover);
|
||||||
|
}
|
||||||
|
@ -17,6 +17,15 @@ const db = await registry.db('36a2ffc9-27ff-480e-84a7-c7700a7d232d');
|
|||||||
let $panel,
|
let $panel,
|
||||||
_views = [];
|
_views = [];
|
||||||
|
|
||||||
|
const svgExpand = web.raw`<svg viewBox="-1 -1 9 11">
|
||||||
|
<path d="M 3.5 0L 3.98809 -0.569442L 3.5 -0.987808L 3.01191 -0.569442L 3.5 0ZM 3.5 9L 3.01191
|
||||||
|
9.56944L 3.5 9.98781L 3.98809 9.56944L 3.5 9ZM 0.488094 3.56944L 3.98809 0.569442L 3.01191
|
||||||
|
-0.569442L -0.488094 2.43056L 0.488094 3.56944ZM 3.01191 0.569442L 6.51191 3.56944L 7.48809
|
||||||
|
2.43056L 3.98809 -0.569442L 3.01191 0.569442ZM -0.488094 6.56944L 3.01191 9.56944L 3.98809
|
||||||
|
8.43056L 0.488094 5.43056L -0.488094 6.56944ZM 3.98809 9.56944L 7.48809 6.56944L 6.51191
|
||||||
|
5.43056L 3.01191 8.43056L 3.98809 9.56944Z"></path>
|
||||||
|
</svg>`;
|
||||||
|
|
||||||
export const panel = async (icon, title, generator = () => {}) => {
|
export const panel = async (icon, title, generator = () => {}) => {
|
||||||
_views.push({
|
_views.push({
|
||||||
icon: web.html`${icon}`,
|
icon: web.html`${icon}`,
|
||||||
@ -31,44 +40,31 @@ export const panel = async (icon, title, generator = () => {}) => {
|
|||||||
await web.whenReady([notionRightSidebarSelector]);
|
await web.whenReady([notionRightSidebarSelector]);
|
||||||
web.loadStylesheet('api/components/panel.css');
|
web.loadStylesheet('api/components/panel.css');
|
||||||
|
|
||||||
const $title = web.html`<div id="enhancer--panel-header-title"></div>`,
|
|
||||||
$header = web.render(web.html`<div id="enhancer--panel-header"></div>`, $title),
|
|
||||||
$content = web.html`<div id="enhancer--panel-content"></div>`;
|
|
||||||
|
|
||||||
// opening/closing
|
// opening/closing
|
||||||
const $notionFrame = document.querySelector('.notion-frame'),
|
const $notionFrame = document.querySelector('.notion-frame'),
|
||||||
$notionRightSidebar = document.querySelector(notionRightSidebarSelector),
|
$notionRightSidebar = document.querySelector(notionRightSidebarSelector),
|
||||||
$pinnedToggle = web.html`<div id="enhancer--panel-header-toggle">
|
$pinnedToggle = web.html`<div id="enhancer--panel-header-toggle"><div>
|
||||||
${await components.feather('chevrons-right')}
|
${await components.feather('chevrons-right')}
|
||||||
</div>`,
|
</div></div>`,
|
||||||
$hoverTrigger = web.html`<div id="enhancer--panel-hover-trigger"></div>`,
|
$hoverTrigger = web.html`<div id="enhancer--panel-hover-trigger"></div>`,
|
||||||
panelPinnedAttr = 'data-enhancer-panel-pinned',
|
panelPinnedAttr = 'data-enhancer-panel-pinned',
|
||||||
isPinned = () => $panel.hasAttribute(panelPinnedAttr),
|
isPinned = () => $panel.hasAttribute(panelPinnedAttr),
|
||||||
isRightSidebarOpen = () =>
|
|
||||||
$notionRightSidebar.matches('[style*="border-left: 1px solid rgba(0, 0, 0, 0)"]'),
|
|
||||||
togglePanel = () => {
|
togglePanel = () => {
|
||||||
const $elems = [$notionRightSidebar, $hoverTrigger, $panel];
|
const $elems = [$notionRightSidebar, $notionFrame, $hoverTrigger, $panel];
|
||||||
if (isPinned()) {
|
if (isPinned()) {
|
||||||
if (isRightSidebarOpen()) $elems.push($notionFrame);
|
closeSwitcher();
|
||||||
for (const $elem of $elems) $elem.removeAttribute(panelPinnedAttr);
|
for (const $elem of $elems) $elem.removeAttribute(panelPinnedAttr);
|
||||||
} else {
|
} else {
|
||||||
$elems.push($notionFrame);
|
|
||||||
for (const $elem of $elems) $elem.setAttribute(panelPinnedAttr, 'true');
|
for (const $elem of $elems) $elem.setAttribute(panelPinnedAttr, 'true');
|
||||||
}
|
}
|
||||||
db.set(['panel.pinned'], isPinned());
|
db.set(['panel.pinned'], isPinned());
|
||||||
};
|
};
|
||||||
web.addDocumentObserver(() => {
|
|
||||||
if (isPinned()) {
|
|
||||||
if (isRightSidebarOpen()) {
|
|
||||||
$notionFrame.removeAttribute(panelPinnedAttr);
|
|
||||||
} else {
|
|
||||||
$notionFrame.setAttribute(panelPinnedAttr, 'true');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, [notionRightSidebarSelector]);
|
|
||||||
if (await db.get(['panel.pinned'])) togglePanel();
|
if (await db.get(['panel.pinned'])) togglePanel();
|
||||||
web.addHotkeyListener(await db.get(['panel.hotkey']), togglePanel);
|
web.addHotkeyListener(await db.get(['panel.hotkey']), togglePanel);
|
||||||
$pinnedToggle.addEventListener('click', togglePanel);
|
$pinnedToggle.addEventListener('click', (event) => {
|
||||||
|
event.stopPropagation();
|
||||||
|
togglePanel();
|
||||||
|
});
|
||||||
|
|
||||||
// resizing
|
// resizing
|
||||||
let dragStartX,
|
let dragStartX,
|
||||||
@ -120,19 +116,90 @@ export const panel = async (icon, title, generator = () => {}) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// view selection
|
// view selection
|
||||||
const $switcherTrigger = web.html`<div id="enhancer--panel-header-switcher">
|
const $title = web.html`<div id="enhancer--panel-header-title"></div>`,
|
||||||
${await components.feather('chevron-up')}
|
$header = web.render(web.html`<div id="enhancer--panel-header"></div>`, $title),
|
||||||
${await components.feather('chevron-down')}
|
$content = web.html`<div id="enhancer--panel-content"></div>`,
|
||||||
|
$switcherTrigger = web.html`<div id="enhancer--panel-header-switcher">
|
||||||
|
${svgExpand}
|
||||||
</div>`,
|
</div>`,
|
||||||
|
$notionApp = document.querySelector('.notion-app-inner'),
|
||||||
|
$switcherOverlayContainer = web.html`<div id="enhancer--panel-switcher-overlay-container"></div>`,
|
||||||
|
$switcher = web.html`<div id="enhancer--panel-switcher"></div>`,
|
||||||
|
isSwitcherOpen = () => document.body.contains($switcher),
|
||||||
renderView = (view) => {
|
renderView = (view) => {
|
||||||
web.render(web.empty($title), web.render(web.html`<p></p>`, view.icon, view.title));
|
web.render(
|
||||||
|
web.empty($title),
|
||||||
|
web.render(
|
||||||
|
web.html`<p class="enhancer--panel-view-title"></p>`,
|
||||||
|
view.icon,
|
||||||
|
view.title
|
||||||
|
)
|
||||||
|
);
|
||||||
web.render(web.empty($content), view.$elem);
|
web.render(web.empty($content), view.$elem);
|
||||||
|
},
|
||||||
|
openSwitcher = () => {
|
||||||
|
if (!isPinned()) return togglePanel();
|
||||||
|
web.render($notionApp, $switcherOverlayContainer);
|
||||||
|
web.empty($switcher);
|
||||||
|
for (const view of _views) {
|
||||||
|
const $item = web.render(
|
||||||
|
web.html`<div class="enhancer--panel-switcher-item" tabindex="0"></div>`,
|
||||||
|
web.render(
|
||||||
|
web.html`<p class="enhancer--panel-view-title"></p>`,
|
||||||
|
view.icon.cloneNode(true),
|
||||||
|
view.title.cloneNode(true)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
$item.addEventListener('click', () => renderView(view));
|
||||||
|
web.render($switcher, $item);
|
||||||
|
}
|
||||||
|
const rect = $header.getBoundingClientRect();
|
||||||
|
web.render(
|
||||||
|
web.empty($switcherOverlayContainer),
|
||||||
|
web.render(
|
||||||
|
web.html`<div style="position: fixed; top: ${rect.top}px; left: ${rect.left}px;
|
||||||
|
width: ${rect.width}px; height: ${rect.height}px;"></div>`,
|
||||||
|
web.render(
|
||||||
|
web.html`<div style="position: relative; top: 100%; pointer-events: auto;"></div>`,
|
||||||
|
$switcher
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
$switcher.firstElementChild.focus();
|
||||||
|
$switcher.animate([{ opacity: 0 }, { opacity: 1 }], { duration: 200 });
|
||||||
};
|
};
|
||||||
|
function closeSwitcher() {
|
||||||
|
$switcher.animate([{ opacity: 1 }, { opacity: 0 }], { duration: 200 }).onfinish = () =>
|
||||||
|
$switcherOverlayContainer.remove();
|
||||||
|
}
|
||||||
|
web.addHotkeyListener(['Escape'], () => {
|
||||||
|
if (isSwitcherOpen()) closeSwitcher();
|
||||||
|
});
|
||||||
|
web.addHotkeyListener(['Enter'], () => {
|
||||||
|
if (isSwitcherOpen()) document.activeElement.click();
|
||||||
|
});
|
||||||
|
document.addEventListener('keydown', (event) => {
|
||||||
|
if (isSwitcherOpen()) {
|
||||||
|
if (event.key === 'ArrowUp') {
|
||||||
|
const $prev = event.target.previousElementSibling;
|
||||||
|
($prev || event.target.parentElement.lastElementChild).focus();
|
||||||
|
event.stopPropagation();
|
||||||
|
}
|
||||||
|
if (event.key === 'ArrowDown') {
|
||||||
|
const $next = event.target.nextElementSibling;
|
||||||
|
($next || event.target.parentElement.firstElementChild).focus();
|
||||||
|
event.stopPropagation();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
$header.addEventListener('click', openSwitcher);
|
||||||
|
$switcherTrigger.addEventListener('click', openSwitcher);
|
||||||
|
$switcherOverlayContainer.addEventListener('click', closeSwitcher);
|
||||||
renderView(_views[0]);
|
renderView(_views[0]);
|
||||||
|
|
||||||
web.render(
|
web.render(
|
||||||
$panel,
|
$panel,
|
||||||
web.render($header, $switcherTrigger, $title, $pinnedToggle),
|
web.render($header, $title, $switcherTrigger, $pinnedToggle),
|
||||||
$content,
|
$content,
|
||||||
$resizeHandle
|
$resizeHandle
|
||||||
);
|
);
|
||||||
|
@ -11,7 +11,12 @@
|
|||||||
* @module notion-enhancer/api/web
|
* @module notion-enhancer/api/web
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { fs, fmt } from './_.mjs';
|
import { fs } from './_.mjs';
|
||||||
|
|
||||||
|
let _hotkeyEventListeners = [],
|
||||||
|
_documentObserver,
|
||||||
|
_documentObserverListeners = [],
|
||||||
|
_documentObserverEvents = [];
|
||||||
|
|
||||||
import '../dep/jscolor.min.js';
|
import '../dep/jscolor.min.js';
|
||||||
/** color picker with alpha channel using https://jscolor.com/ */
|
/** color picker with alpha channel using https://jscolor.com/ */
|
||||||
@ -138,27 +143,26 @@ export const loadStylesheet = (path) => {
|
|||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
const _hotkeyEvent = document.addEventListener('keyup', (event) => {
|
document.addEventListener('keyup', (event) => {
|
||||||
if (document.activeElement.nodeName === 'INPUT') return;
|
if (document.activeElement.nodeName === 'INPUT') return;
|
||||||
for (const hotkey of _hotkeyEventListeners) {
|
for (const hotkey of _hotkeyEventListeners) {
|
||||||
const pressed = hotkey.keys.every((key) => {
|
const pressed = hotkey.keys.every((key) => {
|
||||||
key = key.toLowerCase();
|
key = key.toLowerCase();
|
||||||
const modifiers = {
|
const modifiers = {
|
||||||
metaKey: ['meta', 'os', 'win', 'cmd', 'command'],
|
metaKey: ['meta', 'os', 'win', 'cmd', 'command'],
|
||||||
ctrlKey: ['ctrl', 'control'],
|
ctrlKey: ['ctrl', 'control'],
|
||||||
shiftKey: ['shift'],
|
shiftKey: ['shift'],
|
||||||
altKey: ['alt'],
|
altKey: ['alt'],
|
||||||
};
|
};
|
||||||
for (const modifier in modifiers) {
|
for (const modifier in modifiers) {
|
||||||
const pressed = modifiers[modifier].includes(key) && event[modifier];
|
const pressed = modifiers[modifier].includes(key) && event[modifier];
|
||||||
if (pressed) return true;
|
if (pressed) return true;
|
||||||
}
|
}
|
||||||
if (key === event.key.toLowerCase()) return true;
|
if (key === event.key.toLowerCase()) return true;
|
||||||
});
|
});
|
||||||
if (pressed) hotkey.callback();
|
if (pressed) hotkey.callback(event);
|
||||||
}
|
}
|
||||||
}),
|
});
|
||||||
_hotkeyEventListeners = [];
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* register a hotkey listener to the page
|
* register a hotkey listener to the page
|
||||||
@ -181,39 +185,40 @@ export const removeHotkeyListener = (callback) => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const _documentObserver = new MutationObserver((list, observer) => {
|
|
||||||
if (!_documentObserverEvents.length)
|
|
||||||
requestIdleCallback(() => (queue) => {
|
|
||||||
while (queue.length) {
|
|
||||||
const event = queue.shift();
|
|
||||||
for (const listener of _documentObserverListeners) {
|
|
||||||
if (
|
|
||||||
!listener.selectors.length ||
|
|
||||||
listener.selectors.some(
|
|
||||||
(selector) =>
|
|
||||||
event.target.matches(selector) || event.target.matches(`${selector} *`)
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
listener.callback(event);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
_documentObserverEvents.push(...list);
|
|
||||||
}),
|
|
||||||
_documentObserverListeners = [],
|
|
||||||
_documentObserverEvents = [];
|
|
||||||
_documentObserver.observe(document.body, {
|
|
||||||
childList: true,
|
|
||||||
subtree: true,
|
|
||||||
attributes: true,
|
|
||||||
});
|
|
||||||
/**
|
/**
|
||||||
* add a listener to watch for changes to the dom
|
* add a listener to watch for changes to the dom
|
||||||
* @param {onDocumentObservedCallback} callback
|
* @param {onDocumentObservedCallback} callback
|
||||||
* @param {array<string>} [selectors]
|
* @param {array<string>} [selectors]
|
||||||
*/
|
*/
|
||||||
export const addDocumentObserver = (callback, selectors = []) => {
|
export const addDocumentObserver = (callback, selectors = []) => {
|
||||||
|
if (!_documentObserver) {
|
||||||
|
const handle = (queue) => {
|
||||||
|
while (queue.length) {
|
||||||
|
const event = queue.shift();
|
||||||
|
for (const listener of _documentObserverListeners) {
|
||||||
|
if (
|
||||||
|
!listener.selectors.length ||
|
||||||
|
listener.selectors.some(
|
||||||
|
(selector) =>
|
||||||
|
event.target.matches(selector) || event.target.matches(`${selector} *`)
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
listener.callback(event);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
_documentObserver = new MutationObserver((list, observer) => {
|
||||||
|
if (!_documentObserverEvents.length)
|
||||||
|
requestIdleCallback(() => handle(_documentObserverEvents));
|
||||||
|
_documentObserverEvents.push(...list);
|
||||||
|
});
|
||||||
|
_documentObserver.observe(document.body, {
|
||||||
|
childList: true,
|
||||||
|
subtree: true,
|
||||||
|
attributes: true,
|
||||||
|
});
|
||||||
|
}
|
||||||
_documentObserverListeners.push({ callback, selectors });
|
_documentObserverListeners.push({ callback, selectors });
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -34,4 +34,7 @@ export default async function (api, db) {
|
|||||||
components.panel(await components.feather('sidebar'), 'Test Panel', ($panel) => {
|
components.panel(await components.feather('sidebar'), 'Test Panel', ($panel) => {
|
||||||
return web.html`<p>test</p>`;
|
return web.html`<p>test</p>`;
|
||||||
});
|
});
|
||||||
|
components.panel(await components.feather('users'), 'Other Panel', ($panel) => {
|
||||||
|
return web.html`<p>yay</p>`;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user