mirror of
				https://github.com/notion-enhancer/notion-enhancer.git
				synced 2025-11-04 16:18:08 +11:00 
			
		
		
		
	side panel component: working except switcher
This commit is contained in:
		
							parent
							
								
									33e8907d4f
								
							
						
					
					
						commit
						bc38f5d972
					
				@ -21,5 +21,5 @@ export * as fmt from './fmt.mjs';
 | 
			
		||||
export * as registry from './registry.mjs';
 | 
			
		||||
/** helpers for manipulation of a webpage */
 | 
			
		||||
export * as web from './web.mjs';
 | 
			
		||||
/** notion-style elements inc. the sidebar */
 | 
			
		||||
/** shared notion-style elements */
 | 
			
		||||
export * as components from './components/_.mjs';
 | 
			
		||||
 | 
			
		||||
@ -7,7 +7,7 @@
 | 
			
		||||
'use strict';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * notion-style elements inc. the sidebar
 | 
			
		||||
 * shared notion-style elements
 | 
			
		||||
 * @module notion-enhancer/api/components
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
@ -18,4 +18,12 @@
 | 
			
		||||
 */
 | 
			
		||||
export { tooltip } from './tooltip.mjs';
 | 
			
		||||
 | 
			
		||||
export { side } from './tooltip.mjs';
 | 
			
		||||
/**
 | 
			
		||||
 * generate an icon from the feather icons set
 | 
			
		||||
 * @param {string} name - the name/id of the icon
 | 
			
		||||
 * @param {object} attrs - an object of attributes to apply to the icon e.g. classes
 | 
			
		||||
 * @returns {string} an svg string
 | 
			
		||||
 */
 | 
			
		||||
export { feather } from './feather.mjs';
 | 
			
		||||
 | 
			
		||||
export { panel } from './panel.mjs';
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										36
									
								
								extension/api/components/feather.mjs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										36
									
								
								extension/api/components/feather.mjs
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,36 @@
 | 
			
		||||
/*
 | 
			
		||||
 * notion-enhancer: api
 | 
			
		||||
 * (c) 2021 dragonwocky <thedragonring.bod@gmail.com> (https://dragonwocky.me/)
 | 
			
		||||
 * (https://notion-enhancer.github.io/) under the MIT license
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
'use strict';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * shared notion-style elements
 | 
			
		||||
 * @module notion-enhancer/api/components/feather
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
import { fs, web } from '../_.mjs';
 | 
			
		||||
 | 
			
		||||
let _$iconSheet;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * generate an icon from the feather icons set
 | 
			
		||||
 * @param {string} name - the name/id of the icon
 | 
			
		||||
 * @param {object} attrs - an object of attributes to apply to the icon e.g. classes
 | 
			
		||||
 * @returns {string} an svg string
 | 
			
		||||
 */
 | 
			
		||||
export const feather = async (name, attrs = {}) => {
 | 
			
		||||
  if (!_$iconSheet) {
 | 
			
		||||
    _$iconSheet = web.html`${await fs.getText('dep/feather-sprite.svg')}`;
 | 
			
		||||
  }
 | 
			
		||||
  attrs.style = (
 | 
			
		||||
    (attrs.style ? attrs.style + ';' : '') +
 | 
			
		||||
    'stroke:currentColor;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;fill:none;'
 | 
			
		||||
  ).trim();
 | 
			
		||||
  attrs.viewBox = '0 0 24 24';
 | 
			
		||||
  return `<svg ${Object.entries(attrs)
 | 
			
		||||
    .map(([key, val]) => `${web.escape(key)}="${web.escape(val)}"`)
 | 
			
		||||
    .join(' ')}>${_$iconSheet.getElementById(name)?.innerHTML}</svg>`;
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										151
									
								
								extension/api/components/panel.css
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										151
									
								
								extension/api/components/panel.css
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,151 @@
 | 
			
		||||
/*
 | 
			
		||||
 * notion-enhancer core: components
 | 
			
		||||
 * (c) 2021 dragonwocky <thedragonring.bod@gmail.com> (https://dragonwocky.me/)
 | 
			
		||||
 * (c) 2021 CloudHill <rl.cloudhill@gmail.com> (https://github.com/CloudHill)
 | 
			
		||||
 * (https://notion-enhancer.github.io/) under the MIT license
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
:root {
 | 
			
		||||
  --component--panel-width: 260px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#enhancer--panel-hover-trigger {
 | 
			
		||||
  height: 100vh;
 | 
			
		||||
  width: 2.5rem;
 | 
			
		||||
  max-height: 100%;
 | 
			
		||||
  z-index: 999;
 | 
			
		||||
  position: absolute;
 | 
			
		||||
  top: 0;
 | 
			
		||||
  right: 0;
 | 
			
		||||
  flex-grow: 0;
 | 
			
		||||
  flex-shrink: 0;
 | 
			
		||||
  transition: width 300ms ease-in-out;
 | 
			
		||||
}
 | 
			
		||||
#enhancer--panel-hover-trigger[data-enhancer-panel-pinned] {
 | 
			
		||||
  /* taking up the physical space of the panel to move topbar buttons */
 | 
			
		||||
  position: relative;
 | 
			
		||||
  width: var(--component--panel-width);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.notion-cursor-listener > div[style*='flex-end'] {
 | 
			
		||||
  transition: margin-right 300ms ease-in-out;
 | 
			
		||||
}
 | 
			
		||||
.notion-cursor-listener > div[style*='flex-end'][data-enhancer-panel-pinned] {
 | 
			
		||||
  margin-right: var(--component--panel-width);
 | 
			
		||||
}
 | 
			
		||||
.notion-frame {
 | 
			
		||||
  transition: padding-right 300ms ease-in-out;
 | 
			
		||||
}
 | 
			
		||||
.notion-frame[data-enhancer-panel-pinned] {
 | 
			
		||||
  padding-right: var(--component--panel-width);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#enhancer--panel {
 | 
			
		||||
  z-index: 999;
 | 
			
		||||
  position: absolute;
 | 
			
		||||
  background: var(--theme--bg_secondary);
 | 
			
		||||
  width: var(--component--panel-width);
 | 
			
		||||
  right: calc(-1 * var(--component--panel-width));
 | 
			
		||||
  opacity: 0;
 | 
			
		||||
  height: 100vh;
 | 
			
		||||
  flex-grow: 0;
 | 
			
		||||
  flex-shrink: 0;
 | 
			
		||||
  display: flex;
 | 
			
		||||
  flex-direction: column;
 | 
			
		||||
  transition: 300ms ease-in;
 | 
			
		||||
 | 
			
		||||
  margin-top: 5rem;
 | 
			
		||||
  max-height: calc(100vh - 10rem);
 | 
			
		||||
}
 | 
			
		||||
#enhancer--panel-hover-trigger:hover + #enhancer--panel:not([data-enhancer-panel-pinned]),
 | 
			
		||||
#enhancer--panel:not([data-enhancer-panel-pinned]):hover {
 | 
			
		||||
  opacity: 1;
 | 
			
		||||
  transform: translateX(calc(-1 * var(--component--panel-width)));
 | 
			
		||||
  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;
 | 
			
		||||
}
 | 
			
		||||
