diff --git a/api/components/_.mjs b/api/components/_.mjs index cd0cc0d..dcc4a9e 100644 --- a/api/components/_.mjs +++ b/api/components/_.mjs @@ -29,10 +29,12 @@ export { feather } from './feather.mjs'; /** * adds a view to the enhancer's side panel * @param {object} panel - information used to construct and render the panel - * @param {string} [panel.id] - a uuid, used to restore the last open view on reload + * @param {string} panel.id - a uuid, used to restore the last open view on reload * @param {string} panel.icon - an svg string * @param {string} panel.title - the name of the view * @param {Element} panel.$content - an element containing the content of the view + * @param {function} panel.onBlur - runs when the view is selected/focused + * @param {function} panel.onFocus - runs when the view is unfocused/closed */ export { addPanelView } from './panel.mjs'; diff --git a/api/components/panel.mjs b/api/components/panel.mjs index 44a6735..e3f27eb 100644 --- a/api/components/panel.mjs +++ b/api/components/panel.mjs @@ -170,6 +170,7 @@ const $panel = web.html`
`, } }, renderView = (view) => { + const prevView = _views.find(({ $content }) => document.contains($content)); web.render( web.empty($panelTitle), web.render( @@ -178,10 +179,13 @@ const $panel = web.html``, view.$title ) ); + view.onFocus(); web.render(web.empty($panelContent), view.$content); + if (prevView) prevView.onBlur(); }; async function createPanel() { + await web.whenReady(['.notion-frame']); $notionFrame = document.querySelector('.notion-frame'); const notionRightSidebarSelector = '.notion-cursor-listener > div[style*="flex-end"]', @@ -239,22 +243,35 @@ async function createViews() { /** * adds a view to the enhancer's side panel * @param {object} panel - information used to construct and render the panel - * @param {string} [panel.id] - a uuid, used to restore the last open view on reload + * @param {string} panel.id - a uuid, used to restore the last open view on reload * @param {string} panel.icon - an svg string * @param {string} panel.title - the name of the view * @param {Element} panel.$content - an element containing the content of the view + * @param {function} panel.onBlur - runs when the view is selected/focused + * @param {function} panel.onFocus - runs when the view is unfocused/closed */ -export const addPanelView = async ({ id = fmt.uuidv4(), icon, title, $content }) => { +export const addPanelView = async ({ + id, + icon, + title, + $content, + onFocus = () => {}, + onBlur = () => {}, +}) => { const view = { id, - $icon: web.html` `, - $title: web.html` - ${web.escape(title)} - - `, + $icon: web.render( + web.html``, + icon instanceof Element ? icon : web.html`${icon}` + ), + $title: web.render( + web.html``, + title, + web.html`` + ), $content, + onFocus, + onBlur, }; _views.push(view); if (_views.length === 1) await createPanel(); diff --git a/api/web.mjs b/api/web.mjs index dfa80e7..0de9929 100644 --- a/api/web.mjs +++ b/api/web.mjs @@ -194,17 +194,18 @@ export const addDocumentObserver = (callback, selectors = []) => { if (!_documentObserver) { const handle = (queue) => { while (queue.length) { - const event = queue.shift(); + const event = queue.shift(), + matchesAddedNode = ($node, selector) => + $node instanceof Element && + ($node.matches(selector) || + $node.matches(`${selector} *`) || + $node.querySelector(selector)), + matchesTarget = (selector) => + event.target.matches(selector) || + event.target.matches(`${selector} *`) || + [...event.addedNodes].some(($node) => matchesAddedNode($node, selector)); for (const listener of _documentObserverListeners) { - if ( - !listener.selectors.length || - listener.selectors.some( - (selector) => - event.target.matches(selector) || - event.target.matches(`${selector} *`) || - event.target.querySelector(selector) - ) - ) { + if (!listener.selectors.length || listener.selectors.some(matchesTarget)) { listener.callback(event); } }