|
|
@@ -0,0 +1,71 @@
|
|
|
+<script lang="ts">
|
|
|
+ import { RefreshCwIcon, Search } from '@lucide/svelte';
|
|
|
+ import type { Snippet } from 'svelte';
|
|
|
+
|
|
|
+ import { Button, buttonVariants } from '$lib/components/controls/button';
|
|
|
+ import * as Dialog from '$lib/components/controls/dialog';
|
|
|
+ import * as InputGroup from '$lib/components/controls/input-group';
|
|
|
+
|
|
|
+ let {
|
|
|
+ open = $bindable(false),
|
|
|
+ title,
|
|
|
+ query = $bindable(''),
|
|
|
+ selected = [],
|
|
|
+ onreload,
|
|
|
+ onsave,
|
|
|
+ content,
|
|
|
+ }: {
|
|
|
+ open?: boolean;
|
|
|
+ title?: string;
|
|
|
+ query?: string;
|
|
|
+ selected?: string[];
|
|
|
+ onreload?: () => void;
|
|
|
+ onsave?: () => void;
|
|
|
+ content: Snippet;
|
|
|
+ } = $props();
|
|
|
+</script>
|
|
|
+
|
|
|
+<Dialog.Root bind:open>
|
|
|
+ <Dialog.Content class="sm:max-w-3xl">
|
|
|
+ <Dialog.Header>
|
|
|
+ <Dialog.Title>{title}</Dialog.Title>
|
|
|
+ </Dialog.Header>
|
|
|
+ <div class="relative -mx-4 -mb-4">
|
|
|
+ <div
|
|
|
+ class="absolute top-0 z-10 w-full border-b border-b-white/10 bg-black/10 px-4 py-2 backdrop-blur-md backdrop-brightness-50"
|
|
|
+ >
|
|
|
+ <div class="flex items-center justify-between">
|
|
|
+ <InputGroup.Root class="max-w-64 flex-1">
|
|
|
+ <InputGroup.Addon>
|
|
|
+ <Search size={14} strokeWidth={2} />
|
|
|
+ </InputGroup.Addon>
|
|
|
+ <InputGroup.Input placeholder="Search by name…" bind:value={query} />
|
|
|
+ </InputGroup.Root>
|
|
|
+ <div class="flex items-center gap-1">
|
|
|
+ {#if onreload}
|
|
|
+ <Button size="icon" variant="ghost" onclick={() => onreload()}>
|
|
|
+ <RefreshCwIcon />
|
|
|
+ </Button>
|
|
|
+ {/if}
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ {#if selected.length > 0}
|
|
|
+ <div class="mt-2 text-xs text-wrap text-white/50">
|
|
|
+ Selected: {selected.join(', ')}
|
|
|
+ </div>
|
|
|
+ {/if}
|
|
|
+ </div>
|
|
|
+ <div class="h-100 overflow-y-auto px-4 pt-20 pb-8">
|
|
|
+ {@render content()}
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ {#if onsave}
|
|
|
+ <Dialog.Footer>
|
|
|
+ <Dialog.Close class={buttonVariants({ variant: 'outline' })} type="button">
|
|
|
+ Cancel
|
|
|
+ </Dialog.Close>
|
|
|
+ <Button type="submit" onclick={() => onsave()}>Save changes</Button>
|
|
|
+ </Dialog.Footer>
|
|
|
+ {/if}
|
|
|
+ </Dialog.Content>
|
|
|
+</Dialog.Root>
|