From 94460375bf30d4749150d234c19521e797272f54 Mon Sep 17 00:00:00 2001 From: dragonwocky Date: Fri, 4 Aug 2023 23:59:37 +1000 Subject: [PATCH] feat: make side panel resizeable --- src/api/components/tooltip.css | 31 --------- src/api/components/tooltip.mjs | 118 --------------------------------- src/core/islands/Panel.mjs | 94 ++++++++++++++++---------- src/core/islands/Tooltip.mjs | 6 +- 4 files changed, 64 insertions(+), 185 deletions(-) delete mode 100644 src/api/components/tooltip.css delete mode 100644 src/api/components/tooltip.mjs diff --git a/src/api/components/tooltip.css b/src/api/components/tooltip.css deleted file mode 100644 index 32664dd..0000000 --- a/src/api/components/tooltip.css +++ /dev/null @@ -1,31 +0,0 @@ -/** - * notion-enhancer: components - * (c) 2021 dragonwocky (https://dragonwocky.me/) - * (https://notion-enhancer.github.io/) under the MIT license - */ - -#enhancer--tooltip { - font-family: var(--theme--font_sans); - background: var(--theme--ui_tooltip); - border-radius: 3px; - box-shadow: var(--theme--ui_shadow) 0px 1px 4px; - color: var(--theme--ui_tooltip-description); - display: none; - font-size: 12px; - font-weight: 500; - line-height: 1.4; - max-width: 20rem; - overflow: hidden; - padding: 4px 8px; - position: absolute; - z-index: 999999999999999999; - pointer-events: none; -} -#enhancer--tooltip p { - margin: 0; -} -#enhancer--tooltip b, -#enhancer--tooltip strong { - font-weight: 500; - color: var(--theme--ui_tooltip-title); -} diff --git a/src/api/components/tooltip.mjs b/src/api/components/tooltip.mjs deleted file mode 100644 index 80f1d03..0000000 --- a/src/api/components/tooltip.mjs +++ /dev/null @@ -1,118 +0,0 @@ -/** - * notion-enhancer: components - * (c) 2021 dragonwocky (https://dragonwocky.me/) - * (https://notion-enhancer.github.io/) under the MIT license - */ - -'use strict'; - -/** shared notion-style elements */ - -import { fs, web } from '../index.mjs'; - -let $stylesheet, _$tooltip; - -const countLines = ($el) => - [...$el.getClientRects()].reduce( - (prev, val) => (prev.some((p) => p.y === val.y) ? prev : [...prev, val]), - [] - ).length, - position = ($ref, offsetDirection, maxLines) => { - _$tooltip.style.top = `0px`; - _$tooltip.style.left = `0px`; - const rect = $ref.getBoundingClientRect(), - { offsetWidth, offsetHeight } = _$tooltip, - pad = 6; - let x = rect.x, - y = Math.floor(rect.y); - - if (['top', 'bottom'].includes(offsetDirection)) { - if (offsetDirection === 'top') y -= offsetHeight + pad; - if (offsetDirection === 'bottom') y += rect.height + pad; - x -= offsetWidth / 2 - rect.width / 2; - _$tooltip.style.left = `${x}px`; - _$tooltip.style.top = `${y}px`; - const testLines = () => countLines(_$tooltip.firstElementChild) > maxLines, - padEdgesX = testLines(); - while (testLines()) { - _$tooltip.style.left = `${window.innerWidth - x > x ? x++ : x--}px`; - } - if (padEdgesX) { - x += window.innerWidth - x > x ? pad : -pad; - _$tooltip.style.left = `${x}px`; - } - _$tooltip.style.textAlign = 'center'; - } - - if (['left', 'right'].includes(offsetDirection)) { - y -= offsetHeight / 2 - rect.height / 2; - if (offsetDirection === 'left') x -= offsetWidth + pad; - if (offsetDirection === 'right') x += rect.width + pad; - _$tooltip.style.left = `${x}px`; - _$tooltip.style.top = `${y}px`; - _$tooltip.style.textAlign = 'start'; - } - - return true; - }; - -/** - * add a tooltip to show extra information on hover - * @param {HTMLElement} $ref - the element that will trigger the tooltip when hovered - * @param {string|HTMLElement} $content - markdown or element content of the tooltip - * @param {object=} options - configuration of how the tooltip should be displayed - * @param {number=} options.delay - the amount of time in ms the element needs to be hovered over - * for the tooltip to be shown (default: 100) - * @param {string=} options.offsetDirection - which side of the element the tooltip - * should be shown on: 'top', 'bottom', 'left' or 'right' (default: 'bottom') - * @param {number=} options.maxLines - the max number of lines that the content may be wrapped - * to, used to position and size the tooltip correctly (default: 1) - */ -export const addTooltip = async ( - $ref, - $content, - { delay = 100, offsetDirection = 'bottom', maxLines = 1 } = {} -) => { - if (!$stylesheet) { - $stylesheet = web.loadStylesheet('api/components/tooltip.css'); - _$tooltip = web.html`
`; - web.render(document.body, _$tooltip); - } - - if (!globalThis.markdownit) await import(fs.localPath('dep/markdown-it.min.js')); - const md = markdownit({ linkify: true }); - - if (!($content instanceof Element)) - $content = web.html`
- ${$content - .split('\n') - .map((text) => md.renderInline(text)) - .join('
')} -
`; - - let displayDelay; - $ref.addEventListener('mouseover', (_event) => { - if (!displayDelay) { - displayDelay = setTimeout(async () => { - if ($ref.matches(':hover')) { - if (_$tooltip.style.display !== 'block') { - _$tooltip.style.display = 'block'; - web.render(web.empty(_$tooltip), $content); - position($ref, offsetDirection, maxLines); - await _$tooltip.animate([{ opacity: 0 }, { opacity: 1 }], { duration: 65 }) - .finished; - } - } - displayDelay = undefined; - }, delay); - } - }); - - $ref.addEventListener('mouseout', async (_event) => { - displayDelay = undefined; - if (_$tooltip.style.display === 'block' && !$ref.matches(':hover')) { - await _$tooltip.animate([{ opacity: 1 }, { opacity: 0 }], { duration: 65 }).finished; - _$tooltip.style.display = ''; - } - }); -}; diff --git a/src/core/islands/Panel.mjs b/src/core/islands/Panel.mjs index 1a34cb1..e335b79 100644 --- a/src/core/islands/Panel.mjs +++ b/src/core/islands/Panel.mjs @@ -17,50 +17,74 @@ import { Tooltip } from "./Tooltip.mjs"; -function Panel( - { - _getWidth, - _setWidth, - _getOpen, - _setOpen, - minWidth = 260, - maxWidth = 520, - ...props - }, - ...children -) { +function Panel({ + _getWidth, + _setWidth, + _getOpen, + _setOpen, + minWidth = 260, + maxWidth = 640, + ...props +}) { const { html, extendProps } = globalThis.__enhancerApi; extendProps(props, { class: `notion-enhancer--side-panel order-2 shrink-0 transition-[width] open:w-[var(--side\\_panel--width)] w-0 border-l-1 border-[color:var(--theme--fg-border)] - relative bg-[color:var(--theme--bg-primary)]`, + relative bg-[color:var(--theme--bg-primary)] group`, }); + const $resizeHandle = html`
`, + $panel = html``; + const $tooltip = html`<${Tooltip}> Drag to resize
Click to closed `, - $panel = html``; + showTooltip = (event) => { + setTimeout(() => { + const panelOpen = $panel.hasAttribute("open"), + handleHovered = $resizeHandle.matches(":hover"); + if (!panelOpen || !handleHovered) return; + const { x } = $resizeHandle.getBoundingClientRect(); + $tooltip.show(x, event.clientY); + }, 200); + }; + $resizeHandle.addEventListener("mouseover", showTooltip); + $resizeHandle.addEventListener("mouseout", $tooltip.hide); + $resizeHandle.addEventListener("click", $panel.close); + + let preDragWidth, + dragStartX = 0; + const startDrag = async (event) => { + dragStartX = event.clientX; + preDragWidth = await _getWidth?.(); + if (isNaN(preDragWidth)) preDragWidth = minWidth; + document.addEventListener("mousemove", onDrag); + document.addEventListener("mouseup", endDrag); + $panel.style.transitionDuration = "0ms"; + }, + onDrag = (event) => { + event.preventDefault(); + const newWidth = preDragWidth + (dragStartX - event.clientX); + $panel.resize(newWidth, true); + }, + endDrag = (event) => { + document.removeEventListener("mousemove", onDrag); + document.removeEventListener("mouseup", endDrag); + const finalWidth = preDragWidth + (dragStartX - event.clientX); + $panel.style.transitionDuration = ""; + $panel.resize(finalWidth); + }; + $resizeHandle.addEventListener("mousedown", startDrag); const notionHelp = ".notion-help-button", repositionHelp = async (attempts = 0) => { @@ -80,12 +104,12 @@ function Panel( $notionHelp.animate(keyframes, options); }; - $panel.resize = async (width) => { + $panel.resize = async (width, dragActive = false) => { $tooltip.hide(); if (width) { width = Math.max(width, minWidth); width = Math.min(width, maxWidth); - _setWidth?.(width); + if (!dragActive) _setWidth?.(width); } else width = await _getWidth?.(); if (isNaN(width)) width = minWidth; $panel.style.setProperty("--side_panel--width", `${width}px`); diff --git a/src/core/islands/Tooltip.mjs b/src/core/islands/Tooltip.mjs index 286025f..05acd82 100644 --- a/src/core/islands/Tooltip.mjs +++ b/src/core/islands/Tooltip.mjs @@ -29,7 +29,11 @@ function Tooltip(props, ...children) { if (!document.contains($tooltip)) $notionApp?.append($tooltip); requestAnimationFrame(() => { $tooltip.setAttribute("open", true); - $tooltip.style.left = `${x - $tooltip.clientWidth - 6}px`; + x -= $tooltip.clientWidth + 6; + if (x < 0) x += $tooltip.clientWidth + 12; + y -= $tooltip.clientHeight / 2; + if (y < 0) y += $tooltip.clientHeight / 2; + $tooltip.style.left = `${x}px`; $tooltip.style.top = `${y}px`; $tooltip.onshow?.(); });