mirror of
https://github.com/appwrite/console.git
synced 2026-06-06 19:27:48 +00:00
feat: find projects command
This commit is contained in:
@@ -6,6 +6,7 @@ import { derived, writable } from 'svelte/store';
|
||||
export type CommandGroup =
|
||||
| 'ungrouped'
|
||||
| 'navigation'
|
||||
| 'projects'
|
||||
| 'project'
|
||||
| 'auth'
|
||||
| 'help'
|
||||
@@ -16,7 +17,7 @@ export type CommandGroup =
|
||||
| 'storage';
|
||||
|
||||
export type Command = {
|
||||
keys: string[];
|
||||
keys?: string[];
|
||||
/* Ctrl on Windows/Linux, Meta on Mac */
|
||||
ctrl?: boolean;
|
||||
shift?: boolean;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import AI from './ai.svelte';
|
||||
import Root from './root.svelte';
|
||||
import Template from './template.svelte';
|
||||
export { AI, Root, Template };
|
||||
export { default as AI } from './ai.svelte';
|
||||
export { default as Root } from './root.svelte';
|
||||
export { default as Template } from './template.svelte';
|
||||
export { default as Projects } from './projects.svelte';
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
<!--
|
||||
This is the root command panel. It precedes all other command panels.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { page } from '$app/stores';
|
||||
import type { Models } from '@appwrite.io/console';
|
||||
import Template from './template.svelte';
|
||||
import { goto } from '$app/navigation';
|
||||
|
||||
let search = '';
|
||||
|
||||
$: projects = $page.data.projects as Models.ProjectList;
|
||||
$: filteredProjects = projects.projects
|
||||
.filter((project) => project.name.toLowerCase().includes(search.toLowerCase()))
|
||||
.map((project) => {
|
||||
return {
|
||||
label: project.name,
|
||||
callback: () => {
|
||||
goto(`/console/project-${project.$id}`);
|
||||
},
|
||||
group: 'projects'
|
||||
} as const;
|
||||
});
|
||||
</script>
|
||||
|
||||
<Template options={filteredProjects} bind:search>
|
||||
<div class="option" slot="option" let:option>{option.label}</div>
|
||||
</Template>
|
||||
@@ -75,7 +75,7 @@
|
||||
] as Array<Command | Omit<Command, 'keys'>>;
|
||||
</script>
|
||||
|
||||
<Template options={results} bind:search>
|
||||
<Template options={results} bind:search searchPlaceholder="Search for commands or content...">
|
||||
<div slot="option" class="u-flex u-main-space-between content" let:option={command}>
|
||||
<div class="u-flex u-gap-8 u-cross-center">
|
||||
<i class="icon-{command.icon ?? 'arrow-sm-right'}" />
|
||||
@@ -85,13 +85,13 @@
|
||||
</div>
|
||||
<div class="u-flex u-gap-4 u-cross-center">
|
||||
{#if command.ctrl}
|
||||
<kbd class="kbd"> {isMac() ? '⌘' : 'ctrl'} </kbd>
|
||||
<kbd class="kbd"> {isMac() ? '⌘' : 'Ctrl'} </kbd>
|
||||
{/if}
|
||||
{#if command.shift}
|
||||
<kbd class="kbd"> {isMac() ? '⇧' : 'shift'} </kbd>
|
||||
<kbd class="kbd"> {isMac() ? '⇧' : 'Shift'} </kbd>
|
||||
{/if}
|
||||
{#if command.alt}
|
||||
<kbd class="kbd"> {isMac() ? '⌥' : 'alt'} </kbd>
|
||||
<kbd class="kbd"> {isMac() ? '⌥' : 'Alt'} </kbd>
|
||||
{/if}
|
||||
{#if 'keys' in command}
|
||||
{#each command.keys as key, i}
|
||||
@@ -108,12 +108,4 @@
|
||||
</div>
|
||||
</div>
|
||||
<svelte:fragment slot="no-options">No commands found</svelte:fragment>
|
||||
<div slot="footer" class="footer u-flex u-flex u-cross-center u-main-space-between">
|
||||
<div class="u-flex u-cross-center u-gap-4">
|
||||
<kbd class="kbd">Enter</kbd> <span>to select and close</span>
|
||||
</div>
|
||||
<div class="u-flex u-cross-center u-gap-4">
|
||||
<kbd class="kbd">Esc</kbd> <span>to close</span>
|
||||
</div>
|
||||
</div>
|
||||
</Template>
|
||||
|
||||
@@ -17,9 +17,11 @@
|
||||
type Option = $$Generic<BaseOption>;
|
||||
export let options: Option[] | null = null;
|
||||
export let search = '';
|
||||
export let searchPlaceholder = 'Search...';
|
||||
export let fullheight = false;
|
||||
|
||||
let selected = 0;
|
||||
let contentEl: HTMLElement;
|
||||
|
||||
function triggerOption(option: Option) {
|
||||
const prevPanels = $subPanels.length;
|
||||
@@ -60,9 +62,22 @@
|
||||
|
||||
tick().then(() => {
|
||||
if (!cardEl) return;
|
||||
const selectedEl = cardEl.querySelector('[data-selected]');
|
||||
|
||||
if (selectedEl) {
|
||||
const resultEls = Array.from(contentEl.querySelectorAll('.result'));
|
||||
const selectedEl = contentEl.querySelector('[data-selected]');
|
||||
const selectedIdx = resultEls.indexOf(selectedEl);
|
||||
|
||||
if (selectedIdx === 0) {
|
||||
contentEl.scrollTo({
|
||||
top: 0,
|
||||
behavior: 'smooth'
|
||||
});
|
||||
} else if (selectedIdx === resultEls.length - 1) {
|
||||
contentEl.scrollTo({
|
||||
top: contentEl.scrollHeight,
|
||||
behavior: 'smooth'
|
||||
});
|
||||
} else if (selectedEl) {
|
||||
selectedEl.scrollIntoView({
|
||||
block: 'nearest',
|
||||
behavior: 'smooth'
|
||||
@@ -83,7 +98,7 @@
|
||||
}
|
||||
|
||||
const [send, receive] = crossfade({
|
||||
duration: 150,
|
||||
duration: 100,
|
||||
easing: quadOut
|
||||
});
|
||||
|
||||
@@ -173,16 +188,13 @@
|
||||
|
||||
<slot name="search">
|
||||
<div class="u-flex default-search u-width-full-line">
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Search for commands..."
|
||||
autofocus
|
||||
bind:value={search} />
|
||||
<!-- svelte-ignore a11y-autofocus -->
|
||||
<input type="text" placeholder={searchPlaceholder} autofocus bind:value={search} />
|
||||
</div>
|
||||
</slot>
|
||||
</div>
|
||||
|
||||
<div class="content">
|
||||
<div class="content" bind:this={contentEl}>
|
||||
{#if groupsAndOptions}
|
||||
<ul class="options">
|
||||
{#each groupsAndOptions as item}
|
||||
@@ -222,7 +234,17 @@
|
||||
</div>
|
||||
|
||||
<div class="footer">
|
||||
<slot name="footer" />
|
||||
<slot name="footer">
|
||||
<div class=" u-flex u-flex u-cross-center u-main-space-between">
|
||||
<div class="u-flex u-cross-center u-gap-4">
|
||||
<kbd class="kbd">Enter</kbd> <span>to select</span>
|
||||
</div>
|
||||
<div class="u-flex u-cross-center u-gap-4">
|
||||
<kbd class="kbd">Esc</kbd>
|
||||
<span>to {$subPanels.length > 1 ? 'go back' : 'close'}</span>
|
||||
</div>
|
||||
</div>
|
||||
</slot>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -347,23 +369,18 @@
|
||||
.group {
|
||||
color: hsl(var(--color-neutral-70));
|
||||
margin-inline-start: 0.25rem;
|
||||
margin-block-start: 1rem;
|
||||
margin-block-end: 0.25rem;
|
||||
position: relative;
|
||||
z-index: 10;
|
||||
|
||||
&:not(:first-child) {
|
||||
margin-block-start: 1rem;
|
||||
}
|
||||
}
|
||||
|
||||
.result {
|
||||
transition: 150ms;
|
||||
position: relative;
|
||||
|
||||
opacity: 0.65;
|
||||
transition: 75ms cubic-bezier(0.5, 1, 0.89, 1);
|
||||
&[data-selected] {
|
||||
opacity: 1;
|
||||
transition: 150ms cubic-bezier(0.5, 1, 0.89, 1);
|
||||
}
|
||||
|
||||
.bg {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
|
||||
@@ -17,9 +17,9 @@
|
||||
|
||||
import { goto } from '$app/navigation';
|
||||
|
||||
import { addSubPanel } from '$lib/commandCenter/subPanels';
|
||||
import { registerCommands, CommandCenter } from '$lib/commandCenter';
|
||||
import { CommandCenter, registerCommands } from '$lib/commandCenter';
|
||||
import { AI } from '$lib/commandCenter/panels';
|
||||
import { addSubPanel } from '$lib/commandCenter/subPanels';
|
||||
|
||||
$: $registerCommands([
|
||||
{
|
||||
@@ -38,9 +38,7 @@
|
||||
callback: () => {
|
||||
goto('/console/account');
|
||||
},
|
||||
keys: ['a'],
|
||||
ctrl: true,
|
||||
shift: true,
|
||||
keys: ['g', 'i'],
|
||||
group: 'navigation'
|
||||
},
|
||||
{
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { sdk } from '$lib/stores/sdk';
|
||||
|
||||
import type { LayoutLoad } from './$types';
|
||||
|
||||
export const load: LayoutLoad = async () => {
|
||||
@@ -6,6 +7,9 @@ export const load: LayoutLoad = async () => {
|
||||
const data = await response.json();
|
||||
|
||||
return {
|
||||
version: data?.version ?? null
|
||||
version: data?.version ?? null,
|
||||
promised: {
|
||||
// We nest these so that layout doesn't have to wait for these promises to load
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
<script lang="ts">
|
||||
import { goto } from '$app/navigation';
|
||||
import { base } from '$app/paths';
|
||||
import { page } from '$app/stores';
|
||||
import { type Command, registerCommands } from '$lib/commandCenter';
|
||||
import { addSubPanel, registerCommands } from '$lib/commandCenter';
|
||||
import { Projects } from '$lib/commandCenter/panels';
|
||||
import { CardContainer, Empty, GridItem1, Heading, PaginationWithLimit } from '$lib/components';
|
||||
import { Pill } from '$lib/elements';
|
||||
import { Button } from '$lib/elements/forms';
|
||||
@@ -48,14 +48,17 @@
|
||||
}
|
||||
|
||||
$: $registerCommands([
|
||||
...data.projects.projects.map<Command>((project, i) => ({
|
||||
label: `Project ${project.name}`,
|
||||
{
|
||||
label: 'Find a project',
|
||||
callback: () => {
|
||||
goto(`/console/project-${project.$id}`);
|
||||
addSubPanel({
|
||||
name: 'Projects',
|
||||
component: Projects
|
||||
});
|
||||
},
|
||||
keys: ['p', `${i + 1}`],
|
||||
group: 'project'
|
||||
})),
|
||||
group: 'project',
|
||||
icon: 'search'
|
||||
},
|
||||
{
|
||||
label: 'Create project',
|
||||
callback: () => {
|
||||
|
||||
@@ -58,6 +58,14 @@
|
||||
},
|
||||
keys: ['g', 's'],
|
||||
group: 'navigation'
|
||||
},
|
||||
{
|
||||
label: 'Go to Settings',
|
||||
callback: () => {
|
||||
goto(`/console/project-${$project.$id}/settings`);
|
||||
},
|
||||
keys: ['g', 'p'],
|
||||
group: 'navigation'
|
||||
}
|
||||
]);
|
||||
</script>
|
||||
|
||||
@@ -36,7 +36,6 @@
|
||||
import { toLocaleDateTime } from '$lib/helpers/date';
|
||||
import { app } from '$lib/stores/app';
|
||||
import type { PageData } from './$types';
|
||||
import { registerCommands } from '$lib/commandCenter';
|
||||
|
||||
export let data: PageData;
|
||||
|
||||
@@ -72,14 +71,14 @@
|
||||
}
|
||||
};
|
||||
|
||||
$: $registerCommands([
|
||||
{
|
||||
label: 'Create Web App',
|
||||
callback: () => addPlatform(Platform.Web),
|
||||
keys: ['c'],
|
||||
icon: 'plus'
|
||||
}
|
||||
]);
|
||||
// $: $registerCommands([
|
||||
// {
|
||||
// label: 'Create Web App',
|
||||
// callback: () => addPlatform(Platform.Web),
|
||||
// keys: ['c'],
|
||||
// icon: 'plus'
|
||||
// }
|
||||
// ]);
|
||||
</script>
|
||||
|
||||
<div class="common-section u-flex u-gap-12">
|
||||
|
||||
Reference in New Issue
Block a user