notion-enhancer/src/common/dom.mjs
dragonwocky 5fb4405614
feat: create modal for menu to open within
- replace preact with direct htmlelement creation
2022-12-20 23:45:26 +11:00

100 lines
3.2 KiB
JavaScript

/**
* notion-enhancer
* (c) 2022 dragonwocky <thedragonring.bod@gmail.com> (https://dragonwocky.me/)
* (https://notion-enhancer.github.io/) under the MIT license
*/
import "../vendor/twind.min.js";
import "../vendor/lucide.min.js";
import htm from "../vendor/htm.min.js";
const { readFile } = globalThis.__enhancerApi,
enhancerIconColour = await readFile("/assets/colour.svg"),
enhancerIconMonochrome = await readFile("/assets/monochrome.svg");
const kebabToPascalCase = (string) =>
string[0].toUpperCase() +
string.replace(/-[a-z]/g, (match) => match.slice(1).toUpperCase()).slice(1),
hToString = (type, props, ...children) =>
`<${type}${Object.entries(props)
.map(([attr, value]) => ` ${attr}="${value}"`)
.join("")}>${children
.flat(Infinity)
.map(([tag, attrs, children]) => hToString(tag, attrs, children))
.join("")}</${type}>`;
// https://gist.github.com/jennyknuth/222825e315d45a738ed9d6e04c7a88d0
const encodeSvg = (svg) =>
svg
.replace(
"<svg",
~svg.indexOf("xmlns") ? "<svg" : '<svg xmlns="http://www.w3.org/2000/svg"'
)
.replace(/"/g, "'")
.replace(/%/g, "%25")
.replace(/#/g, "%23")
.replace(/{/g, "%7B")
.replace(/}/g, "%7D")
.replace(/</g, "%3C")
.replace(/>/g, "%3E")
.replace(/\s+/g, " ");
// https://antfu.me/posts/icons-in-pure-css
const presetIcons = () => ({
rules: [
[
/^i-((?:\w|-)+)(?:\?(mask|bg|auto))?$/,
([, icon, mode]) => {
let svg;
// manually register i-notion-enhancer: renders the colour
// version by default, renders the monochrome version when
// mask mode is requested via i-notion-enhancer?mask
if (icon === "notion-enhancer") {
svg = mode === "mask" ? enhancerIconMonochrome : enhancerIconColour;
} else {
icon = kebabToPascalCase(icon);
if (!globalThis.lucide[icon]) return;
svg = hToString(...globalThis.lucide[icon]);
}
const dataUri = `url("data:image/svg+xml;utf8,${encodeSvg(svg)}")`;
if (mode === "auto") mode = undefined;
mode ??= svg.includes("currentColor") ? "mask" : "bg";
return mode === "mask"
? {
mask: `${dataUri} no-repeat`,
"mask-size": "100% 100%",
"background-color": "currentColor",
color: "inherit",
height: "1em",
width: "1em",
}
: {
background: `${dataUri} no-repeat`,
"background-size": "100% 100%",
"background-color": "transparent",
height: "1em",
width: "1em",
};
},
],
],
});
const { twind } = globalThis;
twind.install({ presets: [presetIcons()] });
// constructs elements via html`tagged templates`
const h = (type, props, ...children) => {
const elem = document.createElement(type);
for (const prop in props) {
if (["string", "number", "boolean"].includes(typeof props[prop])) {
elem.setAttribute(prop, props[prop]);
} else elem[prop] = props[prop];
}
for (const child of children) elem.append(child);
return elem;
},
html = htm.bind(h);
export { html, twind };