#enhancer--panel[data-enhancer-panel-pinned] {
 | 
			
		||||
  opacity: 1;
 | 
			
		||||
  max-height: 100%;
 | 
			
		||||
  margin-top: 0;
 | 
			
		||||
  transform: translateX(calc(-1 * var(--component--panel-width)));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#enhancer--panel-header {
 | 
			
		||||
  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;
 | 
			
		||||
  height: 1em;
 | 
			
		||||
  display: flex;
 | 
			
		||||
  align-items: center;
 | 
			
		||||
}
 | 
			
		||||
#enhancer--panel-header-title > p svg,
 | 
			
		||||
#enhancer--panel-header-title > p img {
 | 
			
		||||
  height: 1em;
 | 
			
		||||
  width: 1em;
 | 
			
		||||
}
 | 
			
		||||
#enhancer--panel-header-title > p span {
 | 
			
		||||
  font-size: 0.9em;
 | 
			
		||||
  margin-left: 0.5em;
 | 
			
		||||
}
 | 
			
		||||
#enhancer--panel-header-toggle {
 | 
			
		||||
  margin-left: auto;
 | 
			
		||||
}
 | 
			
		||||
#enhancer--panel-header-toggle,
 | 
			
		||||
#enhancer--panel-header-switcher {
 | 
			
		||||
  height: 1em;
 | 
			
		||||
  width: 1em;
 | 
			
		||||
  cursor: pointer;
 | 
			
		||||
  opacity: 0;
 | 
			
		||||
  transition: 300ms ease-in-out;
 | 
			
		||||
  display: flex;
 | 
			
		||||
  flex-direction: column;
 | 
			
		||||
}
 | 
			
		||||
#enhancer--panel-header-switcher svg {
 | 
			
		||||
  width: 0.5em;
 | 
			
		||||
  height: 0.5em;
 | 
			
		||||
  display: block;
 | 
			
		||||
  margin: auto;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#enhancer--panel:not([data-enhancer-panel-pinned]) #enhancer--panel-header-toggle {
 | 
			
		||||
  transform: rotateZ(-180deg);
 | 
			
		||||
}
 | 
			
		||||
#enhancer--panel:hover #enhancer--panel-header-toggle,
 | 
			
		||||
#enhancer--panel:hover #enhancer--panel-header-switcher {
 | 
			
		||||
  opacity: 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#enhancer--panel-resize {
 | 
			
		||||
  position: absolute;
 | 
			
		||||
  left: -5px;
 | 
			
		||||
  height: 100%;
 | 
			
		||||
  width: 10px;
 | 
			
		||||
}
 | 
			
		||||
#enhancer--panel[data-enhancer-panel-pinned] #enhancer--panel-resize {
 | 
			
		||||
  cursor: col-resize;
 | 
			
		||||
}
 | 
			
		||||
#enhancer--panel-resize div {
 | 
			
		||||
  transition: background 150ms ease-in-out;
 | 
			
		||||
  background: transparent;
 | 
			
		||||
  width: 2px;
 | 
			
		||||
  margin-left: 4px;
 | 
			
		||||
  height: 100%;
 | 
			
		||||
}
 | 
			
		||||
#enhancer--panel[data-enhancer-panel-pinned] #enhancer--panel-resize:hover div {
 | 
			
		||||
  background: var(--theme--ui_divider);
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										141
									
								
								extension/api/components/panel.mjs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										141
									
								
								extension/api/components/panel.mjs
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,141 @@
 | 
			
		||||
