feat(menu): mimic notion select options popup

electron was positioning options distanced from their <select> for... some reason
This commit is contained in:
dragonwocky 2023-01-15 00:39:01 +11:00
parent d1f83884aa
commit e681d462d3
Signed by: dragonwocky
GPG Key ID: 7998D08F7D7BD7A8
3 changed files with 88 additions and 42 deletions

View File

@ -71,13 +71,11 @@ export default async (api, db) => {
$menuFrame = html`<iframe
title="notion-enhancer menu"
src="${enhancerUrl("core/menu/index.html")}"
class="
rounded-[5px] w-[1150px] h-[calc(100vh-100px)]
max-w-[calc(100vw-100px)] max-h-[715px] overflow-hidden
bg-[color:var(--theme--bg-primary)] drop-shadow-xl
group-open:(pointer-events-auto opacity-100 scale-100)
transition opacity-0 scale-95
"
class="rounded-[5px] w-[1150px] h-[calc(100vh-100px)]
max-w-[calc(100vw-100px)] max-h-[715px] overflow-hidden
bg-[color:var(--theme--bg-primary)] drop-shadow-xl
group-open:(pointer-events-auto opacity-100 scale-100)
transition opacity-0 scale-95"
onload=${() => {
// pass notion-enhancer api to electron menu process
if (platform !== "browser") {

View File

@ -400,7 +400,7 @@ function FileInput({ extensions, _get, _set, ...props }) {
$filename = html`<span>Upload a file</span>`,
$clear = html`<button
class="ml-[8px] h-[14px] cursor-pointer text-[color:var(--theme--fg-secondary)]
transition duration-[20ms] hover:text-[color:var(--theme--fg-primary)]"
transition duration-[20ms] hover:text-[color:var(--theme--fg-primary)] flex"
style="display: none"
onclick=${() => {
$filename.innerText = "Upload a file";
@ -459,50 +459,57 @@ function FileInput({ extensions, _get, _set, ...props }) {
function Select({ values, _get, _set, ...props }) {
const { html } = globalThis.__enhancerApi,
$select = html`<select
$select = html`<div
dir="rtl"
role="button"
tabindex="0"
class="appearance-none bg-transparent rounded-[4px] cursor-pointer
text-[14px] leading-[1.2] pl-[8px] pr-[28px] h-[28px] max-w-[256px]
text-[14px] leading-[28px] h-[28px] max-w-[256px] pl-[8px] pr-[28px]
transition duration-[20ms] hover:bg-[color:var(--theme--bg-hover)]"
...${props}
></div>`,
$popup = html`<div
class="group absolute top-0 left-0
flex flex-col justify-center items-end
pointer-events-none w-full h-full"
>
${values.map((value) => {
return html`<option
value=${value}
<div class="relative right-[100%]">
<div
class="bg-[color:var(--theme--bg-secondary)]
text-[color:var(--theme--fg-primary)]"
w-[250px] max-w-[calc(100vw-24px)] max-h-[70vh]
py-[6px] px-[4px] drop-shadow-xl overflow-y-auto
transition duration-[200ms] opacity-0 scale-95 rounded-[4px]
group-open:(pointer-events-auto opacity-100 scale-100)"
>
${value}
</option>`;
})}
</select>`,
updateWidth = () => {
const $tmp = html`<span
class="text-[14px] pl-[8px] pr-[28px]
absolute top-[-9999px] left-[-9999px]"
>${$select.value}</span
>`;
document.body.append($tmp);
requestAnimationFrame(() => {
$select.style.width = `${Math.min($tmp.offsetWidth, 256)}px`;
$tmp.remove();
});
};
${values.map((value) => {
return html`<${SelectOption} ...${{ value, _get, _set }} />`;
})}
</div>
</div>
</div>`;
const { onchange } = $select;
$select.onchange = (event) => {
onchange?.(event);
_set?.($select.value);
updateWidth();
const { onclick } = $select;
$select.onclick = (event) => {
onclick?.(event);
$popup.setAttribute("open", true);
};
useState(["rerender"], () => {
_get?.().then((value) => {
$select.value = value;
updateWidth();
if ($popup.hasAttribute("open")) {
$popup.removeAttribute("open");
$select.style.width = `${$select.offsetWidth}px`;
$select.style.background = "transparent";
$select.innerText = value;
setTimeout(() => {
$select.style.width = "";
$select.style.background = "";
}, 200);
} else $select.innerText = value;
});
});
return html`<div class="notion-enhancer--menu-select relative">
${$select}
${$select}${$popup}
<i
class="i-chevron-down pointer-events-none
absolute right-[6px] top-[6px] w-[16px] h-[16px]
@ -511,13 +518,45 @@ function Select({ values, _get, _set, ...props }) {
</div>`;
}
function SelectOption({ value, _get, _set, ...props }) {
const { html } = globalThis.__enhancerApi,
$selected = html`<i class="ml-auto i-check w-[16px] h-[16px]"></i>`,
$option = html`<div
role="button"
tabindex="0"
class="select-none cursor-pointer rounded-[3px]
flex items-center w-full h-[28px] px-[12px] leading-[1.2]
transition duration-[20ms] hover:bg-[color:var(--theme--bg-hover)]"
...${props}
>
<div class="mr-[6px] text-[14px] text-ellipsis overflow-hidden">
${value}
</div>
</div>`;
const { onclick } = $option;
$option.onclick = (event) => {
onclick?.(event);
_set?.(value);
};
useState(["rerender"], () => {
_get?.().then((actualValue) => {
if (actualValue === value) {
$option.append($selected);
} else $selected.remove();
});
});
return $option;
}
function Toggle({ _get, _set, ...props }) {
const { html } = globalThis.__enhancerApi,
$input = html`<input
tabindex="-1"
type="checkbox"
class="hidden checked:sibling:children:(
bg-[color:var(--theme--accent-primary)] after:translate-x-[12px])"
bg-[color:var(--theme--accent-primary)] after:translate-x-[12px])"
...${props}
/>`;

View File

@ -35,7 +35,10 @@ const renderOptions = async (mod) => {
options = options.map(async (opt) => {
if (opt.type === "heading") return html`<${Option} ...${opt} />`;
const _get = () => db.get(opt.key),
_set = (value) => db.set(opt.key, value);
_set = async (value) => {
await db.set(opt.key, value);
setState({ rerender: true });
};
return html`<${Option} ...${{ ...opt, _get, _set }} />`;
});
return Promise.all(options);
@ -60,7 +63,10 @@ const compatibleMods = (mods) => {
enabledMods = initDatabase([await getProfile(), "enabledMods"]);
mods = compatibleMods(mods).map(async (mod) => {
const _get = () => enabledMods.get(mod.id),
_set = (enabled) => enabledMods.set(mod.id, enabled);
_set = async (enabled) => {
await enabledMods.set(mod.id, enabled);
setState({ rerender: true });
};
return html`<${Mod} ...${{ ...mod, _get, _set }} />`;
});
return html`<${List}>${await Promise.all(mods)}<//>`;
@ -75,7 +81,10 @@ const renderOptionViews = async (parentView, mods) => {
})
.map(async (mod) => {
const _get = () => enabledMods.get(mod.id),
_set = (enabled) => enabledMods.set(mod.id, enabled);
_set = async (enabled) => {
await enabledMods.set(mod.id, enabled);
setState({ rerender: true });
};
return html`<${View} id=${mod.id}>
<${Mod} ...${{ ...mod, options: [], _get, _set }} />
${await renderOptions(mod)}<//