mirror of
https://github.com/immich-app/static-pages.git
synced 2026-09-30 13:23:05 +08:00
feat: action link (#769)
This commit is contained in:
@@ -7,6 +7,7 @@ import {
|
||||
toastManager,
|
||||
type ActionItem,
|
||||
type ActionItemHandler,
|
||||
type ActionLink,
|
||||
type CarouselImageItem,
|
||||
type IconLike,
|
||||
type MenuItems,
|
||||
@@ -64,6 +65,7 @@ import {
|
||||
mdiMessageAlert,
|
||||
mdiMessageAlertOutline,
|
||||
mdiNumeric,
|
||||
mdiOpenInNew,
|
||||
mdiPalette,
|
||||
mdiPanVertical,
|
||||
mdiPartyPopper,
|
||||
@@ -215,7 +217,8 @@ export const ExampleActions = {
|
||||
Edit: { title: 'Edit album', icon: mdiPencilOutline, onAction },
|
||||
Download: { title: 'Download', icon: mdiDownload, onAction },
|
||||
Delete: { title: 'Delete', icon: mdiTrashCanOutline, color: 'danger', onAction },
|
||||
} satisfies Record<string, ActionItem>;
|
||||
Link: { title: 'Link', icon: mdiOpenInNew, href: 'https://immich.app/' },
|
||||
} satisfies Record<string, ActionItem | ActionLink>;
|
||||
|
||||
export const exampleActions: ActionItem[] = [
|
||||
ExampleActions.Share,
|
||||
@@ -230,6 +233,7 @@ export const exampleMenuItems: MenuItems = [
|
||||
ExampleActions.Copy,
|
||||
ExampleActions.Edit,
|
||||
ExampleActions.Download,
|
||||
ExampleActions.Link,
|
||||
MenuItemType.Divider,
|
||||
ExampleActions.Delete,
|
||||
];
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
<script lang="ts">
|
||||
import Icon from '$lib/components/Icon/Icon.svelte';
|
||||
import Link from '$lib/components/Link/Link.svelte';
|
||||
import Text from '$lib/components/Text/Text.svelte';
|
||||
import { zIndex } from '$lib/constants.js';
|
||||
import { styleVariants } from '$lib/styles.js';
|
||||
import { type ActionItem, type ContextMenuProps, type MenuItems } from '$lib/types.js';
|
||||
import { isEnabled, isMenuItemType } from '$lib/utilities/common.js';
|
||||
import { type ActionItem, type ActionLink, type ContextMenuProps, type MenuItems } from '$lib/types.js';
|
||||
import { isActionLink, isEnabled, isMenuItemType } from '$lib/utilities/common.js';
|
||||
import { cleanClass } from '$lib/utilities/internal.js';
|
||||
import { DropdownMenu } from 'bits-ui';
|
||||
import { fly } from 'svelte/transition';
|
||||
@@ -117,6 +118,13 @@
|
||||
const { side, align } = $derived(getAlignment(position));
|
||||
</script>
|
||||
|
||||
{#snippet itemText(item: ActionItem | ActionLink)}
|
||||
{#if item.icon}
|
||||
<Icon icon={item.icon} class="m-2 shrink-0" />
|
||||
{/if}
|
||||
<Text class="grow text-start font-medium select-none" size="medium">{item.title}</Text>
|
||||
{/snippet}
|
||||
|
||||
<DropdownMenu.Root open={true} onOpenChange={() => onClose()}>
|
||||
<DropdownMenu.Portal>
|
||||
<DropdownMenu.Content forceMount customAnchor={target} {side} {align} {alignOffset} {sideOffset}>
|
||||
@@ -127,6 +135,19 @@
|
||||
{#each filteredItems as item, i (isMenuItemType(item) ? i : item.title)}
|
||||
{#if isMenuItemType(item)}
|
||||
<DropdownMenu.Separator class="dark:border-light-300 my-0.5 border-t" />
|
||||
{:else if isActionLink(item)}
|
||||
<DropdownMenu.Item>
|
||||
{#snippet child({ props })}
|
||||
<Link
|
||||
underline={false}
|
||||
{...props}
|
||||
href={item.href}
|
||||
class={itemStyles({ color: item.color, inset: true })}
|
||||
>
|
||||
{@render itemText(item)}
|
||||
</Link>
|
||||
{/snippet}
|
||||
</DropdownMenu.Item>
|
||||
{:else}
|
||||
<DropdownMenu.Item
|
||||
textValue={item.title}
|
||||
@@ -134,10 +155,7 @@
|
||||
onSelect={() => item.onAction(item)}
|
||||
class={itemStyles({ color: item.color, inset: true })}
|
||||
>
|
||||
{#if item.icon}
|
||||
<Icon icon={item.icon} class="m-2 shrink-0" />
|
||||
{/if}
|
||||
<Text class="grow text-start font-medium select-none" size="medium">{item.title}</Text>
|
||||
{@render itemText(item)}
|
||||
</DropdownMenu.Item>
|
||||
{/if}
|
||||
{/each}
|
||||
|
||||
@@ -330,7 +330,8 @@ export enum MenuItemType {
|
||||
Divider = 'divider',
|
||||
}
|
||||
|
||||
export type MenuItems = Array<ActionItem | MenuItemType | undefined>;
|
||||
export type MenuItem = ActionItem | ActionLink | MenuItemType | undefined;
|
||||
export type MenuItems = MenuItem[];
|
||||
|
||||
export type MenuProps = {
|
||||
items: MenuItems;
|
||||
@@ -376,13 +377,23 @@ export type ActionItemTag = {
|
||||
class?: string;
|
||||
};
|
||||
|
||||
export type ActionItem = {
|
||||
type ActionBase = IfLike & {
|
||||
title: string;
|
||||
description?: string;
|
||||
color?: Color;
|
||||
icon?: IconLike;
|
||||
iconClass?: string;
|
||||
};
|
||||
|
||||
export type ActionLink = ActionBase & {
|
||||
href: string;
|
||||
};
|
||||
|
||||
export type ActionItem = ActionBase & {
|
||||
/**
|
||||
renders as badges on the item
|
||||
*/
|
||||
tags?: Array<string | ActionItemTag>;
|
||||
description?: string;
|
||||
/**
|
||||
searchable text in addition to title, tags, and description
|
||||
*/
|
||||
@@ -391,13 +402,10 @@ export type ActionItem = {
|
||||
text that should be highlighted as a match
|
||||
*/
|
||||
highlights?: string[];
|
||||
icon?: IconLike;
|
||||
iconClass?: string;
|
||||
color?: Color;
|
||||
onAction: ActionItemHandler;
|
||||
shortcuts?: MaybeArray<Shortcut>;
|
||||
shortcutOptions?: { ignoreInputFields?: boolean; preventDefault?: boolean };
|
||||
} & IfLike;
|
||||
};
|
||||
|
||||
export type BreadcrumbsProps = {
|
||||
separator?: IconLike | { text: string };
|
||||
|
||||
@@ -1,5 +1,12 @@
|
||||
import { goto } from '$app/navigation';
|
||||
import { MenuItemType, type ActionItem, type GithubLinkProps, type GithubLinkType, type IfLike } from '$lib/types.js';
|
||||
import {
|
||||
MenuItemType,
|
||||
type ActionLink,
|
||||
type GithubLinkProps,
|
||||
type GithubLinkType,
|
||||
type IfLike,
|
||||
type MenuItem,
|
||||
} from '$lib/types.js';
|
||||
import type { DateTime } from 'luxon';
|
||||
|
||||
export type QueryValue = number | string;
|
||||
@@ -118,10 +125,12 @@ export type ArticleMetadata = {
|
||||
tags?: string[];
|
||||
};
|
||||
|
||||
export const isMenuItemType = (item: ActionItem | MenuItemType): item is MenuItemType => {
|
||||
export const isMenuItemType = (item: MenuItem): item is MenuItemType => {
|
||||
return item === MenuItemType.Divider;
|
||||
};
|
||||
|
||||
export const isActionLink = (item: MenuItem): item is ActionLink => typeof item === 'object' && 'href' in item;
|
||||
|
||||
export const resolveMetadata = (site: Metadata, page?: Metadata, article?: ArticleMetadata) => {
|
||||
const title = page ? `${page.title} | ${site.title}` : site.title;
|
||||
const description = page?.description ?? site.description;
|
||||
|
||||
Reference in New Issue
Block a user