From 047b4cce7d20a49563c4a6172f3e3a5a5646b3fb Mon Sep 17 00:00:00 2001 From: dragonwocky Date: Sat, 23 Oct 2021 23:11:52 +1100 Subject: [PATCH] extension: simpler databases --- repo/code-line-numbers/client.mjs | 2 +- repo/collapsible-headers/app.css | 86 ------ repo/collapsible-headers/client.mjs | 15 +- repo/indentation-lines/client.mjs | 2 +- repo/outliner/client.mjs | 2 +- repo/registry.json | 1 + repo/simpler-databases/client.css | 429 ++++++++++++++++++++++++++ repo/simpler-databases/client.mjs | 452 ++++++++++++++++++++++++++++ repo/simpler-databases/mod.json | 22 ++ repo/tweaks/client.css | 6 +- 10 files changed, 921 insertions(+), 96 deletions(-) delete mode 100644 repo/collapsible-headers/app.css create mode 100644 repo/simpler-databases/client.css create mode 100644 repo/simpler-databases/client.mjs create mode 100644 repo/simpler-databases/mod.json diff --git a/repo/code-line-numbers/client.mjs b/repo/code-line-numbers/client.mjs index 3a318ba..eb02472 100644 --- a/repo/code-line-numbers/client.mjs +++ b/repo/code-line-numbers/client.mjs @@ -26,7 +26,7 @@ export default async function ({ web }, db) { lineNumbers += `${i}\n`; if (wordWrap && lines[i - 1]) { $temp.innerText = lines[i - 1]; - $codeBlock.lastElementChild.append($temp); + web.render($codeBlock.lastElementChild, $temp); const height = parseFloat($temp.getBoundingClientRect().height); $temp.remove(); for (let j = 1; j < height / 20.4; j++) lineNumbers += '\n'; diff --git a/repo/collapsible-headers/app.css b/repo/collapsible-headers/app.css deleted file mode 100644 index d96a25f..0000000 --- a/repo/collapsible-headers/app.css +++ /dev/null @@ -1,86 +0,0 @@ -/* - * collapsible headers - * (c) 2020 dragonwocky (https://dragonwocky.me/) - * (c) 2020 CloudHill - * under the MIT license - */ - - .notion-page-content .notion-selectable[collapsed] { - max-height: 0px; - overflow: hidden; - opacity: 0; -} - -.notion-page-content .notion-selectable[collapsed] .notion-selectable { - pointer-events: none; -} - -.collapse-header { - flex-grow: 0; - flex-shrink: 0; - align-self: center; - width: 24px; - height: 24px; - padding: 6px; - margin: 0 6px; - border-radius: 3px; - display: flex; - align-items: center; - justify-content: center; - z-index: 1; - cursor: pointer; - transition: 200ms ease-in; -} -.collapse-header:hover { - background: var(--theme--interactive_hover); -} -/* position: left */ -.collapse-header:first-child { - margin-left: 2px; -} -/* position: right / inline */ -.collapse-header:last-child { - opacity: 0; -} - -/* show toggle on: collapsed, hover, focus */ -[data-collapsed="true"] .collapse-header:last-child, -[data-collapsed]:hover .collapse-header:last-child, -[data-collapsed] :focus + .collapse-header:last-child { - opacity: 1; -} - -.collapse-header svg { - width: 100%; - height: 100%; - transition: transform 200ms ease-out 0s; -} -/* position: left */ -.collapse-header:first-child svg { - transform: rotateZ(90deg); -} -/* position: right / inline */ -.collapse-header:last-child svg { - transform: rotateZ(270deg); -} - -[data-collapsed="false"] .collapse-header svg { - transform: rotateZ(180deg); -} - -/* position: inline */ -[inline-toggle] { - position: relative; - overflow: hidden; -} -[inline-toggle] [placeholder] { - width: auto !important; -} -[inline-toggle] [placeholder]::after { - content: ''; - position: absolute; - top: 0; - width: 100%; - height: 100%; - cursor: text; -} diff --git a/repo/collapsible-headers/client.mjs b/repo/collapsible-headers/client.mjs index 5b04f44..858178a 100644 --- a/repo/collapsible-headers/client.mjs +++ b/repo/collapsible-headers/client.mjs @@ -7,6 +7,7 @@ export default async function ({ web }, db) { const headerSelector = '.notion-page-content [class*="header-block"]', + pageScroller = '.notion-frame > .notion-scroller.vertical.horizontal', haloClass = 'notion-selectable-halo', blockSelector = '.notion-selectable[data-block-id]', dividerClass = 'notion-divider-block', @@ -30,7 +31,7 @@ export default async function ({ web }, db) { overflow: 'hidden', }; - const collapseParentsCache = new Map(), + let collapseParentsCache = new Map(), collapsedBlocksCache = new Map(); const getHeaderLevel = ($block) => { @@ -81,7 +82,7 @@ export default async function ({ web }, db) { [ animationCollapsed, { - height: $block.scrollHeight + 'px', + maxHeight: '100%', opacity: 1, marginTop: $block.style.marginTop, marginBottom: $block.style.marginBottom, @@ -181,6 +182,12 @@ export default async function ({ web }, db) { }; const insertToggles = async (event) => { + if ([...event.addedNodes].some(($node) => $node?.matches(pageScroller))) { + collapseParentsCache = new Map(); + collapsedBlocksCache = new Map(); + return; + } + const childNodeEvent = event.target.matches(blockSelector) && !event.target.matches(headerSelector); if (childNodeEvent) return; @@ -241,7 +248,7 @@ export default async function ({ web }, db) { `; if (togglePosition === 'left') { $header.firstChild.prepend($toggle); - } else $header.firstChild.append($toggle); + } else web.render($header.firstChild, $toggle); if (togglePosition === 'inline') $header.firstChild.classList.add(inlineToggleClass); $toggle.header = $header; @@ -266,7 +273,7 @@ export default async function ({ web }, db) { expandHeaderSection($header, animateToggle); } }; - web.addDocumentObserver(insertToggles, [headerSelector]); + web.addDocumentObserver(insertToggles, ['.notion-page-content', headerSelector]); web.addHotkeyListener(toggleHotkey, (event) => { const $header = document.activeElement.closest(headerSelector); diff --git a/repo/indentation-lines/client.mjs b/repo/indentation-lines/client.mjs index 35137a9..cf48203 100644 --- a/repo/indentation-lines/client.mjs +++ b/repo/indentation-lines/client.mjs @@ -87,5 +87,5 @@ export default async function ({ web }, db) { } } - document.head.append(web.html``); + web.render(document.head, web.html``); } diff --git a/repo/outliner/client.mjs b/repo/outliner/client.mjs index 0f282b1..ca04b58 100644 --- a/repo/outliner/client.mjs +++ b/repo/outliner/client.mjs @@ -61,7 +61,7 @@ export default async function ({ web, components }, db) { $outlineHeader.addEventListener('click', (event) => { location.hash = ''; }); - $fragment.append($outlineHeader); + web.render($fragment, $outlineHeader); } if ($fragment.innerHTML !== $headingList.innerHTML) { web.render(web.empty($headingList), ...$fragment.children); diff --git a/repo/registry.json b/repo/registry.json index 6f83131..f51e23f 100644 --- a/repo/registry.json +++ b/repo/registry.json @@ -22,6 +22,7 @@ "scroll-to-top", "indentation-lines", "right-to-left", + "simpler-databases", "emoji-sets", "bypass-preview", "topbar-icons", diff --git a/repo/simpler-databases/client.css b/repo/simpler-databases/client.css new file mode 100644 index 0000000..e5d2d2f --- /dev/null +++ b/repo/simpler-databases/client.css @@ -0,0 +1,429 @@ +/* + * notion-enhancer: simpler databases + * (c) 2020 CloudHill (https://github.com/CloudHill) + * (c) 2021 dragonwocky (https://dragonwocky.me/) + * (https://notion-enhancer.github.io/) under the MIT license + */ + +.simpler_databases--config_button:hover { + background: linear-gradient( + var(--theme--ui_interactive-hover), + var(--theme--ui_interactive-hover) + ), + linear-gradient(var(--theme--bg), var(--theme--bg)); +} +.simpler_databases--config_button svg { + width: 12px; + height: 12px; + fill: var(--theme--icon_secondary); +} +.notion-collection_view-block[data-simpler-db-tweaks]:not([data-simpler-db-tweaks*='[icon]']):not([data-simpler-db-tweaks*='[title]']):not([data-simpler-db-tweaks*='[toggle]']):not([data-simpler-db-tweaks*='[views']):not([data-simpler-db-tweaks*='[toolbar]']) + [style*=' height: 42px;'] + > :last-child { + position: absolute; + top: 4px; + right: 0; + z-index: 99; + pointer-events: auto; +} +.notion-collection_view-block[data-simpler-db-tweaks]:not([data-simpler-db-tweaks*='[icon]']):not([data-simpler-db-tweaks*='[title]']):not([data-simpler-db-tweaks*='[toggle]']):not([data-simpler-db-tweaks*='[views']):not([data-simpler-db-tweaks*='[toolbar]']) + .simpler_databases--config_button { + 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; +} +.notion-collection_view-block[data-simpler-db-tweaks]:not([data-simpler-db-tweaks*='[icon]']):not([data-simpler-db-tweaks*='[title]']):not([data-simpler-db-tweaks*='[toggle]']):not([data-simpler-db-tweaks*='[views']):not([data-simpler-db-tweaks*='[toolbar]']) + .simpler_databases--config_button:not(:hover) { + background: var(--theme--bg); +} + +.simpler_databases--overlay_container { + position: fixed; + top: 0; + right: 0; + bottom: 0; + left: 0; + z-index: 999; + overflow: hidden; +} + +.simpler_databases--config_menu { + position: relative; + width: 220px; + max-height: 70vh; + padding: 8px 0; + border-radius: 3px; + 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; + background: var(--theme--bg_card); + overflow: hidden auto; +} + +.simpler_databases--config_item-toggle, +.simpler_databases--config_item-input { + display: flex; + align-items: center; + width: 100%; + min-height: 28px; + font-size: 14px; + line-height: 1.2; + user-select: none; +} +.simpler_databases--config_item-toggle { + cursor: pointer; +} +.simpler_databases--config_item-toggle:hover, +.simpler_databases--config_item-toggle:focus { + background: var(--theme--ui_interactive-hover); +} +.simpler_databases--config_item-input { + padding: 6px 0; +} + +.simpler_databases--config_title { + margin: 0 14px; + min-width: 0; + flex: 1 1 auto; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +.simpler_databases--config_toggle { + flex-shrink: 0; + position: relative; + height: 14px; + width: 26px; + margin-left: auto; + margin-right: 14px; + padding: 2px; + border-radius: 44px; + cursor: pointer; + user-select: none; + box-sizing: content-box; + background: var(--theme--ui_toggle-off); + transition: background 200ms ease 0s, box-shadow 200ms ease 0s; +} +.simpler_databases--config_toggle[data-toggled='true'] { + background: var(--theme--ui_toggle-on); +} +.simpler_databases--config_toggle::before { + content: ''; + position: absolute; + width: 14px; + height: 14px; + border-radius: 44px; + background: var(--theme--ui_toggle-feature); + transition: transform 200ms ease-out 0s, background 200ms ease-out 0s; +} +.simpler_databases--config_toggle[data-toggled='true']::before { + transform: translateX(12px); +} + +.simpler_databases--config_input { + display: flex; + align-items: center; + height: 28px; + margin: 0 14px; + padding: 3px 6px; + background: var(--theme--ui_input); + box-shadow: var(--theme--ui_shadow) 0px 0px 0px 1px inset; + border-radius: 3px; + font-size: 14px; + line-height: 20px; + cursor: text; + width: 100%; +} +.simpler_databases--config_input input { + font-size: inherit; + line-height: inherit; + border: none; + background: none; + width: 100%; + display: block; + resize: none; + padding: 0; +} + +.simpler_databases--config_divider { + border-bottom: 1px solid var(--theme--ui_divider); + width: 100%; + margin: 6px 0; +} + +.notion-collection_view-block[data-simpler-db-tweaks*='[config-open]'] + [style*=' height: 42px;'] + > :not(:first-child) { + opacity: 1 !important; +} + +/* TWEAKS */ + +/* Toggle */ +.simpler_databases--toggle { + flex-shrink: 0; + width: 24px; + height: 24px; + display: flex; + align-items: center; + justify-content: center; + border-radius: 3px; + cursor: pointer; + transition: background 20ms ease-in 0s; +} +.simpler_databases--toggle svg { + width: 12px; + height: 12px; + transform: rotateZ(180deg); + transition: transform 200ms ease-out 0s; +} +.simpler_databases--toggle:hover { + background: var(--theme--ui_interactive-hover); +} + +.notion-collection_view-block[data-simpler-db-tweaks*='[toggle]'][data-simpler-db-toggle-hidden='true'] + .simpler_databases--toggle + svg { + transform: rotateZ(90deg); +} + +.notion-collection_view-block[data-simpler-db-tweaks*='[toggle]'] > div > .notion-scroller { + transition: height 200ms ease-in, opacity 200ms ease-in; +} +.notion-collection_view-block[data-simpler-db-tweaks*='[toggle]'][data-simpler-db-toggle-hidden='true'] + > div + > .notion-scroller { + opacity: 0; + height: 0 !important; +} + +.notion-collection_view-block[data-simpler-db-tweaks*='[toggle]'][data-simpler-db-toggle-hidden='true'] + [data-simpler-db-hide-items] + [class$='view'] + > .notion-collection_view-block, +.notion-collection_view-block[data-simpler-db-tweaks*='[toggle]'][data-simpler-db-toggle-hidden='true'] + [data-simpler-db-hide-items] + .notion-collection-item { + display: none !important; +} + +/* Title */ + +.notion-collection_view-block[data-simpler-db-tweaks]:not([data-simpler-db-tweaks*='[title]']) + [style*=' height: 42px;'] + > [style*='white-space: nowrap; overflow: hidden;'] + [placeholder] { + display: none !important; +} + +/* Icons + Link Arrows */ + +.notion-collection_view-block[data-simpler-db-tweaks]:not([data-simpler-db-tweaks*='[icon]']) + [style*=' height: 42px;'] + a + :first-child[style*='margin-right: 6px'] { + display: none !important; +} + +/* Views */ + +.notion-collection_view-block[data-simpler-db-tweaks]:not([data-simpler-db-tweaks*='[views]']) + [style*=' height: 42px;'] + > [role='button'] { + display: none !important; +} + +/* Toolbar */ + +.notion-collection_view-block[data-simpler-db-tweaks]:not([data-simpler-db-tweaks*='[toolbar]']) + .simpler_databases--config_button + ~ * { + display: none !important; +} + +/* Header - table, calendar */ + +.notion-collection_view-block[data-simpler-db-tweaks]:not([data-simpler-db-tweaks*='[header_row]']) + .notion-table-view + > .notion-collection_view-block + > :first-child, +.notion-collection_view-block[data-simpler-db-tweaks]:not([data-simpler-db-tweaks*='[header_row]']) + .notion-table-view + > .notion-collection_view-block + > :first-child + + div { + display: none !important; +} + +.notion-collection_view-block[data-simpler-db-tweaks]:not([data-simpler-db-tweaks*='[header_row]']) + .notion-table-view + .notion-collection_view-block + > [style*='height: 34px'] + + div { + border-top: 1px solid var(--theme--ui_divider); +} + +/* New Item - table, board, timeline, list, gallery */ + +.notion-collection_view-block[data-simpler-db-tweaks]:not([data-simpler-db-tweaks*='[new_item]']) + .notion-table-view-add-row, +.notion-collection_view-block[data-simpler-db-tweaks]:not([data-simpler-db-tweaks*='[new_item]']) + .notion-board-view + .notion-board-group + > [role='button']:not(.notion-collection-item), +.notion-collection_view-block[data-simpler-db-tweaks]:not([data-simpler-db-tweaks*='[new_item]']) + .notion-timeline-item-row:last-child, +.notion-collection_view-block[data-simpler-db-tweaks]:not([data-simpler-db-tweaks*='[new_item]']) + .notion-list-view + > .notion-collection_view-block + > [role='button']:not(.notion-collection-item), +.notion-collection_view-block[data-simpler-db-tweaks]:not([data-simpler-db-tweaks*='[new_item]']) + .notion-gallery-view + > .notion-collection_view-block + [style*='grid'] + > [role='button'] { + display: none !important; +} +.notion-collection_view-block[data-simpler-db-tweaks]:not([data-simpler-db-tweaks*='[new_item]']) + .notion-timeline-view + > [style*='padding-bottom: 34px;'] { + padding-bottom: 0 !important; +} + +/* Calc Row - table, timeline */ + +.notion-collection_view-block[data-simpler-db-tweaks]:not([data-simpler-db-tweaks*='[calc_row]']) + .notion-table-view-add-row + ~ div:not(.notion-selectable-halo):not([role='button']), +.notion-collection_view-block[data-simpler-db-tweaks]:not([data-simpler-db-tweaks*='[calc_row]']) + .notion-timeline-view + > [style*='z-index: 4;']:last-child { + display: none !important; +} + +/* Hidden Columns - board */ + +.notion-collection_view-block[data-simpler-db-tweaks]:not([data-simpler-db-tweaks*='[hidden_column]']) + .notion-board-view + > .notion-collection_view-block + [style*='width: 220px;'] { + display: none !important; +} + +/* Add Group - board */ + +.notion-collection_view-block[data-simpler-db-tweaks]:not([data-simpler-db-tweaks*='[add_group]']) + .notion-board-view + > .notion-collection_view-block + [style*='width: 180px;'] { + display: none !important; +} + +/* New Column - table */ + +.notion-collection_view-block[data-simpler-db-tweaks]:not([data-simpler-db-tweaks*='[new_column]']) + .notion-table-view-add-column, +.notion-collection_view-block[data-simpler-db-tweaks]:not([data-simpler-db-tweaks*='[new_column]']) + .notion-table-view + .notion-collection-item + > [style*='width: 32px;'] { + display: none !important; +} +.notion-collection_view-block[data-simpler-db-tweaks]:not([data-simpler-db-tweaks*='[new_column]']) + .notion-table-view-add-row + + [style*='padding-right: 32px;'] { + padding-right: 0 !important; +} + +/* Full Width - table */ + +.notion-collection_view-block[data-simpler-db-tweaks]:not([data-simpler-db-tweaks*='[full_width]']) + .notion-table-view + > .notion-collection_view-block { + max-width: fit-content; +} +.notion-collection_view-block[data-simpler-db-tweaks]:not([data-simpler-db-tweaks*='[full_width]']) + .notion-table-view + .notion-collection_view-block + > [style*='min-width'] { + min-width: 0 !important; +} +.notion-collection_view-block[data-simpler-db-tweaks]:not([data-simpler-db-tweaks*='[full_width]']) + .notion-table-view + .notion-collection-item { + width: fit-content; +} +.notion-collection_view-block[data-simpler-db-tweaks]:not([data-simpler-db-tweaks*='[full_width]']) + .notion-table-view + .notion-collection_view-block + > [style*='height: 34px'] + + div, +.notion-collection_view-block[data-simpler-db-tweaks]:not([data-simpler-db-tweaks*='[full_width]']) + .notion-table-view + .notion-collection_view-block + > :first-child { + border-left: 1px solid var(--theme--ui_divider); +} + +/* COMPOUND TWEAKS */ + +/* Title and Link disabled > Hide title container */ +.notion-collection_view-block[data-simpler-db-tweaks]:not([data-simpler-db-tweaks*='[icon]']):not([data-simpler-db-tweaks*='[title]']) + [style*=' height: 42px;'] + > [style*='white-space: nowrap; overflow: hidden;'] { + display: none !important; +} + +/* New Row and Calc Row disabled > Add bottom border - table */ +.notion-collection_view-block[data-simpler-db-tweaks]:not([data-simpler-db-tweaks*='[calc_row]']):not([data-simpler-db-tweaks*='[new_item]']) + .notion-table-view + .notion-collection_view-block + > [style*='height: 34px'] + + div { + border-bottom: 1px solid var(--theme--ui_divider); +} + +/* New Column enabled with Full Width disabled > Add right border - table */ +.notion-collection_view-block[data-simpler-db-tweaks][data-simpler-db-tweaks*='[new_column]']:not([data-simpler-db-tweaks*='[full_width]']) + .notion-table-view + .notion-collection_view-block + > [style*='height: 34px'] + + div, +.notion-collection_view-block[data-simpler-db-tweaks][data-simpler-db-tweaks*='[new_column]']:not([data-simpler-db-tweaks*='[full_width]']) + .notion-table-view + .notion-collection_view-block + > :first-child { + border-right: 1px solid var(--theme--ui_divider); +} + +/* REMOVE DATABASE HEADER < Title, Link, Toggle, Views, and Toolbar disabled */ + +/* Hide Header */ +.notion-collection_view-block[data-simpler-db-tweaks]:not([data-simpler-db-tweaks*='[icon]']):not([data-simpler-db-tweaks*='[title]']):not([data-simpler-db-tweaks*='[toggle]']):not([data-simpler-db-tweaks*='[views']):not([data-simpler-db-tweaks*='[toolbar]']) + [style*='min-height: 42px'] { + min-height: 0 !important; + max-height: 0; + pointer-events: none; +} +.notion-collection_view-block[data-simpler-db-tweaks]:not([data-simpler-db-tweaks*='[icon]']):not([data-simpler-db-tweaks*='[title]']):not([data-simpler-db-tweaks*='[toggle]']):not([data-simpler-db-tweaks*='[views']):not([data-simpler-db-tweaks*='[toolbar]']) + [style*='height: 42px'] { + overflow: visible !important; +} + +/* Hide Top Border */ +.notion-collection_view-block[data-simpler-db-tweaks]:not([data-simpler-db-tweaks*='[icon]']):not([data-simpler-db-tweaks*='[title]']):not([data-simpler-db-tweaks*='[toggle]']):not([data-simpler-db-tweaks*='[views']):not([data-simpler-db-tweaks*='[toolbar]']) + :not(.notion-table-view) + > .notion-collection_view-block + > [style*='box-shadow'] { + box-shadow: none !important; +} +.notion-collection_view-block[data-simpler-db-tweaks]:not([data-simpler-db-tweaks*='[icon]']):not([data-simpler-db-tweaks*='[title]']):not([data-simpler-db-tweaks*='[toggle]']):not([data-simpler-db-tweaks*='[views']):not([data-simpler-db-tweaks*='[toolbar]']) + :not(.notion-table-view) + > .notion-collection_view-block[style*='border-top'], +.notion-collection_view-block[data-simpler-db-tweaks]:not([data-simpler-db-tweaks*='[icon]']):not([data-simpler-db-tweaks*='[title]']):not([data-simpler-db-tweaks*='[toggle]']):not([data-simpler-db-tweaks*='[views']):not([data-simpler-db-tweaks*='[toolbar]']) + :not(.notion-table-view) + > .notion-collection_view-block + > [style*='border-top'] { + border-top: none !important; +} diff --git a/repo/simpler-databases/client.mjs b/repo/simpler-databases/client.mjs new file mode 100644 index 0000000..5d13ad3 --- /dev/null +++ b/repo/simpler-databases/client.mjs @@ -0,0 +1,452 @@ +/* + * notion-enhancer: simpler databases + * (c) 2020 CloudHill (https://github.com/CloudHill) + * (c) 2021 dragonwocky (https://dragonwocky.me/) + * (https://notion-enhancer.github.io/) under the MIT license + */ + +export default async function ({ web, components }, db) { + const collectionViewSelector = + '.notion-collection_view-block[style*="width"][style*="max-width"]', + collectionAddNewSelector = '.notion-collection-view-item-add', + collectionToolbarSelector = '[style*=" height: 42px"]', + linkedCollectionTitleSelector = `${collectionToolbarSelector} > a [placeholder]`, + viewContainerSelector = '.notion-scroller [class$="view"]', + configButtonClass = 'simpler_databases--config_button', + configButtonSvg = web.raw` + + `, + overlayContainerClass = 'simpler_databases--overlay_container', + configMenuClass = 'simpler_databases--config_menu', + configDividerClass = 'simpler_databases--config_divider', + configItemClass = 'simpler_databases--config_item', + configTitleClass = 'simpler_databases--config_title', + configToggleClass = 'simpler_databases--config_toggle', + configInputClassName = 'simpler_databases--config_input notion-focusable', + configOpenCollectionSelector = + '.notion-collection_view-block[data-simpler-db-tweaks*="[config-open]"]', + collectionToggleClass = 'simpler_databases--toggle', + notionAppSelector = '.notion-app-inner'; + + const replaceTitle = ($collection, state) => { + const $title = $collection.querySelector(linkedCollectionTitleSelector), + blockId = $collection.dataset.blockId; + if (!$title) return; + if (!$title.dataset.originalTitle && state) { + $title.dataset.originalTitle = $title.innerText; + } + + if (!$title.titleObserver) { + if (!state) return; + $title.titleObserver = new MutationObserver(async () => { + const customTitle = await db.get(['collections', blockId, 'replace_title'], false); + if (customTitle && $title.innerText !== customTitle) $title.innerText = customTitle; + }); + } else $title.titleObserver.disconnect(); + + if (state) { + // observe + $title.innerText = state; + $title.titleObserver.observe($title, { characterData: true, childList: true }); + } else { + // reset + $title.titleObserver.disconnect(); + $title.innerText = $title.dataset.originalTitle; + delete $title.dataset.originalTitle; + } + }, + insertToggle = async ($collection, state) => { + const datasetKey = 'simplerDbToggleHidden', + blockId = $collection.dataset.blockId, + $toolbar = $collection.querySelector(collectionToolbarSelector); + if (!$toolbar) return; + + const $collectionView = $collection.querySelector('.notion-scroller'), + hideCollection = () => { + $collectionView.style.height = $collectionView.offsetHeight + 'px'; + requestAnimationFrame(() => { + $collection.dataset[datasetKey] = true; + setTimeout(() => ($collectionView.dataset.simplerDbHideItems = 'true'), 200); // hide drag handles + }); + }, + showCollection = () => { + $collection.dataset[datasetKey] = false; + $collectionView.style.height = ''; + $collectionView.style.height = $collectionView.offsetHeight + 'px'; + $collection.dataset[datasetKey] = true; + + delete $collectionView.dataset.simplerDbHideItems; + requestAnimationFrame(() => { + $collection.dataset[datasetKey] = false; + setTimeout(() => ($collectionView.style.height = ''), 200); + }); + }; + + if (!$collection.dataset[datasetKey]) { + const storedState = await db.get(['collections', blockId, 'toggle_hidden'], false); + if (storedState) { + hideCollection(); + } + } + + let $toggle = $toolbar.querySelector(`.${collectionToggleClass}`); + if ($toggle) { + if (!state) $toggle.remove(); + return; + } else if (state) { + $toggle = web.html` +
+ +
+ `; + $toggle.addEventListener('click', async () => { + const hide = !($collection.dataset[datasetKey] === 'true'); + await db.set(['collections', blockId, 'toggle_hidden'], hide); + if (hide) { + hideCollection(); + } else showCollection(); + }); + $toolbar.prepend($toggle); + } + }; + + const menuItems = [ + { + key: 'replace_title', + name: 'Replace title...', + type: 'input', + linkedOnly: true, + default: '', + action: replaceTitle, + }, + { + key: 'icon', + name: 'Icon', + type: 'toggle', + default: true, + }, + { + key: 'title', + name: 'Title', + type: 'toggle', + default: true, + }, + { + key: 'toggle', + name: 'Toggle', + type: 'toggle', + default: false, + action: insertToggle, + }, + { + key: 'views', + name: 'Views', + type: 'toggle', + default: true, + }, + { + key: 'toolbar', + name: 'Toolbar', + type: 'toggle', + default: true, + }, + { + key: 'divider', + views: ['table', 'board', 'timeline', 'list', 'gallery'], + }, + { + key: 'header_row', + name: 'Header row', + type: 'toggle', + default: true, + views: ['table'], + }, + { + key: 'new_item', + name: 'New row', + type: 'toggle', + default: true, + views: ['table', 'timeline'], + }, + { + key: 'new_item', + name: 'New item', + type: 'toggle', + default: true, + views: ['board', 'list', 'gallery'], + }, + { + key: 'calc_row', + name: 'Calculation row', + type: 'toggle', + default: true, + views: ['table', 'timeline'], + }, + { + key: 'divider', + views: ['table', 'board'], + }, + { + key: 'hidden_column', + name: 'Hidden columns', + type: 'toggle', + default: true, + views: ['board'], + }, + { + key: 'add_group', + name: 'Add group', + type: 'toggle', + default: true, + views: ['board'], + }, + { + key: 'new_column', + name: 'New column', + type: 'toggle', + default: true, + views: ['table'], + }, + { + key: 'full_width', + name: 'Full width', + type: 'toggle', + default: true, + views: ['table'], + }, + ]; + + const isLinked = ($collection) => !!$collection.querySelector(linkedCollectionTitleSelector), + getViewType = ($collection) => + $collection.querySelector(viewContainerSelector)?.className.split('-')[1], + setTweakState = ($collection, key, state) => { + const datasetKey = 'simplerDbTweaks'; + if (!$collection.dataset[datasetKey]) $collection.dataset[datasetKey] = ''; + + key = web.escape(key); + const isActive = $collection.dataset[datasetKey].includes(`[${key}]`); + + if (state && !isActive) { + $collection.dataset[datasetKey] += `[${key}]`; + } else if (!state && isActive) { + const prev = $collection.dataset[datasetKey]; + $collection.dataset[datasetKey] = prev.replace(`[${key}]`, ''); + } + }; + + const clickItem = (event) => { + event.stopPropagation(); + const focusedItem = event.target.closest(`[class^="${configItemClass}"]`); + if (focusedItem) focusedItem.click(); + }, + focusNextItem = (event) => { + event.stopPropagation(); + event.preventDefault(); + const $focusedItem = event.target.closest(`[class^="${configItemClass}"]`); + if (!$focusedItem) return; + let $targetItem = $focusedItem.nextElementSibling; + if (!$targetItem) $targetItem = $focusedItem.parentElement.firstElementChild; + if ($targetItem.classList.contains(configDividerClass)) { + $targetItem = $targetItem.nextElementSibling; + } + const $input = $targetItem.querySelector('input'); + if ($input) { + $input.focus(); + } else $targetItem.focus(); + }, + focusPrevItem = (event) => { + event.stopPropagation(); + event.preventDefault(); + const $focusedItem = event.target.closest(`[class^="${configItemClass}"]`); + if (!$focusedItem) return; + let $targetItem = $focusedItem.previousElementSibling; + if (!$targetItem) $targetItem = $focusedItem.parentElement.lastElementChild; + if ($targetItem.classList.contains(configDividerClass)) { + $targetItem = $targetItem.previousElementSibling; + } + const $input = $targetItem.querySelector('input'); + if ($input) { + $input.focus(); + } else $targetItem.focus(); + }, + keyListeners = [ + { + keys: ['Escape'], + listener: (event) => { + event.stopPropagation(); + hideConfig(); + }, + opts: { listenInInput: true, keydown: true }, + }, + { + keys: [' '], + listener: (event) => clickItem(event), + opts: { keydown: true }, + }, + { + keys: ['Enter'], + listener: (event) => clickItem(event), + opts: { keydown: true }, + }, + { + keys: ['ArrowDown'], + listener: focusNextItem, + opts: { listenInInput: true, keydown: true }, + }, + { + keys: ['ArrowUp'], + listener: focusPrevItem, + opts: { listenInInput: true, keydown: true }, + }, + { + keys: ['Tab'], + listener: focusNextItem, + opts: { listenInInput: true, keydown: true }, + }, + { + keys: ['Shift', 'Tab'], + listener: focusPrevItem, + opts: { listenInInput: true, keydown: true }, + }, + ]; + + const renderConfigItem = async ($collection, menuItem) => { + if (menuItem.key === 'divider') + return web.html`
`; + + const blockId = $collection.dataset.blockId, + storedState = await db.get(['collections', blockId, menuItem.key], menuItem.default), + $item = web.html`
`; + + switch (menuItem.type) { + case 'toggle': + const $label = web.html`
${menuItem.name}
`, + $toggle = web.html`
`; + web.render($item, $label, $toggle); + $item.setAttribute('tabindex', 0); + $item.addEventListener('click', async (e) => { + e.stopPropagation(); + const newState = !($toggle.dataset.toggled === 'true'); + $toggle.dataset.toggled = newState; + await db.set(['collections', blockId, menuItem.key], newState); + setTweakState($collection, menuItem.key, newState); + if (menuItem.action) menuItem.action($collection, newState); + }); + break; + + case 'input': + const $input = web.html`
+ +
`; + web.render($item, $input); + $item.addEventListener('click', (e) => e.stopPropagation()); + if (menuItem.action) { + $input.firstElementChild.addEventListener('input', async (e) => { + e.stopPropagation(); + const newState = e.target.value; + await db.set(['collections', blockId, menuItem.key], newState); + menuItem.action($collection, newState); + }); + } + break; + } + return $item; + }, + renderConfig = async ($collection, $button) => { + if (document.querySelector(`.${overlayContainerClass}`)) return; + + const collectionViewType = getViewType($collection); + if (!collectionViewType) return; + + const $overlay = web.html`
`; + $overlay.addEventListener('click', hideConfig); + web.render(document.querySelector(notionAppSelector), $overlay); + + const $config = web.html`
`, + viewMenuItems = menuItems.filter( + (item) => + (!item.views || item.views.includes(collectionViewType)) && + (!item.linkedOnly || isLinked($collection)) + ), + $menuItemElements = await Promise.all( + viewMenuItems.map((item) => renderConfigItem($collection, item)) + ); + web.render($config, ...$menuItemElements); + const $firstMenuItem = + $config.firstElementChild.getElementsByTagName('input')[0] || + $config.firstElementChild; + + const $position = web.html` +
+
+
+ `; + $position.firstElementChild.appendChild($config); + web.render($overlay, $position); + + const rect = $button.getBoundingClientRect(); + $position.style.left = + Math.min(rect.left + rect.width / 2, window.innerWidth - ($config.offsetWidth + 14)) + + 'px'; + $position.style.top = + Math.min( + rect.top + rect.height / 2, + window.innerHeight - ($config.offsetHeight + 14) + ) + 'px'; + + setTweakState($collection, 'config-open', true); + for (const { keys, listener, opts } of keyListeners) { + web.addHotkeyListener(keys, listener, opts); + } + await $config.animate([{ opacity: 0 }, { opacity: 1 }], { duration: 200 }).finished; + $firstMenuItem.focus(); + }; + async function hideConfig() { + const $overlay = document.querySelector(`.${overlayContainerClass}`), + $collection = document.querySelector(configOpenCollectionSelector); + if (!$overlay) return; + + $overlay.removeEventListener('click', hideConfig); + for (const { listener } of keyListeners) web.removeHotkeyListener(listener); + + await document + .querySelector(`.${configMenuClass}`) + .animate([{ opacity: 1 }, { opacity: 0 }], { duration: 200 }).finished; + setTweakState($collection, 'config-open', false); + $overlay.remove(); + } + + const simplifyCollection = async () => { + for (const $collection of document.querySelectorAll(collectionViewSelector)) { + const blockId = $collection.dataset.blockId, + $addNew = $collection.querySelector(collectionAddNewSelector); + if ($collection.querySelector(`.${configButtonClass}`) || !$addNew) continue; + + const $configButton = $addNew.previousElementSibling.cloneNode(); + $configButton.className = configButtonClass; + $configButton.innerHTML = configButtonSvg; + $configButton.addEventListener('click', () => { + renderConfig($collection, $configButton); + }); + $addNew.parentElement.prepend($configButton); + + for (const item of menuItems) { + if (item.key === 'divider') continue; + const state = await db.get(['collections', blockId, item.key], item.default); + if ((item.type !== 'input' && !item.linkedOnly) || isLinked($collection)) { + setTweakState($collection, item.key, state); + } + if (state && item.action) item.action($collection, state); + } + } + }; + web.addDocumentObserver(simplifyCollection, [collectionViewSelector]); +} diff --git a/repo/simpler-databases/mod.json b/repo/simpler-databases/mod.json new file mode 100644 index 0000000..966f20b --- /dev/null +++ b/repo/simpler-databases/mod.json @@ -0,0 +1,22 @@ +{ + "name": "simpler databases", + "id": "752933b5-1258-44e3-b49a-61b4885f8bda", + "version": "0.2.0", + "description": "adds a menu to inline databases to toggle ui elements.", + "tags": ["extension", "layout"], + "authors": [ + { + "name": "CloudHill", + "email": "rh.cloudhill@gmail.com", + "homepage": "https://github.com/CloudHill", + "avatar": "https://avatars.githubusercontent.com/u/54142180" + } + ], + "js": { + "client": ["client.mjs"] + }, + "css": { + "client": ["client.css"] + }, + "options": [] +} diff --git a/repo/tweaks/client.css b/repo/tweaks/client.css index 504e3a2..27c2677 100644 --- a/repo/tweaks/client.css +++ b/repo/tweaks/client.css @@ -28,9 +28,9 @@ .enhancer--tweak-normalise_table_scroll .notion-page-template-modal .notion-collection_view-block { - width: calc(100% + 8px) !important; - padding-left: 4px; - padding-right: 4px; + width: 100% !important; + padding-left: 0; + padding-right: 0; } .enhancer--tweak-normalise_table_scroll .notion-collection_view-block