mirror of
https://github.com/notion-enhancer/notion-enhancer.git
synced 2025-04-11 15:59:03 +00:00
fix(menu): keyboard triggering of inputs and switches via space/enter, don't trap tab in hotkey input
This commit is contained in:
parent
c3317bd9ec
commit
50a23f17c5
@ -25,7 +25,7 @@ function Button({ icon, variant, tagName, ...props }, ...children) {
|
|||||||
hover:bg-[color:var(--theme--bg-hover)]`
|
hover:bg-[color:var(--theme--bg-hover)]`
|
||||||
}`,
|
}`,
|
||||||
});
|
});
|
||||||
return html`<${tagName ?? "button"} ...${props}>
|
return html`<${tagName ?? "button"} tabindex="0" ...${props}>
|
||||||
${icon
|
${icon
|
||||||
? html`<i
|
? html`<i
|
||||||
class="i-${icon}
|
class="i-${icon}
|
||||||
|
@ -25,6 +25,12 @@ function Checkbox({ _get, _set, ...props }) {
|
|||||||
return html`<label
|
return html`<label
|
||||||
tabindex="0"
|
tabindex="0"
|
||||||
class="notion-enhancer--menu-checkbox cursor-pointer"
|
class="notion-enhancer--menu-checkbox cursor-pointer"
|
||||||
|
onkeydown=${(event) => {
|
||||||
|
if ([" ", "Enter"].includes(event.key)) {
|
||||||
|
event.preventDefault();
|
||||||
|
$input.click();
|
||||||
|
}
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
${$input}
|
${$input}
|
||||||
<div class="flex items-center h-[16px] transition duration-[200ms]">
|
<div class="flex items-center h-[16px] transition duration-[200ms]">
|
||||||
|
@ -7,13 +7,16 @@
|
|||||||
import { extendProps, useState } from "../state.mjs";
|
import { extendProps, useState } from "../state.mjs";
|
||||||
|
|
||||||
const updateHotkey = (event) => {
|
const updateHotkey = (event) => {
|
||||||
event.preventDefault();
|
|
||||||
const keys = [];
|
const keys = [];
|
||||||
for (const modifier of ["metaKey", "ctrlKey", "altKey", "shiftKey"]) {
|
for (const modifier of ["metaKey", "ctrlKey", "altKey", "shiftKey"]) {
|
||||||
if (!event[modifier]) continue;
|
if (!event[modifier]) continue;
|
||||||
const alias = modifier[0].toUpperCase() + modifier.slice(1, -3);
|
const alias = modifier[0].toUpperCase() + modifier.slice(1, -3);
|
||||||
keys.push(alias);
|
keys.push(alias);
|
||||||
}
|
}
|
||||||
|
// retain tab for keyboard navigation of menu
|
||||||
|
if (event.key === "Tab" && !keys.length) {
|
||||||
|
return;
|
||||||
|
} else event.preventDefault();
|
||||||
if (!keys.length && ["Backspace", "Delete"].includes(event.key)) {
|
if (!keys.length && ["Backspace", "Delete"].includes(event.key)) {
|
||||||
event.target.value = "";
|
event.target.value = "";
|
||||||
} else if (event.key) {
|
} else if (event.key) {
|
||||||
@ -146,6 +149,12 @@ function Input({
|
|||||||
h-[28px] px-[8px] bg-[color:var(--theme--bg-secondary)]
|
h-[28px] px-[8px] bg-[color:var(--theme--bg-secondary)]
|
||||||
text-([14px] [color:var(--theme--fg-secondary)]) rounded-[4px]
|
text-([14px] [color:var(--theme--fg-secondary)]) rounded-[4px]
|
||||||
transition duration-[20ms] hover:bg-[color:var(--theme--bg-hover)]"
|
transition duration-[20ms] hover:bg-[color:var(--theme--bg-hover)]"
|
||||||
|
onkeydown=${(event) => {
|
||||||
|
if ([" ", "Enter"].includes(event.key)) {
|
||||||
|
event.preventDefault();
|
||||||
|
$input.click();
|
||||||
|
}
|
||||||
|
}}
|
||||||
>${$input}${$icon.children[0]}${$filename}
|
>${$input}${$icon.children[0]}${$filename}
|
||||||
</label>
|
</label>
|
||||||
${$clear}
|
${$clear}
|
||||||
|
@ -50,7 +50,10 @@ function Popup({ trigger, onopen, onclose, onbeforeclose }, ...children) {
|
|||||||
extendProps(trigger, {
|
extendProps(trigger, {
|
||||||
onclick: $popup.show,
|
onclick: $popup.show,
|
||||||
onkeydown(event) {
|
onkeydown(event) {
|
||||||
if (event.key === "Enter") $popup.show();
|
if ([" ", "Enter"].includes(event.key)) {
|
||||||
|
event.preventDefault();
|
||||||
|
$popup.show();
|
||||||
|
}
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -11,7 +11,8 @@ function Toggle({ _get, _set, ...props }) {
|
|||||||
$input = html`<input
|
$input = html`<input
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
class="hidden checked:sibling:children:(
|
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}
|
...${props}
|
||||||
/>`;
|
/>`;
|
||||||
extendProps($input, { onchange: () => _set?.($input.checked) });
|
extendProps($input, { onchange: () => _set?.($input.checked) });
|
||||||
@ -25,6 +26,12 @@ function Toggle({ _get, _set, ...props }) {
|
|||||||
tabindex="0"
|
tabindex="0"
|
||||||
class="w-[30px] h-[18px] rounded-[44px] cursor-pointer
|
class="w-[30px] h-[18px] rounded-[44px] cursor-pointer
|
||||||
transition duration-200 bg-[color:var(--theme--bg-hover)]"
|
transition duration-200 bg-[color:var(--theme--bg-hover)]"
|
||||||
|
onkeydown=${(event) => {
|
||||||
|
if ([" ", "Enter"].includes(event.key)) {
|
||||||
|
event.preventDefault();
|
||||||
|
$input.click();
|
||||||
|
}
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="w-full h-full rounded-[44px] text-[12px]
|
class="w-full h-full rounded-[44px] text-[12px]
|
||||||
|
@ -39,14 +39,14 @@ function Profile({ id }) {
|
|||||||
class="py-[2px] px-[4px] rounded-[3px]
|
class="py-[2px] px-[4px] rounded-[3px]
|
||||||
bg-[color:var(--theme--bg-hover)]"
|
bg-[color:var(--theme--bg-hover)]"
|
||||||
></span>`,
|
></span>`,
|
||||||
$success = html`<${Popup}
|
$uploadSuccess = html`<${Popup}
|
||||||
onopen=${async () => ($successName.innerText = await getName())}
|
onopen=${async () => ($successName.innerText = await getName())}
|
||||||
>
|
>
|
||||||
<p class="py-[2px] px-[8px] text-[14px]">
|
<p class="py-[2px] px-[8px] text-[14px]">
|
||||||
The profile ${$successName} has been updated successfully.
|
The profile ${$successName} has been updated successfully.
|
||||||
</p>
|
</p>
|
||||||
<//>`,
|
<//>`,
|
||||||
$error = html`<${Popup}>
|
$uploadError = html`<${Popup}>
|
||||||
<p
|
<p
|
||||||
class="py-[2px] px-[8px] text-[14px]
|
class="py-[2px] px-[8px] text-[14px]
|
||||||
text-[color:var(--theme--accent-secondary)]"
|
text-[color:var(--theme--accent-secondary)]"
|
||||||
@ -60,16 +60,13 @@ function Profile({ id }) {
|
|||||||
reader.onload = async (progress) => {
|
reader.onload = async (progress) => {
|
||||||
const res = progress.currentTarget.result;
|
const res = progress.currentTarget.result;
|
||||||
try {
|
try {
|
||||||
await profile.import({
|
await profile.import(JSON.parse(res));
|
||||||
...JSON.parse(res),
|
|
||||||
profileName: await getName(),
|
|
||||||
});
|
|
||||||
setState({ rerender: true, databaseUpdated: true });
|
setState({ rerender: true, databaseUpdated: true });
|
||||||
$success.show();
|
$uploadSuccess.show();
|
||||||
setTimeout(() => $success.hide(), 2000);
|
setTimeout(() => $uploadSuccess.hide(), 2000);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
$error.show();
|
$uploadError.show();
|
||||||
setTimeout(() => $error.hide(), 2000);
|
setTimeout(() => $uploadError.hide(), 2000);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
reader.readAsText(file);
|
reader.readAsText(file);
|
||||||
@ -94,7 +91,14 @@ function Profile({ id }) {
|
|||||||
$a.click();
|
$a.click();
|
||||||
$a.remove();
|
$a.remove();
|
||||||
},
|
},
|
||||||
deleteProfile = async () => {
|
$uploadInput = html`<input
|
||||||
|
type="file"
|
||||||
|
class="hidden"
|
||||||
|
accept=".json"
|
||||||
|
onchange=${uploadProfile}
|
||||||
|
/>`;
|
||||||
|
|
||||||
|
const deleteProfile = async () => {
|
||||||
let profileIds = await db.get("profileIds");
|
let profileIds = await db.get("profileIds");
|
||||||
if (!profileIds?.length) profileIds = ["default"];
|
if (!profileIds?.length) profileIds = ["default"];
|
||||||
// clear profile data
|
// clear profile data
|
||||||
@ -108,9 +112,8 @@ function Profile({ id }) {
|
|||||||
await db.remove("activeProfile");
|
await db.remove("activeProfile");
|
||||||
setState({ rerender: true, databaseUpdated: true });
|
setState({ rerender: true, databaseUpdated: true });
|
||||||
} else setState({ rerender: true });
|
} else setState({ rerender: true });
|
||||||
};
|
},
|
||||||
|
$delete = html`<button
|
||||||
const $delete = html`<button
|
|
||||||
class="h-[14px] transition duration-[20ms]
|
class="h-[14px] transition duration-[20ms]
|
||||||
text-[color:var(--theme--fg-secondary)]
|
text-[color:var(--theme--fg-secondary)]
|
||||||
hover:text-[color:var(--theme--fg-primary)]"
|
hover:text-[color:var(--theme--fg-primary)]"
|
||||||
@ -138,7 +141,7 @@ function Profile({ id }) {
|
|||||||
<${Button}
|
<${Button}
|
||||||
tabindex="0"
|
tabindex="0"
|
||||||
class="justify-center"
|
class="justify-center"
|
||||||
onclick=${() => $confirm.close()}
|
onclick=${() => $confirm.hide()}
|
||||||
>
|
>
|
||||||
Cancel
|
Cancel
|
||||||
<//>
|
<//>
|
||||||
@ -151,14 +154,18 @@ function Profile({ id }) {
|
|||||||
onchange=${(event) => (event.target.checked = true)}
|
onchange=${(event) => (event.target.checked = true)}
|
||||||
/>
|
/>
|
||||||
<${Input} icon="file-cog" ...${{ _get: getName, _set: setName }} />
|
<${Input} icon="file-cog" ...${{ _get: getName, _set: setName }} />
|
||||||
<${Button} variant="sm" icon="import" class="relative" tagName="label">
|
<${Button}
|
||||||
<input
|
icon="import"
|
||||||
type="file"
|
variant="sm"
|
||||||
class="hidden"
|
tagName="label"
|
||||||
accept=".json"
|
class="relative"
|
||||||
onchange=${uploadProfile}
|
onkeydown=${(event) => {
|
||||||
/>
|
if ([" ", "Enter"].includes(event.key)) {
|
||||||
Import ${$success} ${$error}
|
event.preventDefault();
|
||||||
|
$uploadInput.click();
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>${$uploadInput} Import ${$uploadSuccess}${$uploadError}
|
||||||
<//>
|
<//>
|
||||||
<${Button} variant="sm" icon="upload" onclick=${downloadProfile}>Export<//>
|
<${Button} variant="sm" icon="upload" onclick=${downloadProfile}>Export<//>
|
||||||
<div class="relative flex">${$delete}${$confirm}</div>
|
<div class="relative flex">${$delete}${$confirm}</div>
|
||||||
|
Loading…
Reference in New Issue
Block a user