extension: simpler databases

This commit is contained in:
dragonwocky 2021-10-23 23:11:52 +11:00
parent 38484b7e42
commit 047b4cce7d
10 changed files with 921 additions and 96 deletions

View File

@ -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';

View File

@ -1,86 +0,0 @@
/*
* collapsible headers
* (c) 2020 dragonwocky <thedragonring.bod@gmail.com> (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;
}

View File

@ -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);

View File

@ -87,5 +87,5 @@ export default async function ({ web }, db) {
}
}
document.head.append(web.html`<style>${css}</style>`);
web.render(document.head, web.html`<style>${css}</style>`);
}

View File

@ -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);

View File

@ -22,6 +22,7 @@
"scroll-to-top",
"indentation-lines",
"right-to-left",
"simpler-databases",
"emoji-sets",
"bypass-preview",
"topbar-icons",

View File

@ -0,0 +1,429 @@
/*
* notion-enhancer: simpler databases
* (c) 2020 CloudHill <rl.cloudhill@gmail.com> (https://github.com/CloudHill)
* (c) 2021 dragonwocky <thedragonring.bod@gmail.com> (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;
}

View File

@ -0,0 +1,452 @@
/*
* notion-enhancer: simpler databases
* (c) 2020 CloudHill <rl.cloudhill@gmail.com> (https://github.com/CloudHill)
* (c) 2021 dragonwocky <thedragonring.bod@gmail.com> (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`<svg viewBox="0 0 14 14">
<path d="M14,7.77 L14,6.17 L12.06,5.53 L11.61,4.44 L12.49,2.6 L11.36,1.47
L9.55,2.38 L8.46,1.93 L7.77,0.01 L6.17,0.01 L5.54,1.95 L4.43,2.4 L2.59,1.52
L1.46,2.65 L2.37,4.46 L1.92,5.55 L0,6.23 L0,7.82 L1.94,8.46 L2.39,9.55
L1.51,11.39 L2.64,12.52 L4.45,11.61 L5.54,12.06 L6.23,13.98 L7.82,13.98
L8.45,12.04 L9.56,11.59 L11.4,12.47 L12.53,11.34 L11.61,9.53 L12.08,8.44
L14,7.75 L14,7.77 Z M7,10 C5.34,10 4,8.66 4,7 C4,5.34 5.34,4 7,4 C8.66,4
10,5.34 10,7 C10,8.66 8.66,10 7,10 Z" />
</svg>`,
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`
<div class="${collectionToggleClass}">
<svg viewBox="0 0 100 100"><polygon points="5.9,88.2 50,11.8 94.1,88.2" /></svg>
</div>
`;
$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`<div class="${configDividerClass}"></div>`;
const blockId = $collection.dataset.blockId,
storedState = await db.get(['collections', blockId, menuItem.key], menuItem.default),
$item = web.html`<div class="${configItemClass}-${menuItem.type}"></div>`;
switch (menuItem.type) {
case 'toggle':
const $label = web.html`<div class="${configTitleClass}">${menuItem.name}</div>`,
$toggle = web.html`<div class="${configToggleClass}"
data-toggled="${storedState || false}"></div>`;
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`<div class="${configInputClassName}">
<input placeholder="${menuItem.name}" type="text"
value="${web.escape(storedState) || ''}">
</div>`;
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`<div class="${overlayContainerClass}"></div>`;
$overlay.addEventListener('click', hideConfig);
web.render(document.querySelector(notionAppSelector), $overlay);
const $config = web.html`<div class="${configMenuClass}"></div>`,
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`
<div style="position: fixed;">
<div style="position: relative; pointer-events: auto;"></div>
</div>
`;
$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]);
}

View File

@ -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": []
}

View File

@ -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