/** * notion-enhancer * (c) 2023 dragonwocky (https://dragonwocky.me/) * (https://notion-enhancer.github.io/) under the MIT license */ const updateHotkey = (event) => { const keys = []; for (const modifier of ["metaKey", "ctrlKey", "altKey", "shiftKey"]) { if (!event[modifier]) continue; const alias = modifier[0].toUpperCase() + modifier.slice(1, -3); keys.push(alias); } // retain keyboard navigation of menu if (["Tab", "Escape"].includes(event.key) && !keys.length) { return; } else event.preventDefault(); if (!keys.length && ["Backspace", "Delete"].includes(event.key)) { event.target.value = ""; } else if (event.key) { let key = event.key; if (key === " ") key = "Space"; if (["+", "="].includes(key)) key = "Plus"; if (key === "-") key = "Minus"; if (event.code === "Comma") key = ","; if (event.code === "Period") key = "."; if (key === "Control") key = "Ctrl"; // avoid e.g. Shift+Shift, force inclusion of non-modifier if (keys.includes(key)) return; keys.push(key.length === 1 ? key.toUpperCase() : key); event.target.value = keys.join("+"); } event.target.dispatchEvent(new Event("input")); event.target.dispatchEvent(new Event("change")); }, updateContrast = ($input, $icon) => { $input.style.background = $input.value; const [r, g, b, a = 1] = $input.value .replace(/^rgba?\(/, "") .replace(/\)$/, "") .split(",") .map((n) => parseFloat(n)); if (a > 0.5) { // pick a contrasting foreground for an rgb background // using the percieved brightness constants from http://alienryderflex.com/hsp.html const brightness = 0.299 * (r * r) + 0.587 * (g * g) + 0.114 * (b * b); $input.style.color = Math.sqrt(brightness) > 165.75 ? "#000" : "#fff"; } else $input.style.color = "#000"; $icon.style.color = $input.style.color; $icon.style.opacity = "0.7"; }, readUpload = async (event) => { const file = event.target.files[0], reader = new FileReader(); return new Promise((res) => { reader.onload = async (progress) => { const content = progress.currentTarget.result, upload = { filename: file.name, content }; res(upload); }; reader.readAsText(file); }); }; function Input({ type, icon, variant, extensions, class: className, _get, _set, _requireReload = true, ...props }) { let $filename, $clear; const { html, extendProps, setState, useState } = globalThis.__enhancerApi; Coloris({ format: "rgb" }); type ??= "text"; if (type === "text") icon ??= "text-cursor"; if (type === "number") icon ??= "hash"; if (type === "hotkey") icon ??= "command"; if (type === "color") icon ??= "pipette"; if (type === "file") { icon ??= "file-up"; $filename = html`Upload a file`; $clear = html``; props.accept = extensions ?.map((ext) => (ext.startsWith(".") ? ext : `.${ext}`)) .join(","); } const $input = html``, $icon = html` `; let _initialValue; extendProps($input, { onclick: () => { // change text to "uploading..." until file has uploaded // to reassure users experiencing latency while file is processed if (type === "file") $filename.innerText = "Uploading..."; }, onchange: (event) => { if (_set && type === "file") { readUpload(event) .then(_set) // refocus iframe after file has uploaded, // sometimes switching back after opening the // native file upload menu causes a loss of focus .then(() => window.focus()); } else _set?.($input.value); }, onrerender: async () => { const value = (await _get?.()) ?? $input.value ?? ""; if (type === "file") { $filename.innerText = value?.filename || "Upload a file"; $clear.style.display = value?.filename ? "" : "none"; if (_requireReload) { _initialValue ??= value?.content || ""; if ((value?.content || "") !== _initialValue) { setState({ databaseUpdated: true }); } } } else { if ($input.value !== value) $input.value = value; if (_requireReload) { _initialValue ??= value; if (value !== _initialValue) setState({ databaseUpdated: true }); } if (type === "color") updateContrast($input, $icon); } }, onkeydown: type === "hotkey" ? updateHotkey : undefined, oninput: type === "color" ? () => _set?.($input.value) : undefined, }); useState(["rerender"], () => $input.onrerender?.()); return type === "file" ? html`
${$clear}
` : html``; } export { Input };