mirror of
https://github.com/notion-enhancer/notion-enhancer.git
synced 2025-04-11 15:59:03 +00:00
refactor: move state.mjs into markup.mjs for use in notion interface
This commit is contained in:
parent
bb5853e866
commit
45e0be7d62
@ -8,6 +8,7 @@ import { checkForUpdate } from "./updateCheck.mjs";
|
||||
import { sendTelemetryPing } from "./sendTelemetry.mjs";
|
||||
import { Modal, Frame } from "./islands/Modal.mjs";
|
||||
import { MenuButton } from "./islands/MenuButton.mjs";
|
||||
import { TopbarButton } from "./islands/TopbarButton.mjs";
|
||||
import { Panel } from "./islands/Panel.mjs";
|
||||
|
||||
const shouldLoadThemeOverrides = async (db) => {
|
||||
@ -118,10 +119,11 @@ const insertMenu = async (db) => {
|
||||
});
|
||||
};
|
||||
|
||||
const insertPanel = async (db) => {
|
||||
const insertPanel = async (api, db) => {
|
||||
const notionFrame = ".notion-frame",
|
||||
{ html, addKeyListener, addMutationListener } = globalThis.__enhancerApi,
|
||||
togglePanelHotkey = await db.get("togglePanelHotkey");
|
||||
notionTopbarBtn = ".notion-topbar-more-button",
|
||||
togglePanelHotkey = await db.get("togglePanelHotkey"),
|
||||
{ html } = api;
|
||||
|
||||
const $panel = html`<${Panel}
|
||||
_getWidth=${() => db.get("sidePanelWidth")}
|
||||
@ -129,28 +131,45 @@ const insertPanel = async (db) => {
|
||||
_getOpen=${() => db.get("sidePanelOpen")}
|
||||
_setOpen=${(open) => db.set("sidePanelOpen", open)}
|
||||
/>`,
|
||||
togglePanel = () => {
|
||||
if ($panel.hasAttribute("open")) $panel.close();
|
||||
else $panel.open();
|
||||
};
|
||||
|
||||
const $panelTopbarBtn = html`<${TopbarButton}
|
||||
aria-label="Open side panel"
|
||||
icon="panel-right"
|
||||
onclick=${togglePanel}
|
||||
/>`,
|
||||
appendToDom = () => {
|
||||
const $frame = document.querySelector(notionFrame);
|
||||
if (!$frame) return;
|
||||
if (!$frame.contains($panel)) $frame.append($panel);
|
||||
if (!$frame.style.flexDirection !== "row")
|
||||
$frame.style.flexDirection = "row";
|
||||
if (!document.contains($panelTopbarBtn)) {
|
||||
const $notionTopbarBtn = document.querySelector(notionTopbarBtn);
|
||||
$notionTopbarBtn?.before($panelTopbarBtn);
|
||||
}
|
||||
};
|
||||
addMutationListener(notionFrame, appendToDom);
|
||||
api.addMutationListener(`${notionFrame}, ${notionTopbarBtn}`, appendToDom);
|
||||
api.useState(["panelOpen"], ([panelOpen]) => {
|
||||
if (panelOpen) $panelTopbarBtn.setAttribute("data-active", true);
|
||||
else $panelTopbarBtn.removeAttribute("data-active");
|
||||
});
|
||||
appendToDom();
|
||||
|
||||
addKeyListener(togglePanelHotkey, (event) => {
|
||||
api.addKeyListener(togglePanelHotkey, (event) => {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
if ($panel.hasAttribute("open")) $panel.close();
|
||||
else $panel.open();
|
||||
togglePanel();
|
||||
});
|
||||
};
|
||||
|
||||
export default async (api, db) =>
|
||||
Promise.all([
|
||||
insertMenu(db),
|
||||
insertPanel(db),
|
||||
insertPanel(api, db),
|
||||
insertCustomStyles(db),
|
||||
loadThemeOverrides(db),
|
||||
sendTelemetryPing(),
|
||||
|
@ -4,17 +4,6 @@
|
||||
* (https://notion-enhancer.github.io/) under the MIT license
|
||||
*/
|
||||
|
||||
// <div class="absolute top-0">
|
||||
// <svg
|
||||
// role="graphics-symbol"
|
||||
// viewBox="0 0 16 16"
|
||||
// class="doubleChevronRight"
|
||||
// style="width: 16px; height: 16px; display: block; fill: rgba(255, 255, 255, 0.443); flex-shrink: 0;"
|
||||
// >
|
||||
// <path d="M2.25781 14.1211C2.47656 14.1211 2.66797 14.0391 2.81836 13.8887L8.14355 8.67969C8.32812 8.49512 8.41699 8.29688 8.41699 8.06445C8.41699 7.8252 8.32812 7.62012 8.14355 7.44922L2.81836 2.24023C2.66797 2.08984 2.4834 2.00781 2.25781 2.00781C1.81348 2.00781 1.46484 2.35645 1.46484 2.80078C1.46484 3.0127 1.55371 3.21777 1.7041 3.375L6.50977 8.05762L1.7041 12.7539C1.55371 12.9043 1.46484 13.1094 1.46484 13.3281C1.46484 13.7725 1.81348 14.1211 2.25781 14.1211ZM8.36914 14.1211C8.58789 14.1211 8.77246 14.0391 8.92285 13.8887L14.2549 8.67969C14.4395 8.49512 14.5283 8.29688 14.5283 8.06445C14.5283 7.8252 14.4326 7.62012 14.2549 7.44922L8.92285 2.24023C8.77246 2.08984 8.58789 2.00781 8.36914 2.00781C7.9248 2.00781 7.56934 2.35645 7.56934 2.80078C7.56934 3.0127 7.66504 3.21777 7.81543 3.375L12.6211 8.05762L7.81543 12.7539C7.66504 12.9043 7.56934 13.1094 7.56934 13.3281C7.56934 13.7725 7.9248 14.1211 8.36914 14.1211Z"></path>
|
||||
// </svg>
|
||||
// </div>;
|
||||
|
||||
import { Tooltip } from "./Tooltip.mjs";
|
||||
|
||||
function PanelView(props) {
|
||||
@ -36,9 +25,8 @@ function Panel({
|
||||
maxWidth = 640,
|
||||
...props
|
||||
}) {
|
||||
const { html, extendProps } = globalThis.__enhancerApi,
|
||||
{ addMutationListener, removeMutationListener } = globalThis.__enhancerApi;
|
||||
extendProps(props, {
|
||||
const { html, ...api } = globalThis.__enhancerApi;
|
||||
api.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)]
|
||||
@ -65,8 +53,9 @@ function Panel({
|
||||
d="M2.25781 14.1211C2.47656 14.1211 2.66797 14.0391 2.81836 13.8887L8.14355 8.67969C8.32812 8.49512 8.41699 8.29688 8.41699 8.06445C8.41699 7.8252 8.32812 7.62012 8.14355 7.44922L2.81836 2.24023C2.66797 2.08984 2.4834 2.00781 2.25781 2.00781C1.81348 2.00781 1.46484 2.35645 1.46484 2.80078C1.46484 3.0127 1.55371 3.21777 1.7041 3.375L6.50977 8.05762L1.7041 12.7539C1.55371 12.9043 1.46484 13.1094 1.46484 13.3281C1.46484 13.7725 1.81348 14.1211 2.25781 14.1211ZM8.36914 14.1211C8.58789 14.1211 8.77246 14.0391 8.92285 13.8887L14.2549 8.67969C14.4395 8.49512 14.5283 8.29688 14.5283 8.06445C14.5283 7.8252 14.4326 7.62012 14.2549 7.44922L8.92285 2.24023C8.77246 2.08984 8.58789 2.00781 8.36914 2.00781C7.9248 2.00781 7.56934 2.35645 7.56934 2.80078C7.56934 3.0127 7.66504 3.21777 7.81543 3.375L12.6211 8.05762L7.81543 12.7539C7.66504 12.9043 7.56934 13.1094 7.56934 13.3281C7.56934 13.7725 7.9248 14.1211 8.36914 14.1211Z"
|
||||
></path>
|
||||
</svg>
|
||||
</div>`,
|
||||
$panel = html`<aside ...${props}>
|
||||
</div>`;
|
||||
|
||||
const $panel = html`<aside ...${props}>
|
||||
${$resizeHandle}
|
||||
<div
|
||||
class="flex justify-between items-center
|
||||
@ -139,33 +128,9 @@ function Panel({
|
||||
options = { duration: 150, easing: "cubic-bezier(0.4, 0, 0.2, 1)" };
|
||||
$notionHelp.style.setProperty("right", destination);
|
||||
$notionHelp.animate(keyframes, options);
|
||||
removeMutationListener(repositionHelp);
|
||||
api.removeMutationListener(repositionHelp);
|
||||
};
|
||||
addMutationListener(notionHelp, repositionHelp);
|
||||
|
||||
const notionTopbarMore = ".notion-topbar-more-button",
|
||||
$topbarPanelButton = html`<button
|
||||
aria-label="Open side panel"
|
||||
class="user-select-none h-[28px] w-[33px] duration-[20ms]
|
||||
transition inline-flex items-center justify-center mr-[2px]
|
||||
rounded-[3px] hover:bg-[color:var(--theme--bg-hover)]
|
||||
[data-active]:bg-[color:var(--theme--bg-hover)]"
|
||||
>
|
||||
<i
|
||||
class="i-panel-right w-[20px] h-[20px] fill-[color:var(--theme--fg-secondary)]"
|
||||
/>
|
||||
</button>`,
|
||||
appendToDom = () => {
|
||||
if (document.contains($topbarPanelButton)) return;
|
||||
const $notionTopbarMore = document.querySelector(notionTopbarMore);
|
||||
$notionTopbarMore?.before($topbarPanelButton);
|
||||
};
|
||||
$topbarPanelButton.addEventListener("click", () => {
|
||||
if ($panel.hasAttribute("open")) $panel.close();
|
||||
else $panel.open();
|
||||
});
|
||||
addMutationListener(notionTopbarMore, appendToDom);
|
||||
appendToDom();
|
||||
api.addMutationListener(notionHelp, repositionHelp);
|
||||
|
||||
$panel.resize = async (width) => {
|
||||
$tooltip.hide();
|
||||
@ -181,7 +146,7 @@ function Panel({
|
||||
$panel.open = () => {
|
||||
$panel.setAttribute("open", true);
|
||||
$panel.querySelectorAll("[tabindex]").forEach(($el) => ($el.tabIndex = 0));
|
||||
$topbarPanelButton.setAttribute("data-active", true);
|
||||
api.setState({ panelOpen: true });
|
||||
$panel.onopen?.();
|
||||
_setOpen(true);
|
||||
$panel.resize();
|
||||
@ -191,8 +156,8 @@ function Panel({
|
||||
$panel.onbeforeclose?.();
|
||||
$panel.removeAttribute("open");
|
||||
$panel.style.pointerEvents = "auto";
|
||||
$topbarPanelButton.removeAttribute("data-active");
|
||||
$panel.querySelectorAll("[tabindex]").forEach(($el) => ($el.tabIndex = -1));
|
||||
api.setState({ panelOpen: false });
|
||||
repositionHelp();
|
||||
_setOpen(false);
|
||||
setTimeout(() => {
|
||||
|
26
src/core/islands/TopbarButton.mjs
Normal file
26
src/core/islands/TopbarButton.mjs
Normal file
@ -0,0 +1,26 @@
|
||||
/**
|
||||
* notion-enhancer
|
||||
* (c) 2023 dragonwocky <thedragonring.bod@gmail.com> (https://dragonwocky.me/)
|
||||
* (https://notion-enhancer.github.io/) under the MIT license
|
||||
*/
|
||||
|
||||
function TopbarButton({ icon, ...props }, ...children) {
|
||||
const { html, extendProps } = globalThis.__enhancerApi;
|
||||
extendProps(props, {
|
||||
tabindex: 0,
|
||||
role: "button",
|
||||
class: `notion-enhancer--topbar-button
|
||||
user-select-none h-[28px] w-[33px] duration-[20ms]
|
||||
transition inline-flex items-center justify-center mr-[2px]
|
||||
rounded-[3px] hover:bg-[color:var(--theme--bg-hover)]
|
||||
[data-active]:bg-[color:var(--theme--bg-hover)]`,
|
||||
});
|
||||
return html`<button ...${props}>
|
||||
<i
|
||||
class="i-${icon} w-[20px] h-[20px]
|
||||
fill-[color:var(--theme--fg-secondary)]"
|
||||
/>
|
||||
</button>`;
|
||||
}
|
||||
|
||||
export { TopbarButton };
|
@ -7,7 +7,6 @@
|
||||
import { Popup } from "./Popup.mjs";
|
||||
import { Button } from "./Button.mjs";
|
||||
import { Description } from "./Description.mjs";
|
||||
import { useState } from "../state.mjs";
|
||||
|
||||
const updateGuide =
|
||||
"https://notion-enhancer.github.io/getting-started/updating/",
|
||||
@ -69,7 +68,7 @@ function Circle(rect) {
|
||||
}
|
||||
|
||||
function Banner({ updateAvailable, isDevelopmentBuild }) {
|
||||
const { html, version, initDatabase } = globalThis.__enhancerApi,
|
||||
const { html, version, initDatabase, useState } = globalThis.__enhancerApi,
|
||||
$version = html`<button
|
||||
class="text-[12px] py-[2px] px-[6px] mt-[2px]
|
||||
font-medium leading-tight tracking-wide rounded-[3px]
|
||||
|
@ -4,11 +4,10 @@
|
||||
* (https://notion-enhancer.github.io/) under the MIT license
|
||||
*/
|
||||
|
||||
import { setState, useState } from "../state.mjs";
|
||||
|
||||
function Checkbox({ _get, _set, _requireReload = true, ...props }) {
|
||||
let _initialValue;
|
||||
const { html, extendProps } = globalThis.__enhancerApi,
|
||||
{ setState, useState } = globalThis.__enhancerApi,
|
||||
$input = html`<input
|
||||
type="checkbox"
|
||||
class="hidden checked:sibling:(px-px
|
||||
|
@ -4,11 +4,11 @@
|
||||
* (https://notion-enhancer.github.io/) under the MIT license
|
||||
*/
|
||||
|
||||
import { setState, useState } from "../state.mjs";
|
||||
import { Button } from "./Button.mjs";
|
||||
|
||||
function Footer({ categories }) {
|
||||
const { html, reloadApp } = globalThis.__enhancerApi,
|
||||
{ setState, useState } = globalThis.__enhancerApi,
|
||||
$reload = html`<${Button}
|
||||
class="ml-auto"
|
||||
variant="primary"
|
||||
|
@ -4,8 +4,6 @@
|
||||
* (https://notion-enhancer.github.io/) under the MIT license
|
||||
*/
|
||||
|
||||
import { setState, useState } from "../state.mjs";
|
||||
|
||||
const updateHotkey = (event) => {
|
||||
const keys = [];
|
||||
for (const modifier of ["metaKey", "ctrlKey", "altKey", "shiftKey"]) {
|
||||
@ -76,7 +74,8 @@ function Input({
|
||||
...props
|
||||
}) {
|
||||
let $filename, $clear;
|
||||
const { html, extendProps } = globalThis.__enhancerApi;
|
||||
const { html, extendProps } = globalThis.__enhancerApi,
|
||||
{ setState, useState } = globalThis.__enhancerApi;
|
||||
Coloris({ format: "rgb" });
|
||||
|
||||
type ??= "text";
|
||||
|
@ -4,7 +4,6 @@
|
||||
* (https://notion-enhancer.github.io/) under the MIT license
|
||||
*/
|
||||
|
||||
import { setState } from "../state.mjs";
|
||||
import { Description } from "./Description.mjs";
|
||||
import { Input } from "./Input.mjs";
|
||||
import { Mod } from "./Mod.mjs";
|
||||
@ -38,7 +37,7 @@ function Search({ items, itemType }) {
|
||||
}
|
||||
|
||||
function List({ id, mods, description }) {
|
||||
const { html, isEnabled, setEnabled } = globalThis.__enhancerApi,
|
||||
const { html, isEnabled, setEnabled, setState } = globalThis.__enhancerApi,
|
||||
$mods = mods.map((mod) => {
|
||||
const _get = () => isEnabled(mod.id),
|
||||
_set = async (enabled) => {
|
||||
|
@ -4,7 +4,6 @@
|
||||
* (https://notion-enhancer.github.io/) under the MIT license
|
||||
*/
|
||||
|
||||
import { setState } from "../state.mjs";
|
||||
import { Description } from "./Description.mjs";
|
||||
import { Toggle } from "./Toggle.mjs";
|
||||
|
||||
@ -21,7 +20,7 @@ function Mod({
|
||||
_set,
|
||||
_src,
|
||||
}) {
|
||||
const { html, enhancerUrl } = globalThis.__enhancerApi,
|
||||
const { html, enhancerUrl, setState } = globalThis.__enhancerApi,
|
||||
toggleId = Math.random().toString(36).slice(2, 5);
|
||||
|
||||
return html`<label
|
||||
|
@ -9,13 +9,13 @@ import { Description } from "./Description.mjs";
|
||||
import { Checkbox } from "./Checkbox.mjs";
|
||||
import { Button } from "./Button.mjs";
|
||||
import { Tile } from "./Tile.mjs";
|
||||
import { setState, useState } from "../state.mjs";
|
||||
|
||||
const privacyPolicy = "https://notion-enhancer.github.io/about/privacy-policy/",
|
||||
tsAndCs = "https://notion-enhancer.github.io/about/terms-and-conditions/";
|
||||
|
||||
function Onboarding() {
|
||||
const { html, version, initDatabase } = globalThis.__enhancerApi,
|
||||
{ setState, useState } = globalThis.__enhancerApi,
|
||||
$submitAgreement = html`<${Button}
|
||||
icon="arrow-right"
|
||||
class="ml-auto"
|
||||
|
@ -4,7 +4,6 @@
|
||||
* (https://notion-enhancer.github.io/) under the MIT license
|
||||
*/
|
||||
|
||||
import { setState } from "../state.mjs";
|
||||
import { Heading } from "./Heading.mjs";
|
||||
import { Description } from "./Description.mjs";
|
||||
import { Input } from "./Input.mjs";
|
||||
@ -78,7 +77,7 @@ function Option({ _get, _set, ...opt }) {
|
||||
}
|
||||
|
||||
function Options({ mod }) {
|
||||
const { html, modDatabase } = globalThis.__enhancerApi;
|
||||
const { html, modDatabase, setState } = globalThis.__enhancerApi;
|
||||
return filterOptionsForRender(mod.options).map((opt) => {
|
||||
opt.label ??= camelToSentenceCase(opt.key);
|
||||
if (opt.type === "heading") return html`<${Heading}>${opt.label}<//>`;
|
||||
|
@ -4,10 +4,9 @@
|
||||
* (https://notion-enhancer.github.io/) under the MIT license
|
||||
*/
|
||||
|
||||
import { setState, useState } from "../state.mjs";
|
||||
|
||||
function Popup({ trigger, ...props }, ...children) {
|
||||
const { html, extendProps } = globalThis.__enhancerApi;
|
||||
const { html, extendProps } = globalThis.__enhancerApi,
|
||||
{ setState, useState } = globalThis.__enhancerApi;
|
||||
extendProps(props, {
|
||||
class: `notion-enhancer--menu-popup
|
||||
group absolute top-0 left-0 w-full h-full
|
||||
|
@ -4,7 +4,6 @@
|
||||
* (https://notion-enhancer.github.io/) under the MIT license
|
||||
*/
|
||||
|
||||
import { setState, useState } from "../state.mjs";
|
||||
import { Heading } from "./Heading.mjs";
|
||||
import { Description } from "./Description.mjs";
|
||||
import { Checkbox } from "./Checkbox.mjs";
|
||||
@ -14,6 +13,7 @@ import { Popup } from "./Popup.mjs";
|
||||
|
||||
function Profile({ id }) {
|
||||
const { html, getProfile, initDatabase } = globalThis.__enhancerApi,
|
||||
{ setState, useState } = globalThis.__enhancerApi,
|
||||
profile = initDatabase([id]),
|
||||
db = initDatabase();
|
||||
|
||||
@ -180,6 +180,7 @@ function Profile({ id }) {
|
||||
|
||||
function Profiles() {
|
||||
const { html, initDatabase } = globalThis.__enhancerApi,
|
||||
{ setState, useState } = globalThis.__enhancerApi,
|
||||
$input = html`<${Input} icon="file-cog" />`,
|
||||
$list = html`<ul></ul>`;
|
||||
|
||||
|
@ -4,11 +4,11 @@
|
||||
* (https://notion-enhancer.github.io/) under the MIT license
|
||||
*/
|
||||
|
||||
import { setState, useState } from "../state.mjs";
|
||||
import { Popup } from "./Popup.mjs";
|
||||
|
||||
function Option({ value, _get, _set }) {
|
||||
const { html } = globalThis.__enhancerApi,
|
||||
{ setState, useState } = globalThis.__enhancerApi,
|
||||
$selected = html`<i class="ml-auto i-check w-[16px] h-[16px]"></i>`,
|
||||
$option = html`<div
|
||||
tabindex="0"
|
||||
@ -36,6 +36,7 @@ function Option({ value, _get, _set }) {
|
||||
function Select({ values, _get, _set, _requireReload = true, ...props }) {
|
||||
let _initialValue;
|
||||
const { html } = globalThis.__enhancerApi,
|
||||
{ setState, useState } = globalThis.__enhancerApi,
|
||||
// dir="rtl" overflows to the left during transition
|
||||
$select = html`<div
|
||||
dir="rtl"
|
||||
|
@ -4,11 +4,11 @@
|
||||
* (https://notion-enhancer.github.io/) under the MIT license
|
||||
*/
|
||||
|
||||
import { setState, useState } from "../state.mjs";
|
||||
import { Description } from "./Description.mjs";
|
||||
|
||||
function SidebarHeading({}, ...children) {
|
||||
const { html } = globalThis.__enhancerApi;
|
||||
const { html } = globalThis.__enhancerApi,
|
||||
{ setState, useState } = globalThis.__enhancerApi;
|
||||
return html`<h2
|
||||
class="flex items-center font-semibold leading-none
|
||||
text-([12px] [color:var(--theme--fg-secondary)])
|
||||
@ -20,6 +20,7 @@ function SidebarHeading({}, ...children) {
|
||||
|
||||
function SidebarButton({ id, icon, ...props }, ...children) {
|
||||
const { html, extendProps } = globalThis.__enhancerApi,
|
||||
{ setState, useState } = globalThis.__enhancerApi,
|
||||
$btn = html`<${props["href"] ? "a" : "button"}
|
||||
class="flex items-center select-none text-[14px]
|
||||
min-h-[27px] px-[12px] my-px last:mb-[12px] w-full
|
||||
@ -53,6 +54,7 @@ function SidebarButton({ id, icon, ...props }, ...children) {
|
||||
function Sidebar({ items, categories }) {
|
||||
const { html, version } = globalThis.__enhancerApi,
|
||||
{ initDatabase, isEnabled } = globalThis.__enhancerApi,
|
||||
{ setState, useState } = globalThis.__enhancerApi,
|
||||
$agreeToUnlock = html`<span
|
||||
class="pt-[2px] pb-[5px] px-[15px] text-[12px]
|
||||
inline-block text-[color:var(--theme--fg-red)]"
|
||||
|
@ -5,12 +5,12 @@
|
||||
*/
|
||||
|
||||
import { collectTelemetryData } from "../../sendTelemetry.mjs";
|
||||
import { useState, setState } from "../state.mjs";
|
||||
import { Option } from "./Options.mjs";
|
||||
|
||||
const privacyPolicy = "https://notion-enhancer.github.io/about/privacy-policy/";
|
||||
function Telemetry() {
|
||||
const { html, initDatabase } = globalThis.__enhancerApi,
|
||||
{ setState, useState } = globalThis.__enhancerApi,
|
||||
_get = async () => {
|
||||
// defaults to true, must be explicitly set to false to disable
|
||||
return (await initDatabase().get("telemetryEnabled")) ?? true;
|
||||
|
@ -4,11 +4,10 @@
|
||||
* (https://notion-enhancer.github.io/) under the MIT license
|
||||
*/
|
||||
|
||||
import { setState, useState } from "../state.mjs";
|
||||
|
||||
function Toggle({ _get, _set, _requireReload = true, ...props }) {
|
||||
let _initialValue;
|
||||
const { html, extendProps } = globalThis.__enhancerApi,
|
||||
{ setState, useState } = globalThis.__enhancerApi,
|
||||
$input = html`<input
|
||||
type="checkbox"
|
||||
class="hidden checked:sibling:children:(
|
||||
|
@ -4,10 +4,9 @@
|
||||
* (https://notion-enhancer.github.io/) under the MIT license
|
||||
*/
|
||||
|
||||
import { setState, useState } from "../state.mjs";
|
||||
|
||||
function View({ id }, ...children) {
|
||||
const { html } = globalThis.__enhancerApi,
|
||||
{ setState, useState } = globalThis.__enhancerApi,
|
||||
// set padding on last child to maintain pad on overflow
|
||||
$view = html`<article
|
||||
id=${id}
|
||||
|
@ -4,7 +4,6 @@
|
||||
* (https://notion-enhancer.github.io/) under the MIT license
|
||||
*/
|
||||
|
||||
import { setState, useState } from "./state.mjs";
|
||||
import { checkForUpdate, isDevelopmentBuild } from "../updateCheck.mjs";
|
||||
import { Sidebar } from "./islands/Sidebar.mjs";
|
||||
import { Footer } from "./islands/Footer.mjs";
|
||||
@ -17,6 +16,11 @@ import { Mod } from "./islands/Mod.mjs";
|
||||
import { Options } from "./islands/Options.mjs";
|
||||
import { Profiles } from "./islands/Profiles.mjs";
|
||||
|
||||
let _apiImported = false,
|
||||
_renderStarted = false,
|
||||
_stateHookedInto = false,
|
||||
_hotkeyRegistered = false;
|
||||
|
||||
const categories = [
|
||||
{
|
||||
icon: "palette",
|
||||
@ -85,15 +89,14 @@ const categories = [
|
||||
})),
|
||||
];
|
||||
|
||||
const render = async () => {
|
||||
const { html, getMods } = globalThis.__enhancerApi,
|
||||
{ isEnabled, setEnabled } = globalThis.__enhancerApi,
|
||||
[icon, renderStarted] = useState(["icon", "renderStarted"]);
|
||||
if (!html || !getMods || !icon || renderStarted) return;
|
||||
const renderMenu = async () => {
|
||||
const { html, ...api } = globalThis.__enhancerApi,
|
||||
[theme, icon] = api.useState(["theme", "icon"]);
|
||||
if (!theme || !icon || _renderStarted) return;
|
||||
if (icon === "Monochrome") sidebar[1].icon += "?mask";
|
||||
setState({ renderStarted: true });
|
||||
_renderStarted = true;
|
||||
|
||||
const mods = await getMods();
|
||||
const mods = await api.getMods();
|
||||
for (let i = 0; i < categories.length; i++) {
|
||||
const { id } = categories[i];
|
||||
categories[i].mods = mods.filter(({ _src }) => _src.startsWith(`${id}/`));
|
||||
@ -104,10 +107,10 @@ const render = async () => {
|
||||
for (let i = 0; i < mods.length; i++) {
|
||||
const options = mods[i].options?.filter((opt) => opt.type !== "heading");
|
||||
if (mods[i]._src === "core" || !options?.length) continue;
|
||||
const _get = () => isEnabled(mods[i].id),
|
||||
const _get = () => api.isEnabled(mods[i].id),
|
||||
_set = async (enabled) => {
|
||||
await setEnabled(mods[i].id, enabled);
|
||||
setState({ rerender: true });
|
||||
await api.setEnabled(mods[i].id, enabled);
|
||||
api.setState({ rerender: true });
|
||||
};
|
||||
mods[i].view = html`<${View} id=${mods[i].id}>
|
||||
<!-- passing an empty options array hides the settings button -->
|
||||
@ -145,58 +148,67 @@ const render = async () => {
|
||||
<${Footer} categories=${categories} />
|
||||
</main>
|
||||
`;
|
||||
useState(["footerOpen"], ([footerOpen]) => {
|
||||
api.useState(["footerOpen"], ([footerOpen]) => {
|
||||
$main.style.height = footerOpen ? "100%" : "calc(100% + 33px)";
|
||||
});
|
||||
useState(["transitionInProgress"], ([transitionInProgress]) => {
|
||||
api.useState(["transitionInProgress"], ([transitionInProgress]) => {
|
||||
$main.children[0].style.overflow = transitionInProgress ? "hidden" : "";
|
||||
});
|
||||
|
||||
const $skeleton = document.querySelector("#skeleton");
|
||||
$skeleton.replaceWith($sidebar, $main);
|
||||
};
|
||||
|
||||
window.addEventListener("focus", () => {
|
||||
setState({ focus: true, rerender: true });
|
||||
});
|
||||
window.addEventListener("message", (event) => {
|
||||
if (event.data?.channel !== "notion-enhancer") return;
|
||||
const [hotkey, theme, icon] = useState(["hotkey", "theme", "icon"]);
|
||||
setState({
|
||||
rerender: true,
|
||||
hotkey: event.data?.hotkey ?? hotkey,
|
||||
theme: event.data?.theme ?? theme,
|
||||
icon: event.data?.icon ?? icon,
|
||||
});
|
||||
});
|
||||
useState(["hotkey"], ([hotkey]) => {
|
||||
const { addKeyListener } = globalThis.__enhancerApi ?? {},
|
||||
[hotkeyRegistered] = useState(["hotkeyRegistered"]),
|
||||
[renderStarted] = useState(["renderStarted"]);
|
||||
if (!hotkey || !addKeyListener || hotkeyRegistered || !renderStarted) return;
|
||||
setState({ hotkeyRegistered: true });
|
||||
addKeyListener(hotkey, (event) => {
|
||||
},
|
||||
registerHotkey = ([hotkey]) => {
|
||||
const api = globalThis.__enhancerApi;
|
||||
if (!hotkey || _hotkeyRegistered) return;
|
||||
_hotkeyRegistered = true;
|
||||
api.addKeyListener(hotkey, (event) => {
|
||||
event.preventDefault();
|
||||
const msg = { channel: "notion-enhancer", action: "open-menu" };
|
||||
parent?.postMessage(msg, "*");
|
||||
});
|
||||
addKeyListener("Escape", () => {
|
||||
const [popupOpen] = useState(["popupOpen"]);
|
||||
api.addKeyListener("Escape", () => {
|
||||
const [popupOpen] = api.useState(["popupOpen"]);
|
||||
if (!popupOpen) {
|
||||
const msg = { channel: "notion-enhancer", action: "close-menu" };
|
||||
parent?.postMessage(msg, "*");
|
||||
} else setState({ rerender: true });
|
||||
} else api.setState({ rerender: true });
|
||||
});
|
||||
});
|
||||
useState(["theme"], ([theme]) => {
|
||||
},
|
||||
updateTheme = ([theme]) => {
|
||||
if (theme === "dark") document.body.classList.add("dark");
|
||||
if (theme === "light") document.body.classList.remove("dark");
|
||||
});
|
||||
};
|
||||
|
||||
useState(["rerender"], async () => {
|
||||
const [theme, icon] = useState(["theme", "icon"]);
|
||||
if (!theme || !icon) return;
|
||||
if (typeof globalThis.__enhancerApi === "undefined")
|
||||
await import("../../shared/system.js");
|
||||
(await import("../../load.mjs")).default.then(render);
|
||||
const importApi = async () => {
|
||||
if (_apiImported) return;
|
||||
_apiImported = true;
|
||||
const api = globalThis.__enhancerApi;
|
||||
if (typeof api === "undefined") await import("../../shared/system.js");
|
||||
await import("../../load.mjs").then((i) => i.default);
|
||||
},
|
||||
hookIntoState = () => {
|
||||
if (_stateHookedInto) return;
|
||||
_stateHookedInto = true;
|
||||
const api = globalThis.__enhancerApi;
|
||||
api.useState(["rerender"], renderMenu);
|
||||
api.useState(["hotkey"], registerHotkey);
|
||||
api.useState(["theme"], updateTheme);
|
||||
};
|
||||
|
||||
window.addEventListener("focus", async () => {
|
||||
await importApi().then(hookIntoState);
|
||||
const api = globalThis.__enhancerApi;
|
||||
api.setState({ focus: true, rerender: true });
|
||||
});
|
||||
window.addEventListener("message", async (event) => {
|
||||
if (event.data?.channel !== "notion-enhancer") return;
|
||||
await importApi().then(hookIntoState);
|
||||
const api = globalThis.__enhancerApi;
|
||||
api.setState({
|
||||
rerender: true,
|
||||
hotkey: event.data?.hotkey ?? api.useState(["hotkey"]),
|
||||
theme: event.data?.theme ?? api.useState(["theme"]),
|
||||
icon: event.data?.icon ?? api.useState(["icon"]),
|
||||
});
|
||||
});
|
||||
|
@ -1,24 +0,0 @@
|
||||
/**
|
||||
* notion-enhancer
|
||||
* (c) 2023 dragonwocky <thedragonring.bod@gmail.com> (https://dragonwocky.me/)
|
||||
* (https://notion-enhancer.github.io/) under the MIT license
|
||||
*/
|
||||
|
||||
const _state = {},
|
||||
_subscribers = [];
|
||||
|
||||
const setState = (state) => {
|
||||
Object.assign(_state, state);
|
||||
const updates = Object.keys(state);
|
||||
_subscribers
|
||||
.filter(([keys]) => updates.some((key) => keys.includes(key)))
|
||||
.forEach(([keys, callback]) => callback(keys.map((key) => _state[key])));
|
||||
},
|
||||
useState = (keys, callback) => {
|
||||
const state = keys.map((key) => _state[key]);
|
||||
if (callback) _subscribers.push([keys, callback]);
|
||||
callback?.(state);
|
||||
return state;
|
||||
};
|
||||
|
||||
export { setState, useState };
|
@ -6,6 +6,8 @@
|
||||
|
||||
"use strict";
|
||||
|
||||
// jump to
|
||||
|
||||
const { twind, htm, lucide } = globalThis,
|
||||
{ readFile, iconColour, iconMonochrome } = globalThis.__enhancerApi;
|
||||
|
||||
@ -71,6 +73,14 @@ const encodeSvg = (svg) =>
|
||||
}),
|
||||
};
|
||||
};
|
||||
|
||||
// at-runtime utility class evaluation w/ twind:
|
||||
// - feature parity w/ tailwind v3
|
||||
// - useful for building self-contained components
|
||||
// (mods can extend interfaces w/out needing to
|
||||
// import additional stylesheets)
|
||||
// - integrated with lucide to render icons w/out
|
||||
// complex markup, e.g. `<i class="i-bookmark" />`
|
||||
twind.install({
|
||||
darkMode: "class",
|
||||
rules: [[/^i-((?:\w|-)+)(?:\?(mask|bg|auto))?$/, presetIcons]],
|
||||
@ -87,14 +97,6 @@ twind.install({
|
||||
["\\[.+]", (match) => "&" + match.input],
|
||||
["([a-z-]+):", ({ 1: $1 }) => "&::" + $1],
|
||||
],
|
||||
theme: {
|
||||
extend: {
|
||||
backgroundImage: {
|
||||
"brand-gradient":
|
||||
"linear-gradient(225deg, rgba(255,255,255,0.2) 0%, rgba(255,255,255,0) 100%)",
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
// https://developer.mozilla.org/en-US/docs/Web/SVG/Element
|
||||
@ -535,7 +537,11 @@ const svgElements = [
|
||||
"zoomAndPan",
|
||||
];
|
||||
|
||||
// html`<div class=${className}></div>`
|
||||
// enables use of the jsx-like htm syntax
|
||||
// for building components and interfaces
|
||||
// with tagged templates. instantiates dom
|
||||
// elements directly, does not use a vdom.
|
||||
// e.g. html`<div class=${className}></div>`
|
||||
const h = (type, props, ...children) => {
|
||||
children = children.flat(Infinity);
|
||||
// html`<${Component} attr="value">Click Me<//>`
|
||||
@ -557,9 +563,12 @@ const h = (type, props, ...children) => {
|
||||
elem.append(...children);
|
||||
return elem;
|
||||
},
|
||||
html = htm.bind(h);
|
||||
|
||||
const extendProps = (props, extend) => {
|
||||
// combines instance-provided element props
|
||||
// with a template of element props such that
|
||||
// island/component/template props handlers
|
||||
// and styles can be preserved and extended
|
||||
// rather than overwritten
|
||||
extendProps = (props, extend) => {
|
||||
for (const key in extend) {
|
||||
const { [key]: userProvided } = props;
|
||||
if (typeof extend[key] === "function") {
|
||||
@ -574,7 +583,41 @@ const extendProps = (props, extend) => {
|
||||
} else props[key] = extend[key] ?? userProvided;
|
||||
}
|
||||
return props;
|
||||
};
|
||||
},
|
||||
html = htm.bind(h);
|
||||
|
||||
// provides basic key/value reactivity:
|
||||
// this is shared between all active mods,
|
||||
// i.e. mods can read and update other mods'
|
||||
// reactive states. this enables interop
|
||||
// between a mod's component islands and
|
||||
// supports inter-mod communication if so
|
||||
// required. caution should be used in
|
||||
// naming keys to avoid conflicts
|
||||
const _state = {},
|
||||
_subscribers = [],
|
||||
setState = (state) => {
|
||||
Object.assign(_state, state);
|
||||
const updates = Object.keys(state);
|
||||
_subscribers
|
||||
.filter(([keys]) => updates.some((key) => keys.includes(key)))
|
||||
.forEach(([keys, callback]) => callback(keys.map((key) => _state[key])));
|
||||
},
|
||||
// useState(["keyA", "keyB"]) => returns [valueA, valueB]
|
||||
// useState(["keyA", "keyB"], callback) => registers callback
|
||||
// to be triggered after each update to either keyA or keyB,
|
||||
// with [valueA, valueB] passed to the callback's first arg
|
||||
useState = (keys, callback) => {
|
||||
const state = keys.map((key) => _state[key]);
|
||||
if (callback) _subscribers.push([keys, callback]);
|
||||
callback?.(state);
|
||||
return state;
|
||||
};
|
||||
|
||||
globalThis.__enhancerApi ??= {};
|
||||
Object.assign(globalThis.__enhancerApi, { html, extendProps });
|
||||
Object.assign(globalThis.__enhancerApi, {
|
||||
html,
|
||||
extendProps,
|
||||
setState,
|
||||
useState,
|
||||
});
|
||||
|
Loading…
Reference in New Issue
Block a user