/*
 | 
			
		||||
 * notion-enhancer: api
 | 
			
		||||
 * (c) 2021 dragonwocky <thedragonring.bod@gmail.com> (https://dragonwocky.me/)
 | 
			
		||||
 * (https://notion-enhancer.github.io/) under the MIT license
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
'use strict';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * shared notion-style elements
 | 
			
		||||
 * @module notion-enhancer/api/components/side-panel
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
import { web, components, registry } from '../_.mjs';
 | 
			
		||||
const db = await registry.db('36a2ffc9-27ff-480e-84a7-c7700a7d232d');
 | 
			
		||||
 | 
			
		||||
let $panel,
 | 
			
		||||
  _views = [];
 | 
			
		||||
 | 
			
		||||
export const panel = async (icon, title, generator = () => {}) => {
 | 
			
		||||
  _views.push({
 | 
			
		||||
    icon: web.html`${icon}`,
 | 
			
		||||
    title: web.html`<span>${web.escape(title)}</span>`,
 | 
			
		||||
    $elem: generator(),
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  if (!$panel) {
 | 
			
		||||
    $panel = web.html`<div id="enhancer--panel"></div>`;
 | 
			
		||||
 | 
			
		||||
    const notionRightSidebarSelector = '.notion-cursor-listener > div[style*="flex-end"]';
 | 
			
		||||
    await web.whenReady([notionRightSidebarSelector]);
 | 
			
		||||
    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
 | 
			
		||||
    const $notionFrame = document.querySelector('.notion-frame'),
 | 
			
		||||
      $notionRightSidebar = document.querySelector(notionRightSidebarSelector),
 | 
			
		||||
      $pinnedToggle = web.html`<div id="enhancer--panel-header-toggle">
 | 
			
		||||
        ${await components.feather('chevrons-right')}
 | 
			
		||||
      </div>`,
 | 
			
		||||
      $hoverTrigger = web.html`<div id="enhancer--panel-hover-trigger"></div>`,
 | 
			
		||||
      panelPinnedAttr = 'data-enhancer-panel-pinned',
 | 
			
		||||
      isPinned = () => $panel.hasAttribute(panelPinnedAttr),
 | 
			
		||||
      isRightSidebarOpen = () =>
 | 
			
		||||
        $notionRightSidebar.matches('[style*="border-left: 1px solid rgba(0, 0, 0, 0)"]'),
 | 
			
		||||
      togglePanel = () => {
 | 
			
		||||
        const $elems = [$notionRightSidebar, $hoverTrigger, $panel];
 | 
			
		||||
        if (isPinned()) {
 | 
			
		||||
          if (isRightSidebarOpen()) $elems.push($notionFrame);
 | 
			
		||||
          for (const $elem of $elems) $elem.removeAttribute(panelPinnedAttr);
 | 
			
		||||
        } else {
 | 
			
		||||
          $elems.push($notionFrame);
 | 
			
		||||
          for (const $elem of $elems) $elem.setAttribute(panelPinnedAttr, 'true');
 | 
			
		||||
        }
 | 
			
		||||
        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();
 | 
			
		||||
    web.addHotkeyListener(await db.get(['panel.hotkey']), togglePanel);
 | 
			
		||||
    $pinnedToggle.addEventListener('click', togglePanel);
 | 
			
		||||
 | 
			
		||||
    // resizing
 | 
			
		||||
    let dragStartX,
 | 
			
		||||
      dragStartWidth,
 | 
			
		||||
      dragEventsFired,
 | 
			
		||||
      panelWidth = await db.get(['panel.width'], 240);
 | 
			
		||||
    const $resizeHandle = web.html`<div id="enhancer--panel-resize"><div></div></div>`,
 | 
			
		||||
      updateWidth = async () => {
 | 
			
		||||
        document.documentElement.style.setProperty(
 | 
			
		||||
          '--component--panel-width',
 | 
			
		||||
          panelWidth + 'px'
 | 
			
		||||
        );
 | 
			
		||||
        db.set(['panel.width'], panelWidth);
 | 
			
		||||
      },
 | 
			
		||||
      resizeDrag = (event) => {
 | 
			
		||||
        event.preventDefault();
 | 
			
		||||
        dragEventsFired = true;
 | 
			
		||||
        panelWidth = dragStartWidth + (dragStartX - event.clientX);
 | 
			
		||||
        if (panelWidth < 190) panelWidth = 190;
 | 
			
		||||
        if (panelWidth > 480) panelWidth = 480;
 | 
			
		||||
        $panel.style.width = panelWidth + 'px';
 | 
			
		||||
        $hoverTrigger.style.width = panelWidth + 'px';
 | 
			
		||||
        $notionFrame.style.paddingRight = panelWidth + 'px';
 | 
			
		||||
        $notionRightSidebar.style.right = panelWidth + 'px';
 | 
			
		||||
      },
 | 
			
		||||
      resizeEnd = (event) => {
 | 
			
		||||
        $panel.style.width = '';
 | 
			
		||||
        $hoverTrigger.style.width = '';
 | 
			
		||||
        $notionFrame.style.paddingRight = '';
 | 
			
		||||
        $notionRightSidebar.style.right = '';
 | 
			
		||||
        updateWidth();
 | 
			
		||||
        $resizeHandle.style.cursor = '';
 | 
			
		||||
        document.body.removeEventListener('mousemove', resizeDrag);
 | 
			
		||||
        document.body.removeEventListener('mouseup', resizeEnd);
 | 
			
		||||
      },
 | 
			
		||||
      resizeStart = (event) => {
 | 
			
		||||
        dragStartX = event.clientX;
 | 
			
		||||
        dragStartWidth = panelWidth;
 | 
			
		||||
        $resizeHandle.style.cursor = 'auto';
 | 
			
		||||
        document.body.addEventListener('mousemove', resizeDrag);
 | 
			
		||||
        document.body.addEventListener('mouseup', resizeEnd);
 | 
			
		||||
      };
 | 
			
		||||
    updateWidth();
 | 
			
		||||
    $resizeHandle.addEventListener('mousedown', resizeStart);
 | 
			
		||||
    $resizeHandle.addEventListener('click', () => {
 | 
			
		||||
      if (dragEventsFired) {
 | 
			
		||||
        dragEventsFired = false;
 | 
			
		||||
      } else togglePanel();
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    // view selection
 | 
			
		||||
    const $switcherTrigger = web.html`<div id="enhancer--panel-header-switcher">
 | 
			
		||||
        ${await components.feather('chevron-up')}
 | 
			
		||||
        ${await components.feather('chevron-down')}
 | 
			
		||||
      </div>`,
 | 
			
		||||
      renderView = (view) => {
 | 
			
		||||
        web.render(web.empty($title), web.render(web.html`<p></p>`, view.icon, view.title));
 | 
			
		||||
        web.render(web.empty($content), view.$elem);
 | 
			
		||||
      };
 | 
			
		||||
    renderView(_views[0]);
 | 
			
		||||
 | 
			
		||||
    web.render(
 | 
			
		||||
      $panel,
 | 
			
		||||
      web.render($header, $switcherTrigger, $title, $pinnedToggle),
 | 
			
		||||
      $content,
 | 
			
		||||
      $resizeHandle
 | 
			
		||||
    );
 | 
			
		||||
    $notionRightSidebar.after($hoverTrigger, $panel);
 | 
			
		||||
  }
 | 
			
		||||
};
 | 
			
		||||
@ -1,6 +0,0 @@
 | 
			
		||||
/*
 | 
			
		||||
 * notion-enhancer core: components
 | 
			
		||||
 * (c) 2021 dragonwocky <thedragonring.bod@gmail.com> (https://dragonwocky.me/)
 | 
			
		||||
 * (c) 2021 CloudHill <rl.cloudhill@gmail.com> (https://github.com/CloudHill)
 | 
			
		||||
 * (https://notion-enhancer.github.io/) under the MIT license
 | 
			
		||||
 */
 | 
			
		||||
