From 41ce56e870e23fa078cacdc5b055edd1a6944950 Mon Sep 17 00:00:00 2001 From: tglide <26071571+TGlide@users.noreply.github.com> Date: Tue, 16 May 2023 08:25:06 +0100 Subject: [PATCH 1/8] fix: onboarding missing shortcus --- src/lib/helpers/platform.ts | 1 - .../console/project-[project]/databases/+page.svelte | 4 ++++ .../console/project-[project]/overview/onboard.svelte | 3 +++ .../project-[project]/overview/platforms/+page.svelte | 8 +++++++- 4 files changed, 14 insertions(+), 2 deletions(-) diff --git a/src/lib/helpers/platform.ts b/src/lib/helpers/platform.ts index e03708a01..98920c7c6 100644 --- a/src/lib/helpers/platform.ts +++ b/src/lib/helpers/platform.ts @@ -1,4 +1,3 @@ export function isMac(): boolean { - console.log('isMac()', window.navigator.platform.toUpperCase().indexOf('MAC') >= 0); return window.navigator.platform.toUpperCase().indexOf('MAC') >= 0; } diff --git a/src/routes/console/project-[project]/databases/+page.svelte b/src/routes/console/project-[project]/databases/+page.svelte index 6295c5474..dc3eb69ca 100644 --- a/src/routes/console/project-[project]/databases/+page.svelte +++ b/src/routes/console/project-[project]/databases/+page.svelte @@ -12,6 +12,7 @@ import Grid from './grid.svelte'; import { columns } from './store'; import Table from './table.svelte'; + import { commandCenter } from '$lib/helpers/commandCenter'; export let data: PageData; @@ -30,9 +31,12 @@ showCreate = true; }, keys: ['c'], + ctrl: true, disabled: showCreate } ]); + + $: $commandCenter.enabled = !showCreate; diff --git a/src/routes/console/project-[project]/overview/onboard.svelte b/src/routes/console/project-[project]/overview/onboard.svelte index 65b657852..016280d7e 100644 --- a/src/routes/console/project-[project]/overview/onboard.svelte +++ b/src/routes/console/project-[project]/overview/onboard.svelte @@ -9,6 +9,7 @@ import { app } from '$lib/stores/app'; import { wizard } from '$lib/stores/wizard'; import Wizard from './keys/wizard.svelte'; + import { registerProjectCommand } from '../store'; export let projectId: string; @@ -42,6 +43,8 @@ $: onBoardIntro = $app.themeInUse === 'dark' ? OnboardDarkIntro : OnboardLightIntro; $: onBoardImage1 = $app.themeInUse === 'dark' ? OnboardDark1 : OnboardLight1; $: onBoardImage2 = $app.themeInUse === 'dark' ? OnboardDark2 : OnboardLight2; + + $: $registerProjectCommand();
diff --git a/src/routes/console/project-[project]/overview/platforms/+page.svelte b/src/routes/console/project-[project]/overview/platforms/+page.svelte index 2c5fac9a1..4536d6d86 100644 --- a/src/routes/console/project-[project]/overview/platforms/+page.svelte +++ b/src/routes/console/project-[project]/overview/platforms/+page.svelte @@ -72,7 +72,13 @@ } }; - $: $registerProjectCommand(); + $: $registerProjectCommand([ + { + label: 'Create Web App', + callback: () => addPlatform(Platform.Web), + keys: ['c'] + } + ]);
From bf77043c8e5cdf5a52eb855658304c72a779007d Mon Sep 17 00:00:00 2001 From: tglide <26071571+TGlide@users.noreply.github.com> Date: Tue, 16 May 2023 09:04:48 +0100 Subject: [PATCH 2/8] fix: disabling commands --- src/lib/components/dialog.svelte | 1 + src/lib/helpers/commandCenter.ts | 77 +++++++++++++------ src/routes/console/commandCenter.svelte | 6 +- .../project-[project]/databases/+page.svelte | 5 +- 4 files changed, 59 insertions(+), 30 deletions(-) diff --git a/src/lib/components/dialog.svelte b/src/lib/components/dialog.svelte index aa055883e..6578ebef9 100644 --- a/src/lib/components/dialog.svelte +++ b/src/lib/components/dialog.svelte @@ -7,6 +7,7 @@ export let error: string = null; export let closable = true; export let headerDivider = true; + export let animate = true; let dialog: HTMLDialogElement; let alert: HTMLElement; diff --git a/src/lib/helpers/commandCenter.ts b/src/lib/helpers/commandCenter.ts index a5196b241..1289b6f57 100644 --- a/src/lib/helpers/commandCenter.ts +++ b/src/lib/helpers/commandCenter.ts @@ -1,5 +1,6 @@ import { derived, writable } from 'svelte/store'; import { debounce } from './debounce'; +import { isMac } from './platform'; export type Command = { keys: string[]; @@ -16,18 +17,23 @@ export type Command = { type CommandCenterState = { commandMap: Map; - enabled: boolean; + disabledMap: Map; }; export const commandCenter = writable({ commandMap: new Map(), - enabled: true + disabledMap: new Map() }); export const commands = derived(commandCenter, ($commandCenter) => { return Array.from($commandCenter.commandMap.values()).flat(); }); +const commandsEnabled = derived(commandCenter, ($commandCenter) => { + // If there's an item on the disabledMap that's true, then disable the command center + return Array.from($commandCenter.disabledMap.values()).every((disabled) => !disabled); +}); + export const registerCommand = { subscribe(runner: (cb: (newCommands: Command[]) => void) => void) { const uuid = crypto.randomUUID(); @@ -48,33 +54,56 @@ export const registerCommand = { } }; -export const commandCenterKeyDownHandler = derived(commandCenter, ({ commandMap, enabled }) => { - const commandsArr = Array.from(commandMap.values()).flat(); - const recentKeyCodes = new Set(); +export const disableCommands = { + subscribe(runner: (cb: (disabled: boolean) => void) => void) { + const uuid = crypto.randomUUID(); - return (event: KeyboardEvent) => { - const isMac = navigator.platform.toUpperCase().indexOf('MAC') >= 0; + runner((disabled: boolean) => { + commandCenter.update((curr) => { + curr.disabledMap.set(uuid, disabled); + return curr; + }); + }); - recentKeyCodes.add(event.keyCode); - debounce(() => recentKeyCodes.clear(), 1000)(); + return () => { + commandCenter.update((curr) => { + curr.disabledMap.delete(uuid); + return curr; + }); + }; + } +}; - for (const command of commandsArr) { - if (command.disabled || (!enabled && !command.forceEnable)) continue; +export const commandCenterKeyDownHandler = derived( + [commandCenter, commandsEnabled], + ([{ commandMap }, enabled]) => { + const commandsArr = Array.from(commandMap.values()).flat(); + const recentKeyCodes = new Set(); - const { keys, ctrl: meta, shift, alt, callback } = command; + return (event: KeyboardEvent) => { + recentKeyCodes.add(event.keyCode); + debounce(() => recentKeyCodes.clear(), 1000)(); - const isMetaPressed = meta ? (isMac ? event.metaKey : event.ctrlKey) : true; - const isShiftPressed = shift ? event.shiftKey : true; - const isAltPressed = alt ? event.altKey : true; + for (const command of commandsArr) { + if (command.disabled || (!enabled && !command.forceEnable)) continue; - const commandKeyCodes = keys.map((key) => key.toUpperCase().charCodeAt(0)); - const allKeysPressed = commandKeyCodes.every((keyCode) => recentKeyCodes.has(keyCode)); + const { keys, ctrl: meta, shift, alt, callback } = command; - if (allKeysPressed && isMetaPressed && isShiftPressed && isAltPressed) { - event.preventDefault(); - callback(); - return; + const isMetaPressed = meta ? (isMac() ? event.metaKey : event.ctrlKey) : true; + const isShiftPressed = shift ? event.shiftKey : true; + const isAltPressed = alt ? event.altKey : true; + + const commandKeyCodes = keys.map((key) => key.toUpperCase().charCodeAt(0)); + const allKeysPressed = commandKeyCodes.every((keyCode) => + recentKeyCodes.has(keyCode) + ); + + if (allKeysPressed && isMetaPressed && isShiftPressed && isAltPressed) { + event.preventDefault(); + callback(); + return; + } } - } - }; -}); + }; + } +); diff --git a/src/routes/console/commandCenter.svelte b/src/routes/console/commandCenter.svelte index e39c113ca..e1857455d 100644 --- a/src/routes/console/commandCenter.svelte +++ b/src/routes/console/commandCenter.svelte @@ -1,6 +1,6 @@ @@ -98,7 +98,7 @@ } .result { - border-radius: 0.25rem; + border-radius: 0.75rem; padding: 0.5rem 0.75rem; } diff --git a/src/routes/console/project-[project]/databases/+page.svelte b/src/routes/console/project-[project]/databases/+page.svelte index dc3eb69ca..b76e9a840 100644 --- a/src/routes/console/project-[project]/databases/+page.svelte +++ b/src/routes/console/project-[project]/databases/+page.svelte @@ -4,6 +4,7 @@ import { page } from '$app/stores'; import { Empty, PaginationWithLimit } from '$lib/components'; import { Button } from '$lib/elements/forms'; + import { disableCommands } from '$lib/helpers/commandCenter'; import { Container, GridHeader } from '$lib/layout'; import type { Models } from '@appwrite.io/console'; import { registerProjectCommand } from '../store'; @@ -12,7 +13,6 @@ import Grid from './grid.svelte'; import { columns } from './store'; import Table from './table.svelte'; - import { commandCenter } from '$lib/helpers/commandCenter'; export let data: PageData; @@ -31,12 +31,11 @@ showCreate = true; }, keys: ['c'], - ctrl: true, disabled: showCreate } ]); - $: $commandCenter.enabled = !showCreate; + $: $disableCommands(showCreate); From adbd2c7f53350757d0f8f71a75f512d2a582d315 Mon Sep 17 00:00:00 2001 From: tglide <26071571+TGlide@users.noreply.github.com> Date: Tue, 16 May 2023 09:19:32 +0100 Subject: [PATCH 3/8] fix: command key order --- src/lib/helpers/commandCenter.ts | 10 +-- src/routes/console/+layout.svelte | 3 +- src/routes/console/commandCenter.svelte | 9 +- .../organization-[organization]/+page.svelte | 4 +- src/routes/console/project-[project]/store.ts | 88 ++++++++++--------- 5 files changed, 60 insertions(+), 54 deletions(-) diff --git a/src/lib/helpers/commandCenter.ts b/src/lib/helpers/commandCenter.ts index 1289b6f57..f1da960f5 100644 --- a/src/lib/helpers/commandCenter.ts +++ b/src/lib/helpers/commandCenter.ts @@ -78,11 +78,11 @@ export const commandCenterKeyDownHandler = derived( [commandCenter, commandsEnabled], ([{ commandMap }, enabled]) => { const commandsArr = Array.from(commandMap.values()).flat(); - const recentKeyCodes = new Set(); + let recentKeyCodes: number[] = []; return (event: KeyboardEvent) => { - recentKeyCodes.add(event.keyCode); - debounce(() => recentKeyCodes.clear(), 1000)(); + recentKeyCodes.push(event.keyCode); + debounce(() => (recentKeyCodes = []), 1000)(); for (const command of commandsArr) { if (command.disabled || (!enabled && !command.forceEnable)) continue; @@ -94,9 +94,7 @@ export const commandCenterKeyDownHandler = derived( const isAltPressed = alt ? event.altKey : true; const commandKeyCodes = keys.map((key) => key.toUpperCase().charCodeAt(0)); - const allKeysPressed = commandKeyCodes.every((keyCode) => - recentKeyCodes.has(keyCode) - ); + const allKeysPressed = recentKeyCodes.join('').includes(commandKeyCodes.join('')); if (allKeysPressed && isMetaPressed && isShiftPressed && isAltPressed) { event.preventDefault(); diff --git a/src/routes/console/+layout.svelte b/src/routes/console/+layout.svelte index f2f21e417..f8511ad3f 100644 --- a/src/routes/console/+layout.svelte +++ b/src/routes/console/+layout.svelte @@ -34,8 +34,7 @@ callback: () => { goto('/console'); }, - keys: ['h'], - alt: true + keys: ['g', 'h'] } ]); diff --git a/src/routes/console/commandCenter.svelte b/src/routes/console/commandCenter.svelte index e1857455d..bbdf7c95f 100644 --- a/src/routes/console/commandCenter.svelte +++ b/src/routes/console/commandCenter.svelte @@ -65,7 +65,7 @@ {command.label} -
+
{#if command.ctrl} {isMac() ? '⌘' : 'ctrl'} {/if} @@ -75,10 +75,15 @@ {#if command.alt} {isMac() ? '⌥' : 'alt'} {/if} - {#each command.keys as key} + {#each command.keys as key, i} + {@const hasNext = command.keys.length - 1 !== i} + {key.toUpperCase()} + {#if hasNext} + then + {/if} {/each}
diff --git a/src/routes/console/organization-[organization]/+page.svelte b/src/routes/console/organization-[organization]/+page.svelte index fecfaae90..ddf821e93 100644 --- a/src/routes/console/organization-[organization]/+page.svelte +++ b/src/routes/console/organization-[organization]/+page.svelte @@ -5,7 +5,7 @@ import { CardContainer, Empty, GridItem1, Heading, PaginationWithLimit } from '$lib/components'; import { Pill } from '$lib/elements'; import { Button } from '$lib/elements/forms'; - import { registerCommand } from '$lib/helpers/commandCenter'; + import { disableCommands, registerCommand } from '$lib/helpers/commandCenter'; import { Container } from '$lib/layout'; import CreateOrganization from '../createOrganization.svelte'; import type { PageData } from './$types'; @@ -63,6 +63,8 @@ disabled: showCreate } ]); + + $: $disableCommands(showCreate); diff --git a/src/routes/console/project-[project]/store.ts b/src/routes/console/project-[project]/store.ts index d932b663f..ed5e12960 100644 --- a/src/routes/console/project-[project]/store.ts +++ b/src/routes/console/project-[project]/store.ts @@ -7,51 +7,53 @@ import { derived, writable, type Readable } from 'svelte/store'; export const project = derived(page, ($page) => $page.data.project as Models.Project); -export const registerProjectCommand = derived([project, registerCommand], ([$project, $register]) => { - return (c: Command[] = []) => - $register([ - ...[ - { - label: 'Go to Overview', - keys: ['o'], - callback: () => { - goto(`/console/project-${$project.$id}`); - } - }, +export const registerProjectCommand = derived( + [project, registerCommand], + ([$project, $register]) => { + return (c: Command[] = []) => { + const projectCommands: Command[] = [ + { + label: 'Go to Overview', + keys: ['g', 'o'], - { - label: 'Go to Auth', - callback: () => { - goto(`/console/project-${$project.$id}/auth`); - }, - keys: ['a'] - }, - { - label: 'Go to Databases', - callback: () => { - goto(`/console/project-${$project.$id}/databases`); - }, - keys: ['d'] - }, - { - label: 'Go to Functions', - callback: () => { - goto(`/console/project-${$project.$id}/functions`); - }, - keys: ['f'] - }, - { - label: 'Go to Storage', - callback: () => { - goto(`/console/project-${$project.$id}/storage`); - }, - keys: ['s'] + callback: () => { + goto(`/console/project-${$project.$id}`); } - ], - ...(c ?? []) - ]) - -}) as Readable<(c?: Command[]) => void>; + }, + + { + label: 'Go to Auth', + callback: () => { + goto(`/console/project-${$project.$id}/auth`); + }, + keys: ['g', 'a'] + }, + { + label: 'Go to Databases', + callback: () => { + goto(`/console/project-${$project.$id}/databases`); + }, + keys: ['g', 'd'] + }, + { + label: 'Go to Functions', + callback: () => { + goto(`/console/project-${$project.$id}/functions`); + }, + keys: ['g', 'f'] + }, + { + label: 'Go to Storage', + callback: () => { + goto(`/console/project-${$project.$id}/storage`); + }, + keys: ['g', 's'] + } + ]; + return $register([...projectCommands, ...(c ?? [])]); + }; + } +) as Readable<(c?: Command[]) => void>; export const onboarding = derived( project, From 46c7a26f901cd087b84ca026943044223ee6c84d Mon Sep 17 00:00:00 2001 From: tglide <26071571+TGlide@users.noreply.github.com> Date: Tue, 16 May 2023 09:47:52 +0100 Subject: [PATCH 4/8] refactor: disable commands on input elements --- src/lib/helpers/commandCenter.ts | 5 +++++ src/routes/console/organization-[organization]/+page.svelte | 4 +--- src/routes/console/project-[project]/databases/+page.svelte | 3 --- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/lib/helpers/commandCenter.ts b/src/lib/helpers/commandCenter.ts index f1da960f5..f84a2b6f6 100644 --- a/src/lib/helpers/commandCenter.ts +++ b/src/lib/helpers/commandCenter.ts @@ -81,6 +81,11 @@ export const commandCenterKeyDownHandler = derived( let recentKeyCodes: number[] = []; return (event: KeyboardEvent) => { + // ignore keypresses that come from input, textarea and select elements + if (['INPUT', 'TEXTAREA', 'SELECT'].includes((event.target as HTMLElement).tagName)) { + return; + } + recentKeyCodes.push(event.keyCode); debounce(() => (recentKeyCodes = []), 1000)(); diff --git a/src/routes/console/organization-[organization]/+page.svelte b/src/routes/console/organization-[organization]/+page.svelte index ddf821e93..fecfaae90 100644 --- a/src/routes/console/organization-[organization]/+page.svelte +++ b/src/routes/console/organization-[organization]/+page.svelte @@ -5,7 +5,7 @@ import { CardContainer, Empty, GridItem1, Heading, PaginationWithLimit } from '$lib/components'; import { Pill } from '$lib/elements'; import { Button } from '$lib/elements/forms'; - import { disableCommands, registerCommand } from '$lib/helpers/commandCenter'; + import { registerCommand } from '$lib/helpers/commandCenter'; import { Container } from '$lib/layout'; import CreateOrganization from '../createOrganization.svelte'; import type { PageData } from './$types'; @@ -63,8 +63,6 @@ disabled: showCreate } ]); - - $: $disableCommands(showCreate); diff --git a/src/routes/console/project-[project]/databases/+page.svelte b/src/routes/console/project-[project]/databases/+page.svelte index b76e9a840..6295c5474 100644 --- a/src/routes/console/project-[project]/databases/+page.svelte +++ b/src/routes/console/project-[project]/databases/+page.svelte @@ -4,7 +4,6 @@ import { page } from '$app/stores'; import { Empty, PaginationWithLimit } from '$lib/components'; import { Button } from '$lib/elements/forms'; - import { disableCommands } from '$lib/helpers/commandCenter'; import { Container, GridHeader } from '$lib/layout'; import type { Models } from '@appwrite.io/console'; import { registerProjectCommand } from '../store'; @@ -34,8 +33,6 @@ disabled: showCreate } ]); - - $: $disableCommands(showCreate); From ea8a92481ca9414f903fc73c02494b7707507b6e Mon Sep 17 00:00:00 2001 From: tglide <26071571+TGlide@users.noreply.github.com> Date: Tue, 16 May 2023 09:57:45 +0100 Subject: [PATCH 5/8] feat: cmd center animation --- src/routes/console/commandCenter.svelte | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/src/routes/console/commandCenter.svelte b/src/routes/console/commandCenter.svelte index bbdf7c95f..80b712773 100644 --- a/src/routes/console/commandCenter.svelte +++ b/src/routes/console/commandCenter.svelte @@ -2,6 +2,8 @@ import Dialog from '$lib/components/dialog.svelte'; import { commands, disableCommands, registerCommand } from '$lib/helpers/commandCenter'; import { isMac } from '$lib/helpers/platform'; + import { bounceOut, elasticOut, quadOut } from 'svelte/easing'; + import { crossfade } from 'svelte/transition'; let open = false; let search = ''; @@ -49,6 +51,11 @@ } $: $disableCommands(open); + + const [send, receive] = crossfade({ + duration: 150, + easing: quadOut + }); @@ -86,6 +93,9 @@ {/if} {/each}
+ {#if selected === i} +
+ {/if} {:else}
  • @@ -103,14 +113,25 @@ } .result { - border-radius: 0.75rem; padding: 0.5rem 0.75rem; + transition: 150ms; + position: relative; } - .result[data-selected] { + .result .bg { + position: absolute; + inset: 0; background-color: hsl(var(--color-neutral-200)); + border-radius: 0.75rem; + translate: 0 -1px; + + z-index: -1; } + /* .result[data-selected] { + background-color: hsl(var(--color-neutral-200)); + } */ + .kbd { padding-inline: 0.25rem; } From 6874ab276ff36a7c6054d054bcee64dc24798bc7 Mon Sep 17 00:00:00 2001 From: tglide <26071571+TGlide@users.noreply.github.com> Date: Tue, 16 May 2023 10:39:11 +0100 Subject: [PATCH 6/8] feat: more command center animations --- src/lib/components/dialog.svelte | 56 +++++++++++++---------- src/routes/console/commandCenter.svelte | 61 +++++++++++++------------ 2 files changed, 65 insertions(+), 52 deletions(-) diff --git a/src/lib/components/dialog.svelte b/src/lib/components/dialog.svelte index 6578ebef9..daa2cb6c0 100644 --- a/src/lib/components/dialog.svelte +++ b/src/lib/components/dialog.svelte @@ -1,6 +1,7 @@ From 7de0a2195d7ee117d6e1ccce70db982da5690f19 Mon Sep 17 00:00:00 2001 From: tglide <26071571+TGlide@users.noreply.github.com> Date: Tue, 16 May 2023 11:24:26 +0100 Subject: [PATCH 8/8] refactor: animation changes --- src/routes/console/commandCenter.svelte | 32 +++++++++++++++++++------ 1 file changed, 25 insertions(+), 7 deletions(-) diff --git a/src/routes/console/commandCenter.svelte b/src/routes/console/commandCenter.svelte index 1735870a1..f2457580a 100644 --- a/src/routes/console/commandCenter.svelte +++ b/src/routes/console/commandCenter.svelte @@ -3,7 +3,7 @@ import Dialog from '$lib/components/dialog.svelte'; import { commands, disableCommands, registerCommand } from '$lib/helpers/commandCenter'; import { isMac } from '$lib/helpers/platform'; - import { bounceOut, elasticOut, quadOut } from 'svelte/easing'; + import { quadOut } from 'svelte/easing'; import { crossfade } from 'svelte/transition'; let open = false; @@ -32,10 +32,18 @@ function handleKeyDown(event: KeyboardEvent) { if (event.key === 'ArrowDown') { event.preventDefault(); - selected = selected === results.length - 1 ? 0 : selected + 1; + if (event.metaKey) { + selected = results.length - 1; + } else { + selected = selected === results.length - 1 ? results.length - 1 : selected + 1; + } } else if (event.key === 'ArrowUp') { event.preventDefault(); - selected = selected === 0 ? results.length - 1 : selected - 1; + if (event.metaKey) { + selected = 0; + } else { + selected = selected === 0 ? 0 : selected - 1; + } } else if (event.key === 'Enter') { event.preventDefault(); if (results[selected]) { @@ -43,6 +51,12 @@ open = false; search = ''; } + } else if (event.key === 'Home') { + event.preventDefault(); + selected = 0; + } else if (event.key === 'End') { + event.preventDefault(); + selected = results.length - 1; } } @@ -122,6 +136,14 @@ padding: 0.5rem 0.75rem; transition: 150ms; position: relative; + + opacity: 0.65; + transition: 75ms cubic-bezier(0.5, 1, 0.89, 1); + } + + .result[data-selected] { + opacity: 1; + transition: 150ms cubic-bezier(0.5, 1, 0.89, 1); } .result .content { @@ -137,10 +159,6 @@ translate: 0 -1px; } - /* .result[data-selected] { - background-color: hsl(var(--color-neutral-200)); - } */ - .kbd { padding-inline: 0.25rem; }