show the stack icon and name in stack action popups
This commit is contained in:
5 files changed
+29
-16
No files matched your search
@@ -22,6 +22,8 @@
|
|||||||
onOpenChange?: (open: boolean) => void;
|
onOpenChange?: (open: boolean) => void;
|
||||||
children: Snippet<[{ open: boolean }]>;
|
children: Snippet<[{ open: boolean }]>;
|
||||||
extraContent?: Snippet;
|
extraContent?: Snippet;
|
||||||
|
/** Optional leading glyph rendered before the confirm text (e.g. a stack icon). */
|
||||||
|
icon?: Snippet;
|
||||||
}
|
}
|
||||||
|
|
||||||
let {
|
let {
|
||||||
@@ -39,7 +41,8 @@
|
|||||||
onConfirm,
|
onConfirm,
|
||||||
onOpenChange,
|
onOpenChange,
|
||||||
children,
|
children,
|
||||||
extraContent
|
extraContent,
|
||||||
|
icon
|
||||||
}: Props = $props();
|
}: Props = $props();
|
||||||
|
|
||||||
const triggerClass = $derived(unstyled
|
const triggerClass = $derived(unstyled
|
||||||
@@ -109,7 +112,8 @@
|
|||||||
>
|
>
|
||||||
<div class="flex flex-col gap-1.5">
|
<div class="flex flex-col gap-1.5">
|
||||||
<div class="flex items-center gap-2">
|
<div class="flex items-center gap-2">
|
||||||
<span class="text-xs whitespace-nowrap">{action} {itemType} {#if displayName}<strong>{displayName}</strong>{/if}?</span>
|
{#if icon}{@render icon()}{/if}
|
||||||
|
<span class="text-xs whitespace-nowrap">{action} {itemType} {#if displayName}<strong class="font-semibold text-foreground">{displayName}</strong>{/if}?</span>
|
||||||
<Button size="sm" {variant} class="h-6 px-2 text-xs" onclick={handleConfirm}>
|
<Button size="sm" {variant} class="h-6 px-2 text-xs" onclick={handleConfirm}>
|
||||||
{confirmText}
|
{confirmText}
|
||||||
</Button>
|
</Button>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { Box } from 'lucide-svelte';
|
import { Box, Layers } from 'lucide-svelte';
|
||||||
import { getStackIconComponent, isSelfhstIcon, selfhstRef, isCustomIcon } from '$lib/utils/icons';
|
import { getStackIconComponent, isSelfhstIcon, selfhstRef, isCustomIcon } from '$lib/utils/icons';
|
||||||
import { selfhstIcons, requestSelfhst } from '$lib/stores/selfhst-icons';
|
import { selfhstIcons, requestSelfhst } from '$lib/stores/selfhst-icons';
|
||||||
|
|
||||||
@@ -23,8 +23,12 @@
|
|||||||
|
|
||||||
const selfhst = $derived(selfhstRef(icon));
|
const selfhst = $derived(selfhstRef(icon));
|
||||||
const custom = $derived(isCustomIcon(icon));
|
const custom = $derived(isCustomIcon(icon));
|
||||||
// Lucide is the fallback for any non-selfhst, non-custom value (incl. null).
|
// A named lucide icon resolves via the map; an EMPTY/null value falls back to Layers -
|
||||||
const LucideIcon = $derived(!isSelfhstIcon(icon) && !custom ? getStackIconComponent(icon || '') : null);
|
// the generic stack glyph used everywhere else in the stacks UI (page header, tiles,
|
||||||
|
// list). getStackIconComponent's own default is Boxes, which reads as a different icon.
|
||||||
|
const LucideIcon = $derived(
|
||||||
|
!isSelfhstIcon(icon) && !custom ? (icon ? getStackIconComponent(icon) : Layers) : null
|
||||||
|
);
|
||||||
|
|
||||||
// Resolve the selfh.st icon via the shared batch store (one request for the whole
|
// Resolve the selfh.st icon via the shared batch store (one request for the whole
|
||||||
// list) instead of a per-icon <img src>. `resolved`: data URI when ready, '' when it
|
// list) instead of a per-icon <img src>. `resolved`: data URI when ready, '' when it
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
import { Checkbox } from '$lib/components/ui/checkbox';
|
import { Checkbox } from '$lib/components/ui/checkbox';
|
||||||
import { Trash2, Folder, Database, Loader2, ArrowRight } from 'lucide-svelte';
|
import { Trash2, Folder, Database, Loader2, ArrowRight } from 'lucide-svelte';
|
||||||
import GitGenericIcon from '$lib/components/icons/GitGenericIcon.svelte';
|
import GitGenericIcon from '$lib/components/icons/GitGenericIcon.svelte';
|
||||||
|
import StackIcon from '$lib/components/StackIcon.svelte';
|
||||||
import { appendEnvParam } from '$lib/stores/environment';
|
import { appendEnvParam } from '$lib/stores/environment';
|
||||||
|
|
||||||
// The parent owns the fetch; onConfirm receives what the user chose to remove.
|
// The parent owns the fetch; onConfirm receives what the user chose to remove.
|
||||||
@@ -11,11 +12,13 @@
|
|||||||
open = $bindable(false),
|
open = $bindable(false),
|
||||||
stackName = '',
|
stackName = '',
|
||||||
envId = null as number | null,
|
envId = null as number | null,
|
||||||
|
stackIcon = null as string | null,
|
||||||
onConfirm,
|
onConfirm,
|
||||||
}: {
|
}: {
|
||||||
open?: boolean;
|
open?: boolean;
|
||||||
stackName?: string;
|
stackName?: string;
|
||||||
envId?: number | null;
|
envId?: number | null;
|
||||||
|
stackIcon?: string | null;
|
||||||
onConfirm: (opts: { deleteFiles: boolean; deleteVolumes: boolean }) => void | Promise<void>;
|
onConfirm: (opts: { deleteFiles: boolean; deleteVolumes: boolean }) => void | Promise<void>;
|
||||||
} = $props();
|
} = $props();
|
||||||
|
|
||||||
@@ -90,7 +93,7 @@
|
|||||||
<Dialog.Content class="max-w-2xl">
|
<Dialog.Content class="max-w-2xl">
|
||||||
<Dialog.Header>
|
<Dialog.Header>
|
||||||
<Dialog.Title class="flex items-center gap-2">
|
<Dialog.Title class="flex items-center gap-2">
|
||||||
<Trash2 class="w-5 h-5 text-destructive" />
|
<StackIcon icon={stackIcon} {stackName} {envId} class="w-5 h-5 shrink-0 text-muted-foreground" />
|
||||||
Remove stack "{stackName}"
|
Remove stack "{stackName}"
|
||||||
</Dialog.Title>
|
</Dialog.Title>
|
||||||
<Dialog.Description>
|
<Dialog.Description>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { Loader2, CheckCircle2, XCircle, Rocket, Layers } from 'lucide-svelte';
|
import { Loader2, CheckCircle2, XCircle, Rocket } from 'lucide-svelte';
|
||||||
import StackIcon from '$lib/components/StackIcon.svelte';
|
import StackIcon from '$lib/components/StackIcon.svelte';
|
||||||
import EnvironmentIcon from '$lib/components/EnvironmentIcon.svelte';
|
import EnvironmentIcon from '$lib/components/EnvironmentIcon.svelte';
|
||||||
import { environments } from '$lib/stores/environment';
|
import { environments } from '$lib/stores/environment';
|
||||||
@@ -13,8 +13,7 @@
|
|||||||
// The action word, e.g. "Bringing down" or "Git deploy".
|
// The action word, e.g. "Bringing down" or "Git deploy".
|
||||||
verb: string;
|
verb: string;
|
||||||
stackName?: string;
|
stackName?: string;
|
||||||
// Icon value (lucide/selfhst/custom). When unset, a generic Layers glyph shows --
|
// Icon value (lucide/selfhst/custom). When unset, StackIcon shows the generic Layers glyph.
|
||||||
// never StackIcon's Boxes fallback, which reads as a different icon.
|
|
||||||
stackIcon?: string | null;
|
stackIcon?: string | null;
|
||||||
envId?: number | null;
|
envId?: number | null;
|
||||||
// Drives the leading icon. 'idle' shows a rocket (a not-yet-started deploy prompt).
|
// Drives the leading icon. 'idle' shows a rocket (a not-yet-started deploy prompt).
|
||||||
@@ -41,11 +40,7 @@
|
|||||||
{/if}
|
{/if}
|
||||||
<span class="shrink-0 font-medium">{verb}</span>
|
<span class="shrink-0 font-medium">{verb}</span>
|
||||||
{#if stackName}
|
{#if stackName}
|
||||||
{#if stackIcon}
|
<StackIcon icon={stackIcon} {stackName} {envId} class="{iconClass} shrink-0 text-muted-foreground" />
|
||||||
<StackIcon icon={stackIcon} {stackName} {envId} class="{iconClass} shrink-0 text-muted-foreground" />
|
|
||||||
{:else}
|
|
||||||
<Layers class="{iconClass} shrink-0 text-muted-foreground" />
|
|
||||||
{/if}
|
|
||||||
<span class="truncate font-medium">{stackName}</span>
|
<span class="truncate font-medium">{stackName}</span>
|
||||||
{/if}
|
{/if}
|
||||||
{#if env}
|
{#if env}
|
||||||
|
|||||||
@@ -6,10 +6,13 @@
|
|||||||
import { Loader2 } from 'lucide-svelte';
|
import { Loader2 } from 'lucide-svelte';
|
||||||
import type { Snippet } from 'svelte';
|
import type { Snippet } from 'svelte';
|
||||||
import { cn } from '$lib/utils';
|
import { cn } from '$lib/utils';
|
||||||
|
import StackIcon from '$lib/components/StackIcon.svelte';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
stackName: string;
|
stackName: string;
|
||||||
envId: number | null;
|
envId: number | null;
|
||||||
|
/** The stack's icon value (custom/selfhst/lucide/null); shown in the header. */
|
||||||
|
stackIcon?: string | null;
|
||||||
disabled?: boolean;
|
disabled?: boolean;
|
||||||
side?: 'top' | 'bottom';
|
side?: 'top' | 'bottom';
|
||||||
align?: 'start' | 'center' | 'end';
|
align?: 'start' | 'center' | 'end';
|
||||||
@@ -47,6 +50,7 @@
|
|||||||
let {
|
let {
|
||||||
stackName,
|
stackName,
|
||||||
envId,
|
envId,
|
||||||
|
stackIcon = null,
|
||||||
disabled = false,
|
disabled = false,
|
||||||
side = 'top',
|
side = 'top',
|
||||||
align = 'end',
|
align = 'end',
|
||||||
@@ -110,13 +114,16 @@
|
|||||||
{/snippet}
|
{/snippet}
|
||||||
</Popover.Trigger>
|
</Popover.Trigger>
|
||||||
<Popover.Content
|
<Popover.Content
|
||||||
class="w-56 p-3 z-[200]"
|
class="w-72 p-3 z-[200]"
|
||||||
{side}
|
{side}
|
||||||
{align}
|
{align}
|
||||||
sideOffset={8}
|
sideOffset={8}
|
||||||
>
|
>
|
||||||
<div class="space-y-3">
|
<div class="space-y-3">
|
||||||
<p class="text-xs font-medium">Redeploy stack</p>
|
<p class="flex items-center gap-1.5 text-xs text-muted-foreground">
|
||||||
|
<StackIcon icon={stackIcon} {stackName} {envId} class="w-4 h-4 shrink-0" />
|
||||||
|
<span class="truncate">Redeploy stack <strong class="font-semibold text-foreground">{stackName}</strong></span>
|
||||||
|
</p>
|
||||||
<div class="space-y-2">
|
<div class="space-y-2">
|
||||||
<label class="flex items-center gap-2 cursor-pointer">
|
<label class="flex items-center gap-2 cursor-pointer">
|
||||||
<Checkbox bind:checked={pull} disabled={deploying} />
|
<Checkbox bind:checked={pull} disabled={deploying} />
|
||||||
|
|||||||
Reference in new issue
Block a user