@ -1,24 +0,0 @@
 | 
			
		||||
/*
 | 
			
		||||
 * notion-enhancer: api
 | 
			
		||||
 * (c) 2021 dragonwocky <thedragonring.bod@gmail.com> (https://dragonwocky.me/)
 | 
			
		||||
 * (https://notion-enhancer.github.io/) under the MIT license
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
'use strict';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * notion-style elements inc. the sidebar
 | 
			
		||||
 * @module notion-enhancer/api/components/side-panel
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
import { web } from '../_.mjs';
 | 
			
		||||
 | 
			
		||||
let _$sidebar;
 | 
			
		||||
 | 
			
		||||
export const sidebar = (icon, name, loader = ($panel) => {}) => {
 | 
			
		||||
  if (!_$sidebar) {
 | 
			
		||||
    web.loadStylesheet('api/components/sidebar.css');
 | 
			
		||||
    _$sidebar = web.html`<div id="enhancer--sidebar"></div>`;
 | 
			
		||||
    web.render(document.body, _$sidebar);
 | 
			
		||||
  }
 | 
			
		||||
};
 | 
			
		||||
@ -7,7 +7,7 @@
 | 
			
		||||
'use strict';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * notion-style elements inc. the sidebar
 | 
			
		||||
 * shared notion-style elements
 | 
			
		||||
 * @module notion-enhancer/api/components/tooltip
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
@ -22,8 +22,8 @@ let _$tooltip;
 | 
			
		||||
 */
 | 
			
		||||
