feat: keyboard shortcuts for common actions (closes #2)
This commit is contained in:
parent
99b515d1d1
commit
6b554cf90b
@ -498,12 +498,12 @@
|
||||
@apply font-copernicus mb-4 text-center text-4xl;
|
||||
}
|
||||
|
||||
.greeting-bottom-link {
|
||||
@apply cursor-pointer text-[var(--light-accent)]/70 transition-colors duration-200 hover:underline;
|
||||
.greeting-bottom-highlight {
|
||||
@apply mx-1 cursor-default rounded-lg border-1 border-[var(--light-text)]/10 p-0.5 text-[var(--light-accent)]/70 transition-colors duration-200 hover:text-[var(--light-highlight-text)];
|
||||
}
|
||||
|
||||
.dark .greeting-bottom-link {
|
||||
@apply text-[var(--dark-accent)];
|
||||
.dark .greeting-bottom-highlight {
|
||||
@apply border-[var(--dark-text)]/10 text-[var(--dark-accent)] hover:text-[var(--light-highlight-text)];
|
||||
}
|
||||
|
||||
/* * * * * * * * * * * * * */
|
||||
@ -570,6 +570,14 @@
|
||||
@apply border-[var(--dark-text)]/10 text-[var(--dark-text)] hover:bg-[var(--dark-foreground)]/80;
|
||||
}
|
||||
|
||||
.button-keyboard-shortcut-hint {
|
||||
@apply ml-2 rounded-lg border-1 border-[var(--light-text)]/10 px-1 text-[var(--light-text)]/50;
|
||||
}
|
||||
|
||||
.dark .button-keyboard-shortcut-hint {
|
||||
@apply border-[var(--dark-text)]/10 text-[var(--dark-text)]/50;
|
||||
}
|
||||
|
||||
.note-action-icon-button {
|
||||
@apply flex h-9 w-9 items-center justify-center rounded-lg border border-[var(--light-text)]/10 bg-transparent p-0 text-[var(--light-text)] hover:bg-[var(--light-foreground)]/80;
|
||||
}
|
||||
@ -700,7 +708,7 @@
|
||||
@apply list-decimal;
|
||||
}
|
||||
|
||||
/* Checkboxes ('todo items') */
|
||||
/* Checkboxes */
|
||||
|
||||
.markdown-preview input[type="checkbox"] {
|
||||
@apply mr-1 h-4 w-4 appearance-none rounded-md border-[var(--light-text)]/30 bg-[var(--light-foreground)] p-0 align-text-bottom;
|
||||
|
@ -18,8 +18,7 @@
|
||||
let passwordError = ""
|
||||
let isFormValid = false
|
||||
|
||||
// TODO (optional feature):
|
||||
// during `onMount`, check whether user is *already authenticated* (redirect straight to `/` if yes)
|
||||
// TODO (optional): route already authenticated users straight to "/"
|
||||
|
||||
// clear any errors when swapping between login/signup views
|
||||
onMount(() => ($cError = null))
|
||||
|
@ -191,19 +191,20 @@
|
||||
</h1>
|
||||
{/if}
|
||||
|
||||
<!-- TODO: add pagination support for versions dropdown -->
|
||||
<!-- TODO: handle versions pagination support later if it becomes an issue (`util/itemPagination.ts`) -->
|
||||
|
||||
<!-- Note action buttons -->
|
||||
<div class="note-action-container">
|
||||
<!-- Editor mode toggle button -->
|
||||
{#if isLatestVersion(note)}
|
||||
<button on:click={toggleEditMode} class="note-action-button w-28">
|
||||
<button on:click={toggleEditMode} class="note-action-button w-28 sm:w-auto">
|
||||
{#if isEditing}
|
||||
<EditPen classString="mr-3 h-4 w-4" />
|
||||
{:else}
|
||||
<ViewEye classString="mr-3 h-4 w-4" />
|
||||
{/if}
|
||||
{isEditing ? "Editing" : "Viewing"}
|
||||
<span class="button-keyboard-shortcut-hint hidden sm:inline">⌥ + a</span>
|
||||
</button>
|
||||
{:else}
|
||||
<button disabled class="note-action-button w-28 cursor-not-allowed opacity-40"
|
||||
|
@ -33,6 +33,7 @@
|
||||
let username = "friend"
|
||||
let greetMessage = "What's up?"
|
||||
let currentContentHash = ""
|
||||
const keysPressed = new Map()
|
||||
|
||||
onMount(async (): Promise<any> => {
|
||||
// the following fetch attempts to refresh any expired tokens automatically
|
||||
@ -67,10 +68,42 @@
|
||||
isComponentReady = true
|
||||
|
||||
return () => {
|
||||
// listener destructors on unmount
|
||||
window.removeEventListener("resize", handleResize)
|
||||
}
|
||||
})
|
||||
|
||||
const handleKeydown = (event: KeyboardEvent) => {
|
||||
keysPressed.set(event.code, true)
|
||||
|
||||
if (keysPressed.has("AltLeft") && keysPressed.has("KeyE")) {
|
||||
// toggle sidebar with alt/option + e
|
||||
event.preventDefault()
|
||||
isSidebarOpen = !isSidebarOpen
|
||||
} else if (keysPressed.has("AltLeft") && keysPressed.has("KeyN")) {
|
||||
// create new note with alt/option + n
|
||||
event.preventDefault()
|
||||
createNewNote()
|
||||
} else if (isSidebarOpen && keysPressed.has("AltLeft") && keysPressed.has("KeyK")) {
|
||||
// focus to searchbar with alt/option + k
|
||||
event.preventDefault()
|
||||
document.getElementById("sidebar-search-bar")?.focus()
|
||||
} else if ($currentFullNote !== null && keysPressed.has("AltLeft") && keysPressed.has("KeyA")) {
|
||||
// toggle note editor mode with alt/option + e
|
||||
event.preventDefault()
|
||||
isEditing = !isEditing
|
||||
}
|
||||
}
|
||||
|
||||
const handleKeyUp = (_event: KeyboardEvent) => {
|
||||
keysPressed.clear()
|
||||
}
|
||||
|
||||
const handleBlur = () => {
|
||||
// blur = window losing focus
|
||||
keysPressed.clear()
|
||||
}
|
||||
|
||||
const loadNotes = async () => {
|
||||
const notes = await apiClient.listNotes()
|
||||
|
||||
@ -249,6 +282,9 @@
|
||||
})
|
||||
</script>
|
||||
|
||||
<!-- Keyboard shortcuts -->
|
||||
<svelte:window on:keydown={handleKeydown} on:keyup={handleKeyUp} on:blur={handleBlur} />
|
||||
|
||||
{#if isComponentReady}
|
||||
<div class="main-layout-container">
|
||||
<!-- Error notification -->
|
||||
@ -309,18 +345,11 @@
|
||||
<div class="greeting-container">
|
||||
<p class="greeting-message">{greetMessage}</p>
|
||||
<p class="greeting-message text-base">
|
||||
Want to create a new note?<span class="hidden sm:inline"> </span>
|
||||
<span class="block whitespace-nowrap sm:inline"
|
||||
>Click
|
||||
<!-- svelte-ignore a11y_click_events_have_key_events -->
|
||||
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
||||
<span
|
||||
class="greeting-bottom-link"
|
||||
on:click={createNewNote}
|
||||
aria-roledescription="create-note-text-link">here</span
|
||||
></span
|
||||
>
|
||||
<!-- TODO: italic & grayed out text new note creation keyboard shortcut -->
|
||||
<span class="block whitespace-nowrap sm:inline">
|
||||
Press
|
||||
<span class="greeting-bottom-highlight">⌥ + n</span>
|
||||
to create a new note
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
{/if}
|
||||
|
@ -156,7 +156,13 @@
|
||||
</div>
|
||||
|
||||
<div class="relative flex flex-col px-2 pb-3">
|
||||
<input type="text" placeholder="Search" bind:value={searchQuery} class="sidebar-search-bar" />
|
||||
<input
|
||||
type="text"
|
||||
placeholder="⌥ + k"
|
||||
bind:value={searchQuery}
|
||||
id="sidebar-search-bar"
|
||||
class="sidebar-search-bar"
|
||||
/>
|
||||
<Search classString="sidebar-search-bar-icon" />
|
||||
</div>
|
||||
|
||||
|
@ -2,8 +2,6 @@
|
||||
export let classString = "h-6 w-6"
|
||||
</script>
|
||||
|
||||
<!-- TODO: this needs to be fixed :) -->
|
||||
|
||||
<svg
|
||||
viewBox="0 -4 20 20"
|
||||
class={classString}
|
||||
|
Loading…
x
Reference in New Issue
Block a user