diff --git a/mods/admiraldus-global-linking-blocks/app.css b/mods/admiraldus-global-linking-blocks/app.css new file mode 100644 index 0000000..6fd9857 --- /dev/null +++ b/mods/admiraldus-global-linking-blocks/app.css @@ -0,0 +1,101 @@ +/* + * global linking blocks + * (c) 2020 admiraldus (https://github.com/admiraldus) + * under the MIT license + */ + +/* ========== THE PAGE BUTTON ========== */ +.admiraldus-glb-page-button +{ + display: inline-flex; + align-items: center; + flex-shrink: 0; + border-radius: 3px; + height: 28px; + min-width: 0px; + padding-right: 8px; + padding-left: 6px; + white-space: nowrap; + font-size: 14px; + line-height: 1.2; + color: var(--theme--text); + cursor: pointer; + transition: background 20ms ease-in 0s; + user-select: none; +} + +.admiraldus-glb-page-button:hover +{ + background: var(--theme--interactive_hover); + box-shadow: 0 0 0 0.5px var(--theme--interactive_hover-border); +} + +.admiraldus-glb-page-button > svg +{ + backface-visibility: hidden; + display: block; + flex-shrink: 0; + margin-right: 6px; + height: 14px; + width: 14px; + fill: var(--theme--text); +} + +.admiraldus-glb-page-button > span { + opacity: 1; + transition: opacity .4s ease; +} + +.admiraldus-glb-span-hide { + position: absolute; + top: -9999px; + opacity: 0 !important; +} + +/* ========== THE BLOCK BUTTON ========== */ +.admiraldus-glb-block-button { + display: flex; + align-items: center; + min-height: 28px; + width: 100%; + font-size: var(--theme--font_label-size); + line-height: 120%; + cursor: pointer; + transition: background 20ms ease-in 0s; + user-select: none; +} + +.admiraldus-glb-block-button:hover +{ + background: var(--theme--interactive_hover); + box-shadow: 0 0 0 0.5px var(--theme--interactive_hover-border); +} + +.admiraldus-glb-block-button > svg { + backface-visibility: hidden; + display: flex; + display: block; + justify-content: center; + align-items: center; + flex-shrink: 0; + margin-left: 14px; + height: 17px; + width: 17px; + fill: inherit; +} + +.admiraldus-glb-block-button > span { + margin-right: 14px; + margin-left: 8px; + min-width: 0px; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + flex: 1 1 auto; +} + +/* ========== THE MENU ========== */ +.--on-hover div[role="button"]:not(.admiraldus-glb-block-button) { + background: transparent !important; + box-shadow: none !important; +} diff --git a/mods/admiraldus-global-linking-blocks/helper.js b/mods/admiraldus-global-linking-blocks/helper.js new file mode 100644 index 0000000..02b6c61 --- /dev/null +++ b/mods/admiraldus-global-linking-blocks/helper.js @@ -0,0 +1,82 @@ +/* + * helper.js from admiraldus + * (c) 2020 admiraldus (https://github.com/admiraldus) + * use for your own modules but you have to attribute to me. + * under the MIT license + */ + +'use strict'; + +const PATH = require('path'); +const FS = require('fs-extra'); + +var x$ = { + sel: function(els, mode = false, base = null) { + base = base === null ? document : base; + return mode ? base.querySelectorAll(els) : base.querySelector(els); + }, + + cls: { + r: function(els, cls, mode = false, base = null) { + base = base === null ? document : base; + mode ? x$.sel(els, true).forEach((el) => + el.classList.remove(cls)) : els.classList.remove(cls); + }, + + a: function(els, cls, mode = false, base = null) { + base = base === null ? document : base; + mode ? x$.sel(els, true).forEach((el) => + el.classList.add(cls)) : els.classList.add(cls); + }, + + c: function(els, cls, mode = false, base = null) { + base = base === null ? document : base; + return mode ? x$.sel(els, true).forEach((el) => + el.classList.contains(cls)) : els.classList.contains(cls); + }, + }, + + svg: function(path) { + return FS.readFile(PATH.resolve(__dirname + path)); + }, + + on: function(base, event, fn, flag = false) { + base.addEventListener(event, fn, flag); + }, + + sim: function(events, els) { + events.forEach((event) => els.dispatchEvent( + new MouseEvent(event, { + view: window, + bubbles: true, + cancelable: true, + buttons: 1, + }))); + }, + + obs: function(fn, els, config) { + const observer = new MutationObserver(fn); + observer.observe(els, config); + }, + + clp: function(mode = true, value) { + switch (mode) { + case false: + navigator.clipboard.writeText(value); + break; + case true: + return navigator.clipboard.readText(); + break; + } + }, + + el: function(html) { + const temp = document.createElement('template'); + temp.innerHTML = html.trim(); + return temp.content.firstElementChild; + }, +}; + +module.exports = { + x$, +}; diff --git a/mods/admiraldus-global-linking-blocks/icons/link.svg b/mods/admiraldus-global-linking-blocks/icons/link.svg new file mode 100644 index 0000000..b88b775 --- /dev/null +++ b/mods/admiraldus-global-linking-blocks/icons/link.svg @@ -0,0 +1,58 @@ +<?xml version="1.0" encoding="iso-8859-1"?> +<!-- Generator: Adobe Illustrator 19.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) --> +<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" + viewBox="0 0 512 512" style="enable-background:new 0 0 512 512;" xml:space="preserve"> +<g> + <g> + <path d="M256,0C114.516,0,0,114.497,0,256c0,141.483,114.497,256,256,256c141.484,0,256-114.497,256-256 + C512,114.517,397.503,0,256,0z M179.672,43.154c-18.976,24.15-32.411,54.72-41.322,84.252 + c-14.826-9.037-28.655-19.814-41.172-32.171C120.97,71.723,149.058,54.11,179.672,43.154z M30,256 + c0-50.725,16.604-98.895,47.232-138.315c16.079,15.678,34.043,29.063,53.365,39.92C123.981,188.356,120.5,221.677,120.5,256 + s3.481,67.644,10.097,98.395c-19.322,10.857-37.286,24.242-53.365,39.92C46.604,354.895,30,306.725,30,256z M97.177,416.765 + c12.517-12.357,26.346-23.134,41.172-32.171c8.908,29.518,22.341,60.094,41.322,84.252 + C149.057,457.891,120.969,440.278,97.177,416.765z M241,479.476c-39.328-13.125-64.166-68.866-75.544-108.983 + c23.765-10.401,49.312-16.72,75.544-18.472V479.476z M241,256v65.957c-28.585,1.685-56.481,8.155-82.582,18.927 + c-5.195-26.628-7.918-55.305-7.918-84.884s2.723-58.256,7.918-84.884c26.1,10.772,53.996,17.242,82.582,18.927V256z M241,159.979 + c-26.232-1.753-51.779-8.071-75.544-18.472c11.347-40.006,36.17-95.843,75.544-108.983V159.979z M414.823,95.235 + c-12.517,12.357-26.346,23.134-41.172,32.171c-8.908-29.517-22.341-60.094-41.322-84.252 + C362.943,54.109,391.031,71.722,414.823,95.235z M271,32.524c39.328,13.125,64.166,68.866,75.544,108.983 + c-23.765,10.401-49.312,16.72-75.544,18.472V32.524z M271,256v-65.957c28.585-1.685,56.481-8.155,82.582-18.927 + c5.195,26.628,7.918,55.305,7.918,84.884s-2.723,58.256-7.918,84.884c-26.1-10.772-53.996-17.242-82.582-18.927V256z M271,479.476 + V352.021c26.232,1.753,51.779,8.071,75.544,18.472C335.197,410.499,310.374,466.336,271,479.476z M332.329,468.846 + c18.974-24.15,32.41-54.72,41.322-84.252c14.826,9.037,28.656,19.814,41.172,32.171 + C391.031,440.278,362.943,457.891,332.329,468.846z M434.769,394.314c-16.079-15.678-34.043-29.063-53.366-39.92 + c6.616-30.75,10.097-64.071,10.097-98.395c0-34.324-3.481-67.644-10.097-98.395c19.322-10.856,37.286-24.241,53.366-39.92 + C465.396,157.105,482,205.275,482,256C482,306.725,465.396,354.895,434.769,394.314z"/> + </g> +</g> +<g> +</g> +<g> +</g> +<g> +</g> +<g> +</g> +<g> +</g> +<g> +</g> +<g> +</g> +<g> +</g> +<g> +</g> +<g> +</g> +<g> +</g> +<g> +</g> +<g> +</g> +<g> +</g> +<g> +</g> +</svg> diff --git a/mods/admiraldus-global-linking-blocks/mod.js b/mods/admiraldus-global-linking-blocks/mod.js new file mode 100644 index 0000000..40d57b1 --- /dev/null +++ b/mods/admiraldus-global-linking-blocks/mod.js @@ -0,0 +1,213 @@ +/* + * global linking blocks + * (c) 2020 admiraldus (https://github.com/admiraldus) + * under the MIT license + */ + +'use strict'; + +const {x$} = require('./helper.js'); + +module.exports = { + id: '74856af4-6970-455d-bd86-0a385a402dd1', + name: 'global linking blocks', + tags: ['extension'], + desc: 'easily copy the global link of the page or the desired block.', + version: '0.1.0', + author: { + name: 'admiraldus', + link: 'https://github.com/admiraldus', + avatar: 'https://raw.githubusercontent.com/admiraldus/admiraldus/main/module.gif', + }, + hacks: { + 'renderer/preload.js'(store, __exports) { + document.addEventListener('readystatechange', () => { + if (document.readyState !== 'complete') return false; + + /** + * Prevent selectors from failing. + * + * @return {Function} Returns "wait()" until "main()" returns. + */ + const wait = !function wait() { + const els = [x$.sel('.notion-frame'), x$.sel('.notion-topbar')]; + if (els.some((el) => el !== null)) return main(); + setTimeout(() => wait(), 500); + }(); + + /** + * Everything happens here. ¯\_(ツ)_/¯ + */ + async function main() { + const icon = await x$.svg('/icons/link.svg'); + const pageClass = 'admiraldus-glb-page-button'; + const blockClass = 'admiraldus-glb-block-button'; + const spanClass = 'admiraldus-glb-span-hide'; + /** + * Create the page link button and append it to the topbar. + * + * @return {create} Returns "create()" if not appended. + */ + const pageButton = !function create() { + const target = x$.sel('.notion-topbar-share-menu'); + const attr = [ + `class="${pageClass}" role="button" tabindex="0"`, + `class="${spanClass}"`, + ]; + const html = x$.el( + `<div ${attr[0]}> + ${icon} + <span>Global Link</span> + <span ${attr[1]}>Link copied!</span + </div>`); + + target.before(html); + if (html === null) return create(); + }(); + + /** + * Create the block link button and append it to the block menu. + * + * @param {HTMLDivElement} el The copy link button. + * + * @return {create} Returns "create()" if not appended. + */ + const blockButton = function create(el) { + const target = el; + const attr = `class="${blockClass}" role="button" tabindex="0"`; + const html = x$.el( + `<div ${attr}> + ${icon} + <span>Global link</span> + </div>`); + + target.before(html); + if (html === null) return create(); + }; + + let buttonDelay; + let link; + /** + * Copy the link to the clipboard. + * + * @param {boolean} page If the link is the link of the page. + */ + function copyLink(page) { + /** + * Change the button text to provide the copied feedback. + */ + const changeButtonText = function create() { + const span = x$.sel('span', true, x$.sel(`.${pageClass}`)); + /** + * Set the classes of the button's div elements. + * + * @param {number} first A specific array items. + * @param {number} second A specific array items. + */ + function setClasses(first, second) { + x$.cls.a(span[first], spanClass); + x$.cls.r(span[second], spanClass); + } + + clearTimeout(buttonDelay); + setClasses(0, 1); + buttonDelay = setTimeout(() => { + setClasses(1, 0); + }, 700); + }; + + switch (page) { + case true: + link = `https://${window.location.href}/`.replace(/notion:\/\//, ''); + changeButtonText(); + x$.clp(false, link); + break; + case false: + const events = ['mousedown', 'mouseup', 'click']; + x$.sim(events, x$.sel(`.${blockClass}`).nextSibling); + x$.clp().then((text) => { + x$.clp(false, `${text.replace(/(?<=so[\/]).*#/, '')}/`); + }); + break; + } + } + + /** + * Observer for the overlay container. + */ + x$.obs(() => { + /** + * Get the copy link button. + * + * @return {HTMLDivElement} Returns the copy link button. + */ + function getLinkButton() { + const lang = ['Copy link', '링크 복사']; + const overlay = x$.sel('.notion-overlay-container'); + const record = x$.sel( + '[style*="text-overflow: ellipsis;"]', true, overlay); + + return Array.from(record).find( + (div) => lang.some((text) => div.textContent === text)); + } + if (x$.sel(`.${blockClass}`) !== null || + getLinkButton() === undefined) return; + blockButton(getLinkButton().closest('[role="button"]')); + }, x$.sel('.notion-overlay-container'), { + subtree: true, childList: true, + }); + + /** + * Listen for click events to call "copyLink()"". + * + * @type {HTMLElement} + * @listens document.body#click + */ + x$.on(document.body, 'click', (event) => { + const target = event.target; + + if (x$.cls.c(target, pageClass) || + target.closest(`.${pageClass}`)) { + copyLink(/* page= */ true); + } else if (x$.cls.c(target, blockClass) || + target.closest(`.${blockClass}`)) { + copyLink(/* page= */ false); + } + }); + + /** + * Listen for mouseenter events to add class. + * + * @type {HTMLElement} + * @listens document.body#mouseenter + */ + x$.on(document.body, 'mouseenter', (event) => { + const target = event.target; + + if (x$.cls.c(target, 'admiraldus-glb-block-button')) { + const menu = target.closest('.notion-scroller.vertical'); + + x$.cls.a(menu, '--on-hover'); + } + }, true); + + /** + * Listen for mouseleave events to remove class. + * + * @type {HTMLElement} + * @listens document.body#mouseleave + */ + x$.on(document.body, 'mouseleave', (event) => { + const target = event.target; + + if (x$.cls.c(target, 'admiraldus-glb-block-button')) { + const menu = target.closest('.notion-scroller.vertical'); + + x$.cls.r(menu, '--on-hover'); + } + }, true); + } + }); + }, + }, +};