export const tooltip = ($ref, text) => {
 | 
			
		||||
  if (!_$tooltip) {
 | 
			
		||||
    web.loadStylesheet('api/components/tooltip.css');
 | 
			
		||||
    _$tooltip = web.html`<div id="enhancer--tooltip"></div>`;
 | 
			
		||||
    web.loadStylesheet('api/components/tooltip.css');
 | 
			
		||||
    web.render(document.body, _$tooltip);
 | 
			
		||||
  }
 | 
			
		||||
  text = fmt.md.render(text);
 | 
			
		||||
 | 
			
		||||
@ -11,7 +11,7 @@
 | 
			
		||||
 * @module notion-enhancer/api/fmt
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
import { web, fs } from './_.mjs';
 | 
			
		||||
import { web, fs, components } from './_.mjs';
 | 
			
		||||
 | 
			
		||||
import '../dep/prism.min.js';
 | 
			
		||||
/** syntax highlighting using https://prismjs.com/ */
 | 
			
		||||
@ -21,7 +21,7 @@ Prism.hooks.add('complete', async (event) => {
 | 
			
		||||
  event.element.parentElement.removeAttribute('tabindex');
 | 
			
		||||
  event.element.parentElement.parentElement
 | 
			
		||||
    .querySelector('.copy-to-clipboard-button')
 | 
			
		||||
    .prepend(web.html`${await web.icon('clipboard')}`);
 | 
			
		||||
    .prepend(web.html`${await components.feather('clipboard')}`);
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
import '../dep/markdown-it.min.js';
 | 
			
		||||
 | 
			
		||||
@ -13,12 +13,6 @@
 | 
			
		||||
 | 
			
		||||
import { fs, fmt } from './_.mjs';
 | 
			
		||||
 | 
			
		||||
const _hotkeyEventListeners = [],
 | 
			
		||||
  _documentObserverListeners = [],
 | 
			
		||||
  _documentObserverEvents = [];
 | 
			
		||||
 | 
			
		||||
let _hotkeyEvent, _documentObserver;
 | 
			
		||||
 | 
			
		||||
import '../dep/jscolor.min.js';
 | 
			
		||||
/** color picker with alpha channel using https://jscolor.com/ */
 | 
			
		||||
export const jscolor = JSColor;
 | 
			
		||||
@ -144,21 +138,27 @@ export const loadStylesheet = (path) => {
 | 
			
		||||
  return true;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * generate an icon from the feather icons set
 | 
			
		||||
 * @param {string} name - the name/id of the icon
 | 
			
		||||
 * @param {object} attrs - an object of attributes to apply to the icon e.g. classes
 | 
			
		||||
 * @returns {string} an svg string
 | 
			
		||||
 */
 | 
			
		||||
export const icon = (name, attrs = {}) => {
 | 
			
		||||
  attrs.style = (
 | 
			
		||||
    (attrs.style || '') +
 | 
			
		||||
    ';stroke:currentColor;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;fill:none;'
 | 
			
		||||
  ).trim();
 | 
			
		||||
  return `<svg ${Object.entries(attrs)
 | 
			
		||||
    .map(([key, val]) => `${escape(key)}="${escape(val)}"`)
 | 
			
		||||
    .join(' ')}><use xlink:href="${fs.localPath('dep/feather-sprite.svg')}#${name}" /></svg>`;
 | 
			
		||||
};
 | 
			
		||||
const _hotkeyEvent = document.addEventListener('keyup', (event) => {
 | 
			
		||||
    if (document.activeElement.nodeName === 'INPUT') return;
 | 
			
		||||
    for (const hotkey of _hotkeyEventListeners) {
 | 
			
		||||
      const pressed = hotkey.keys.every((key) => {
 | 
			
		||||
        key = key.toLowerCase();
 | 
			
		||||
        const modifiers = {
 | 
			
		||||
          metaKey: ['meta', 'os', 'win', 'cmd', 'command'],
 | 
			
		||||
          ctrlKey: ['ctrl', 'control'],
 | 
			
		||||
          shiftKey: ['shift'],
 | 
			
		||||
          altKey: ['alt'],
 | 
			
		||||
        };
 | 
			
		||||
        for (const modifier in modifiers) {
 | 
			
		||||
          const pressed = modifiers[modifier].includes(key) && event[modifier];
 | 
			
		||||
          if (pressed) return true;
 | 
			
		||||
        }
 | 
			
		||||
        if (key === event.key.toLowerCase()) return true;
 | 
			
		||||
      });
 | 
			
		||||
      if (pressed) hotkey.callback();
 | 
			
		||||
    }
 | 
			
		||||
  }),
 | 
			
		||||
  _hotkeyEventListeners = [];
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * register a hotkey listener to the page
 | 
			
		||||
@ -169,28 +169,6 @@ export const icon = (name, attrs = {}) => {
 | 
			
		||||
 */
 | 
			
		||||
export const addHotkeyListener = (keys, callback) => {
 | 
			
		||||
  if (typeof keys === 'string') keys = keys.split('+');
 | 
			
		||||
  if (!_hotkeyEvent) {
 | 
			
		||||
    _hotkeyEvent = document.addEventListener('keyup', (event) => {
 | 
			
		||||
      if (document.activeElement.nodeName === 'INPUT') return;
 | 
			
		||||
      for (const hotkey of _hotkeyEventListeners) {
 | 
			
		||||
        const pressed = hotkey.keys.every((key) => {
 | 
			
		||||
          key = key.toLowerCase();
 | 
			
		||||
          const modifiers = {
 | 
			
		||||
            metaKey: ['meta', 'os', 'win', 'cmd', 'command'],
 | 
			
		||||
            ctrlKey: ['ctrl', 'control'],
 | 
			
		||||
            shiftKey: ['shift'],
 | 
			
		||||
            altKey: ['alt'],
 | 
			
		||||
          };
 | 
			
		||||
          for (const modifier in modifiers) {
 | 
			
		||||
            const pressed = modifiers[modifier].includes(key) && event[modifier];
 | 
			
		||||
            if (pressed) return true;
 | 
			
		||||
          }
 | 
			
		||||
          if (key === event.key.toLowerCase()) return true;
 | 
			
		||||
        });
 | 
			
		||||
        if (pressed) hotkey.callback();
 | 
			
		||||
      }
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
  _hotkeyEventListeners.push({ keys, callback });
 | 
			
		||||
};
 | 
			
		||||
/**
 | 
			
		||||
@ -203,40 +181,39 @@ 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
 | 
			
		||||
 * @param {onDocumentObservedCallback} callback
 | 
			
		||||
 * @param {array<string>} [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 });
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -7,7 +7,7 @@
 | 
			
		||||
'use strict';
 | 
			
		||||
 | 
			
		||||
export default async function (api, db) {
 | 
			
		||||
  const { web } = api;
 | 
			
		||||
  const { web, components } = api;
 | 
			
		||||
  await web.whenReady();
 | 
			
		||||
 | 
			
		||||
  let _lastPage = {};
 | 
			
		||||
@ -30,4 +30,8 @@ export default async function (api, db) {
 | 
			
		||||
      _lastPage = getCurrentPage();
 | 
			
		||||
    }
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  components.panel(await components.feather('sidebar'), 'Test Panel', ($panel) => {
 | 
			
		||||
    return web.html`<p>test</p>`;
 | 
			
		||||
  });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -3,7 +3,7 @@
 | 
			
		||||
  "name": "components",
 | 
			
		||||
  "id": "36a2ffc9-27ff-480e-84a7-c7700a7d232d",
 | 
			
		||||
  "version": "0.2.0",
 | 
			
		||||
  "description": "notion-style elements reused by other mods, inc. the sidebar.",
 | 
			
		||||
  "description": "shared notion-style elements.",
 | 
			
		||||
  "tags": ["core"],
 | 
			
		||||
  "authors": [
 | 
			
		||||
    {
 | 
			
		||||
@ -24,10 +24,10 @@
 | 
			
		||||
  "options": [
 | 
			
		||||
    {
 | 
			
		||||
      "type": "hotkey",
 | 
			
		||||
      "key": "side-panel-hotkey",
 | 
			
		||||
      "label": "toggle enhancer sidebar hotkey",
 | 
			
		||||
      "key": "panel.hotkey",
 | 
			
		||||
      "label": "toggle panel hotkey",
 | 
			
		||||
      "value": "Ctrl+Alt+\\",
 | 
			
		||||
      "tooltip": "opens/closes the extra sidebar in notion - will only work if a mod is making use of it."
 | 
			
		||||
      "tooltip": "opens/closes the side panel in notion - will only work if a mod is making use of it."
 | 
			
		||||
    }
 | 
			
		||||
  ]
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -58,7 +58,7 @@ export const options = {
 | 
			
		||||
  toggle: async (mod, opt) => {
 | 
			
		||||
    const checked = await profileDB.get([mod.id, opt.key], opt.value),
 | 
			
		||||
      $toggle = blocks.toggle(opt.label, checked),
 | 
			
		||||
      $tooltip = web.html`${web.icon('info', { class: 'input-tooltip' })}`,
 | 
			
		||||
      $tooltip = web.html`${await components.feather('info', { class: 'input-tooltip' })}`,
 | 
			
		||||
      $label = $toggle.children[0],
 | 
			
		||||
      $input = $toggle.children[1];
 | 
			
		||||
    if (opt.tooltip) {
 | 
			
		||||
@ -73,7 +73,7 @@ export const options = {
 | 
			
		||||
  },
 | 
			
		||||
  select: async (mod, opt) => {
 | 
			
		||||
    const value = await profileDB.get([mod.id, opt.key], opt.values[0]),
 | 
			
		||||
      $tooltip = web.html`${web.icon('info', { class: 'input-tooltip' })}`,
 | 
			
		||||
      $tooltip = web.html`${await components.feather('info', { class: 'input-tooltip' })}`,
 | 
			
		||||
      $label = web.render(
 | 
			
		||||
        web.html`<label class="input-label"></label>`,
 | 
			
		||||
        web.render(web.html`<p></p>`, opt.tooltip ? $tooltip : '', opt.label)
 | 
			
		||||
@ -88,7 +88,7 @@ export const options = {
 | 
			
		||||
      $select = web.html`<select class="input">
 | 
			
		||||
        ${$options.join('')}
 | 
			
		||||
      </select>`,
 | 
			
		||||
      $icon = web.html`${web.icon('chevron-down', { class: 'input-icon' })}`;
 | 
			
		||||
      $icon = web.html`${await components.feather('chevron-down', { class: 'input-icon' })}`;
 | 
			
		||||
    if (opt.tooltip) components.tooltip($tooltip, opt.tooltip);
 | 
			
		||||
    $select.addEventListener('change', async (event) => {
 | 
			
		||||
      await profileDB.set([mod.id, opt.key], $select.value);
 | 
			
		||||
@ -98,13 +98,13 @@ export const options = {
 | 
			
		||||
  },
 | 
			
		||||
  text: async (mod, opt) => {
 | 
			
		||||
    const value = await profileDB.get([mod.id, opt.key], opt.value),
 | 
			
		||||
      $tooltip = web.html`${web.icon('info', { class: 'input-tooltip' })}`,
 | 
			
		||||
      $tooltip = web.html`${await components.feather('info', { class: 'input-tooltip' })}`,
 | 
			
		||||
      $label = web.render(
 | 
			
		||||
        web.html`<label class="input-label"></label>`,
 | 
			
		||||
        web.render(web.html`<p></p>`, opt.tooltip ? $tooltip : '', opt.label)
 | 
			
		||||
      ),
 | 
			
		||||
      $input = web.html`<input type="text" class="input" value="${web.escape(value)}">`,
 | 
			
		||||
      $icon = web.html`${web.icon('type', { class: 'input-icon' })}`;
 | 
			
		||||
      $icon = web.html`${await components.feather('type', { class: 'input-icon' })}`;
 | 
			
		||||
    if (opt.tooltip) components.tooltip($tooltip, opt.tooltip);
 | 
			
		||||
    $input.addEventListener('change', async (event) => {
 | 
			
		||||
      await profileDB.set([mod.id, opt.key], $input.value);
 | 
			
		||||
@ -114,13 +114,13 @@ export const options = {
 | 
			
		||||
  },
 | 
			
		||||
  number: async (mod, opt) => {
 | 
			
		||||
    const value = await profileDB.get([mod.id, opt.key], opt.value),
 | 
			
		||||
      $tooltip = web.html`${web.icon('info', { class: 'input-tooltip' })}`,
 | 
			
		||||
      $tooltip = web.html`${await components.feather('info', { class: 'input-tooltip' })}`,
 | 
			
		||||
      $label = web.render(
 | 
			
		||||
        web.html`<label class="input-label"></label>`,
 | 
			
		||||
        web.render(web.html`<p></p>`, opt.tooltip ? $tooltip : '', opt.label)
 | 
			
		||||
      ),
 | 
			
		||||
      $input = web.html`<input type="number" class="input" value="${value}">`,
 | 
			
		||||
      $icon = web.html`${web.icon('hash', { class: 'input-icon' })}`;
 | 
			
		||||
      $icon = web.html`${await components.feather('hash', { class: 'input-icon' })}`;
 | 
			
		||||
    if (opt.tooltip) components.tooltip($tooltip, opt.tooltip);
 | 
			
		||||
    $input.addEventListener('change', async (event) => {
 | 
			
		||||
      await profileDB.set([mod.id, opt.key], $input.value);
 | 
			
		||||
@ -130,13 +130,13 @@ export const options = {
 | 
			
		||||
  },
 | 
			
		||||
  color: async (mod, opt) => {
 | 
			
		||||
    const value = await profileDB.get([mod.id, opt.key], opt.value),
 | 
			
		||||
      $tooltip = web.html`${web.icon('info', { class: 'input-tooltip' })}`,
 | 
			
		||||
      $tooltip = web.html`${await components.feather('info', { class: 'input-tooltip' })}`,
 | 
			
		||||
      $label = web.render(
 | 
			
		||||
        web.html`<label class="input-label"></label>`,
 | 
			
		||||
        web.render(web.html`<p></p>`, opt.tooltip ? $tooltip : '', opt.label)
 | 
			
		||||
      ),
 | 
			
		||||
      $input = web.html`<input type="text" class="input">`,
 | 
			
		||||
      $icon = web.html`${web.icon('droplet', { class: 'input-icon' })}`,
 | 
			
		||||
      $icon = web.html`${await components.feather('droplet', { class: 'input-icon' })}`,
 | 
			
		||||
      paint = () => {
 | 
			
		||||
        $input.style.background = $picker.toBackground();
 | 
			
		||||
        $input.style.color = $picker.isLight() ? '#000' : '#fff';
 | 
			
		||||
@ -163,7 +163,7 @@ export const options = {
 | 
			
		||||
  },
 | 
			
		||||
  file: async (mod, opt) => {
 | 
			
		||||
    const { filename } = (await profileDB.get([mod.id, opt.key], {})) || {},
 | 
			
		||||
      $tooltip = web.html`${web.icon('info', { class: 'input-tooltip' })}`,
 | 
			
		||||
      $tooltip = web.html`${await components.feather('info', { class: 'input-tooltip' })}`,
 | 
			
		||||
      $label = web.render(
 | 
			
		||||
        web.html`<label class="input-label"></label>`,
 | 
			
		||||
        web.render(web.html`<p></p>`, opt.tooltip ? $tooltip : '', opt.label)
 | 
			
		||||
@ -172,7 +172,7 @@ export const options = {
 | 
			
		||||
      $input = web.html`<input type="file" class="hidden" accept=${web.escape(
 | 
			
		||||
        opt.extensions.join(',')
 | 
			
		||||
      )}>`,
 | 
			
		||||
      $icon = web.html`${web.icon('file', { class: 'input-icon' })}`,
 | 
			
		||||
      $icon = web.html`${await components.feather('file', { class: 'input-icon' })}`,
 | 
			
		||||
      $filename = web.html`<span>${web.escape(filename || 'none')}</span>`,
 | 
			
		||||
      $latest = web.render(web.html`<button class="file-latest">Latest: </button>`, $filename);
 | 
			
		||||
    if (opt.tooltip) components.tooltip($tooltip, opt.tooltip);
 | 
			
		||||
@ -201,13 +201,13 @@ export const options = {
 | 
			
		||||
  },
 | 
			
		||||
  hotkey: async (mod, opt) => {
 | 
			
		||||
    const value = await profileDB.get([mod.id, opt.key], opt.value),
 | 
			
		||||
      $tooltip = web.html`${web.icon('info', { class: 'input-tooltip' })}`,
 | 
			
		||||
      $tooltip = web.html`${await components.feather('info', { class: 'input-tooltip' })}`,
 | 
			
		||||
      $label = web.render(
 | 
			
		||||
        web.html`<label class="input-label"></label>`,
 | 
			
		||||
        web.render(web.html`<p></p>`, opt.tooltip ? $tooltip : '', opt.label)
 | 
			
		||||
      ),
 | 
			
		||||
      $input = web.html`<input type="text" class="input" value="${web.escape(value)}">`,
 | 
			
		||||
      $icon = web.html`${web.icon('command', { class: 'input-icon' })}`;
 | 
			
		||||
      $icon = web.html`${await components.feather('command', { class: 'input-icon' })}`;
 | 
			
		||||
    if (opt.tooltip) components.tooltip($tooltip, opt.tooltip);
 | 
			
		||||
    $input.addEventListener('keydown', async (event) => {
 | 
			
		||||
      event.preventDefault();
 | 
			
		||||
 | 
			
		||||
@ -6,7 +6,7 @@
 | 
			
		||||
 | 
			
		||||
'use strict';
 | 
			
		||||
 | 
			
		||||
import { env, fs, storage, registry, web } from '../../api/_.mjs';
 | 
			
		||||
import { env, fs, storage, registry, web, components } from '../../api/_.mjs';
 | 
			
		||||
import { notifications } from './notifications.mjs';
 | 
			
		||||
import { blocks, options } from './blocks.mjs';
 | 
			
		||||
import './styles.mjs';
 | 
			
		||||
@ -74,17 +74,17 @@ $profile.addEventListener('click', async (event) => {
 | 
			
		||||
        pattern="/^[A-Za-z0-9_-]+$/"
 | 
			
		||||
      >`,
 | 
			
		||||
      $export = web.html`<button class="profile-export">
 | 
			
		||||
        ${web.icon('download', { class: 'profile-icon-action' })}
 | 
			
		||||
        ${await components.feather('download', { class: 'profile-icon-action' })}
 | 
			
		||||
      </button>`,
 | 
			
		||||
      $import = web.html`<label class="profile-import">
 | 
			
		||||
        <input type="file" class="hidden" accept="application/json">
 | 
			
		||||
        ${web.icon('upload', { class: 'profile-icon-action' })}
 | 
			
		||||
        ${await components.feather('upload', { class: 'profile-icon-action' })}
 | 
			
		||||
      </label>`,
 | 
			
		||||
      $save = web.html`<button class="profile-save">
 | 
			
		||||
        ${web.icon('save', { class: 'profile-icon-text' })} Save
 | 
			
		||||
        ${await components.feather('save', { class: 'profile-icon-text' })} Save
 | 
			
		||||
      </button>`,
 | 
			
		||||
      $delete = web.html`<button class="profile-delete">
 | 
			
		||||
        ${web.icon('trash-2', { class: 'profile-icon-text' })} Delete
 | 
			
		||||
        ${await components.feather('trash-2', { class: 'profile-icon-text' })} Delete
 | 
			
		||||
      </button>`,
 | 
			
		||||
      $error = web.html`<p class="profile-error"></p>`;
 | 
			
		||||
    $export.addEventListener('click', async (event) => {
 | 
			
		||||
@ -175,12 +175,12 @@ $profile.addEventListener('click', async (event) => {
 | 
			
		||||
      web.render(
 | 
			
		||||
        web.html`<label class="input-label"></label>`,
 | 
			
		||||
        $select,
 | 
			
		||||
        web.html`${web.icon('chevron-down', { class: 'input-icon' })}`
 | 
			
		||||
        web.html`${await components.feather('chevron-down', { class: 'input-icon' })}`
 | 
			
		||||
      ),
 | 
			
		||||
      web.render(
 | 
			
		||||
        web.html`<label class="input-label"></label>`,
 | 
			
		||||
        $edit,
 | 
			
		||||
        web.html`${web.icon('type', { class: 'input-icon' })}`
 | 
			
		||||
        web.html`${await components.feather('type', { class: 'input-icon' })}`
 | 
			
		||||
      ),
 | 
			
		||||
      web.render(web.html`<p class="profile-actions"></p>`, $export, $import, $save, $delete),
 | 
			
		||||
      $error
 | 
			
		||||
@ -288,7 +288,7 @@ const _$modListCache = {},
 | 
			
		||||
          web.render(
 | 
			
		||||
            web.html`<label class="search-container"></label>`,
 | 
			
		||||
            $search,
 | 
			
		||||
            web.html`${web.icon('search', { class: 'input-icon' })}`
 | 
			
		||||
            web.html`${await components.feather('search', { class: 'input-icon' })}`
 | 
			
		||||
          ),
 | 
			
		||||
          message ? web.html`<p class="main-message">${web.escape(message)}</p>` : '',
 | 
			
		||||
          $list
 | 
			
		||||
@ -303,14 +303,13 @@ const $notionNavItem = web.html`<h1 class="nav-notion">
 | 
			
		||||
      /width="\d+" height="\d+"/,
 | 
			
		||||
      `class="nav-notion-icon"`
 | 
			
		||||
    )}
 | 
			
		||||
    <a href="https://notion-enhancer.github.io/" target="_blank">notion-enhancer</a>
 | 
			
		||||
    <span>notion-enhancer</span>
 | 
			
		||||
  </h1>`;
 | 
			
		||||
$notionNavItem.children[0].addEventListener('click', env.focusNotion);
 | 
			
		||||
$notionNavItem.addEventListener('click', env.focusNotion);
 | 
			
		||||
 | 
			
		||||
const $coreNavItem = web.html`<a href="?view=core" class="nav-item">core</a>`,
 | 
			
		||||
  $extensionsNavItem = web.html`<a href="?view=extensions" class="nav-item">extensions</a>`,
 | 
			
		||||
  $themesNavItem = web.html`<a href="?view=themes" class="nav-item">themes</a>`,
 | 
			
		||||
  $communityNavItem = web.html`<a href="https://discord.gg/sFWPXtA" class="nav-item">community</a>`;
 | 
			
		||||
  $themesNavItem = web.html`<a href="?view=themes" class="nav-item">themes</a>`;
 | 
			
		||||
 | 
			
		||||
web.render(
 | 
			
		||||
  document.body,
 | 
			
		||||
@ -324,7 +323,8 @@ web.render(
 | 
			
		||||
        $coreNavItem,
 | 
			
		||||
        $extensionsNavItem,
 | 
			
		||||
        $themesNavItem,
 | 
			
		||||
        $communityNavItem
 | 
			
		||||
        web.html`<a href="https://notion-enhancer.github.io" class="nav-item">docs</a>`,
 | 
			
		||||
        web.html`<a href="https://discord.gg/sFWPXtA" class="nav-item">community</a>`
 | 
			
		||||
      ),
 | 
			
		||||
      $main
 | 
			
		||||
    ),
 | 
			
		||||
 | 
			
		||||
@ -6,7 +6,7 @@
 | 
			
		||||
 | 
			
		||||
'use strict';
 | 
			
		||||
 | 
			
		||||
import { env, fs, storage, fmt, registry, web } from '../../api/_.mjs';
 | 
			
		||||
import { env, fs, storage, fmt, registry, web, components } from '../../api/_.mjs';
 | 
			
		||||
import { tw } from './styles.mjs';
 | 
			
		||||
 | 
			
		||||
export const notifications = {
 | 
			
		||||
@ -16,7 +16,7 @@ export const notifications = {
 | 
			
		||||
    registry.welcomeNotification,
 | 
			
		||||
    ...(await fs.getJSON('https://notion-enhancer.github.io/notifications.json')),
 | 
			
		||||
  ],
 | 
			
		||||
  add({ icon, message, id = undefined, color = undefined, link = undefined }) {
 | 
			
		||||
  async add({ icon, message, id = undefined, color = undefined, link = undefined }) {
 | 
			
		||||
    const $notification = link
 | 
			
		||||
        ? web.html`<a
 | 
			
		||||
          href="${web.escape(link)}"
 | 
			
		||||
@ -47,16 +47,16 @@ export const notifications = {
 | 
			
		||||
        web.html`<span class="notification-text markdown-inline">
 | 
			
		||||
          ${fmt.md.renderInline(message)}
 | 
			
		||||
        </span>`,
 | 
			
		||||
        web.html`${web.icon(icon, { class: 'notification-icon' })}`
 | 
			
		||||
        web.html`${await components.feather(icon, { class: 'notification-icon' })}`
 | 
			
		||||
      )
 | 
			
		||||
    );
 | 
			
		||||
    return $notification;
 | 
			
		||||
  },
 | 
			
		||||
  _onChange: false,
 | 
			
		||||
  onChange() {
 | 
			
		||||
  async onChange() {
 | 
			
		||||
    if (this._onChange) return;
 | 
			
		||||
    this._onChange = true;
 | 
			
		||||
    const $notification = this.add({
 | 
			
		||||
    const $notification = await this.add({
 | 
			
		||||
      icon: 'refresh-cw',
 | 
			
		||||
      message: 'Reload to apply changes.',
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
@ -36,13 +36,13 @@ const customClasses = {
 | 
			
		||||
  'notification-icon': apply`fill-current opacity-75 h-4 w-4 mx-2`,
 | 
			
		||||
  'body-container': apply`flex w-full h-full overflow-hidden`,
 | 
			
		||||
  'content-container': apply`h-full w-full-96`,
 | 
			
		||||
  'nav': apply`px-4 py-3 flex flex-wrap items-center border-b border-divider h-48 sm:h-32 lg:h-16`,
 | 
			
		||||
  'nav': apply`px-4 py-3 flex flex-wrap items-center border-b border-divider h-64 sm:h-48 md:h-32 lg:h-16`,
 | 
			
		||||
  'nav-notion': apply`flex items-center font-semibold text-xl cursor-pointer select-none mr-4
 | 
			
		||||
      ml-4 sm:mb-4 md:w-full lg:(w-auto ml-0 mb-0)`,
 | 
			
		||||
  'nav-notion-icon': apply`h-12 w-12 mr-5 sm:(h-6 w-6 mr-3)`,
 | 
			
		||||
  'nav-item': apply`ml-4 px-3 py-2 rounded-md text-sm font-medium hover:bg-interactive-hover focus:bg-interactive-focus`,
 | 
			
		||||
  'nav-item-selected': apply`ml-4 px-3 py-2 rounded-md text-sm font-medium ring-1 ring-divider bg-notion-secondary`,
 | 
			
		||||
  'main': apply`transition px-4 py-3 overflow-y-auto max-h-full-48 sm:max-h-full-32 lg:max-h-full-16`,
 | 
			
		||||
  'main': apply`transition px-4 py-3 overflow-y-auto max-h-full-64 sm:max-h-full-48 md:max-h-full-32 lg:max-h-full-16`,
 | 
			
		||||
  'main-message': apply`mx-2.5 my-2.5 px-px text-sm text-foreground-secondary text-justify`,
 | 
			
		||||
  'mods-list': apply`flex flex-wrap`,
 | 
			
		||||
  'mod-container': apply`w-full md:w-1/2 lg:w-1/3 xl:w-1/4 2xl:w-1/5 px-2.5 py-2.5 box-border`,
 | 
			
		||||
@ -145,6 +145,7 @@ setup({
 | 
			
		||||
        'full-16': 'calc(100% - 4rem)',
 | 
			
		||||
        'full-32': 'calc(100% - 8rem)',
 | 
			
		||||
        'full-48': 'calc(100% - 12rem)',
 | 
			
		||||
        'full-64': 'calc(100% - 16rem)',
 | 
			
		||||
      },
 | 
			
		||||
    },
 | 
			
		||||
  },
 | 
			
		||||
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user