From 3c05d0a957832dcf3351492bf16253100551803c Mon Sep 17 00:00:00 2001 From: John Factotum <50942278+johnfactotum@users.noreply.github.com> Date: Fri, 21 Oct 2022 18:57:26 +0000 Subject: [PATCH] Reader: add footnote popovers Lacks a "go to" button, but at least shows something instead of nothing --- reader.html | 14 ++++++++++++ reader.js | 25 +++++++++++++++++++++ ui/popover.js | 61 +++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 100 insertions(+) create mode 100644 ui/popover.js diff --git a/reader.html b/reader.html index 8820ea3..69e5634 100644 --- a/reader.html +++ b/reader.html @@ -237,6 +237,20 @@ body { background-repeat: no-repeat; background-image: url('data:image/svg+xml;utf8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2224%22%20height%3D%2224%22%3E%3Ccircle%20cx%3D%2212%22%20cy%3D%2212%22%20r%3D%223%22%2F%3E%3C%2Fsvg%3E'); } +.popover { + background: Canvas; + color: CanvasText; + border-radius: 6px; + box-shadow: 0 0 0 1px rgba(0, 0, 0, .2), 0 0 16px rgba(0, 0, 0, .1), 0 0 32px rgba(0, 0, 0, .1); +} +.popover-arrow-down { + fill: Canvas; + filter: drop-shadow(0 -1px 0 rgba(0, 0, 0, .2)); +} +.popover-arrow-up { + fill: Canvas; + filter: drop-shadow(0 1px 0 rgba(0, 0, 0, .2)); +}
diff --git a/reader.js b/reader.js index 86ee8b2..59d07ee 100644 --- a/reader.js +++ b/reader.js @@ -2,6 +2,7 @@ import { View } from './view.js' import { createTOCView } from './ui/tree.js' import { createMenu } from './ui/menu.js' +import { createPopover } from './ui/popover.js' const { ZipReader, BlobReader, TextWriter, BlobWriter } = zip zip.configure({ useWebWorkers: false }) @@ -119,6 +120,30 @@ const emit = obj => { if (tocItem?.href) setCurrentHref?.(tocItem.href) break } + case 'reference': { + const { content, pos: { point, dir } } = obj + const iframe = document.createElement('iframe') + iframe.sandbox = 'allow-same-origin' + iframe.srcdoc = content + iframe.onload = () => { + const doc = iframe.contentDocument + doc.documentElement.style.colorScheme = 'light dark' + doc.body.style.margin = '18px' + } + Object.assign(iframe.style, { + border: '0', + width: '100%', + height: '100%', + }) + const { popover, arrow, overlay } = createPopover(300, 250, point, dir) + overlay.style.zIndex = 3 + popover.style.zIndex = 3 + arrow.style.zIndex = 3 + popover.append(iframe) + document.body.append(overlay) + document.body.append(popover) + document.body.append(arrow) + } } } diff --git a/ui/popover.js b/ui/popover.js new file mode 100644 index 0000000..5b99618 --- /dev/null +++ b/ui/popover.js @@ -0,0 +1,61 @@ +const clamp = (min, max, x) => Math.min(max, Math.max(min, x)) + +const arrowHeight = 10 +const arrowWidth = 20 +const radius = 6 + +const createSVGElement = tag => + document.createElementNS('http://www.w3.org/2000/svg', tag) + +const createArrow = down => { + const h = arrowHeight + 1 + const svg = createSVGElement('svg') + svg.setAttribute('width', arrowWidth) + svg.setAttribute('height', arrowHeight) + const polygon = createSVGElement('polygon') + polygon.setAttribute('points', down + ? `0 ${h}, ${arrowWidth / 2} 0, ${arrowWidth} ${h}` + : `0 0, ${arrowWidth / 2} ${h}, ${arrowWidth} 0`) + svg.classList.add(down ? 'popover-arrow-down' : 'popover-arrow-up') + svg.append(polygon) + return svg +} + +export const createPopover = (width, height, { x, y }, dir) => { + const down = dir === 'down' + const fullHeight = height + arrowHeight + const overlay = document.createElement('div') + Object.assign(overlay.style, { + position: 'absolute', + left: '0', + top: '0', + width: '100vw', + height: '100vh', + }) + const arrow = createArrow(down) + Object.assign(arrow.style, { + position: 'absolute', + left: `${clamp(radius, window.innerWidth - arrowWidth - radius, + x - arrowWidth / 2)}px`, + top: `${clamp(0, window.innerHeight - arrowHeight, + down ? y : y - arrowHeight)}px`, + }) + const popover = document.createElement('div') + popover.classList.add('popover') + Object.assign(popover.style, { + position: 'absolute', + boxSizing: 'border-box', + overflow: 'hidden', + left: `${clamp(0, window.innerWidth - width, x - width / 2)}px`, + top: `${clamp(0, window.innerHeight - fullHeight, + down ? y + arrowHeight : y - fullHeight)}px`, + width: `${width}px`, + height: `${height}px`, + }) + overlay.addEventListener('click', () => { + overlay.parentNode.removeChild(overlay) + arrow.parentNode.removeChild(arrow) + popover.parentNode.removeChild(popover) + }) + return { popover, arrow, overlay } +}