@immich/ui 0.49.2 → 0.50.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/AppShell/AppShellSidebar.svelte +1 -1
- package/dist/components/Breadcrumbs/Breadcrumbs.svelte +1 -1
- package/dist/components/Card/Card.svelte +8 -11
- package/dist/components/ContextMenu/ContextMenu.svelte +34 -12
- package/dist/components/ContextMenu/ContextMenuButton.svelte +12 -1
- package/dist/components/Toast/ToastPanel.svelte +12 -6
- package/dist/components/Toast/ToastPanel.svelte.d.ts +2 -5
- package/dist/internal/Button.svelte +14 -17
- package/dist/services/command-palette-manager.svelte.d.ts +64 -4
- package/dist/services/toast-manager.svelte.d.ts +3 -2
- package/dist/services/toast-manager.svelte.js +11 -8
- package/dist/services/translation.svelte.d.ts +1 -0
- package/dist/services/translation.svelte.js +2 -0
- package/dist/types.d.ts +9 -9
- package/dist/utilities/common.d.ts +2 -0
- package/dist/utilities/common.js +4 -0
- package/package.json +1 -1
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
<Scrollable
|
|
19
19
|
class={cleanClass(
|
|
20
20
|
'bg-light text-dark absolute shrink-0 border-e shadow-lg transition-all duration-200 md:relative',
|
|
21
|
-
open ? `${zIndex.AppShellSidebar} w-[min(100vw,16rem)]` : 'w-
|
|
21
|
+
open ? `${zIndex.AppShellSidebar} w-[min(100vw,16rem)]` : 'w-0 border-e-0',
|
|
22
22
|
className,
|
|
23
23
|
)}
|
|
24
24
|
>
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
</span>
|
|
20
20
|
{/snippet}
|
|
21
21
|
|
|
22
|
-
<nav class={cleanClass('flex items-center gap-1', className)} {...props}>
|
|
22
|
+
<nav class={cleanClass('flex flex-wrap items-center gap-1', className)} {...props}>
|
|
23
23
|
{#each items as item, index (index)}
|
|
24
24
|
{#if index > 0}
|
|
25
25
|
{#if typeof separator === 'object' && 'text' in separator}
|
|
@@ -9,7 +9,6 @@
|
|
|
9
9
|
import { cubicOut } from 'svelte/easing';
|
|
10
10
|
import type { HTMLAttributes } from 'svelte/elements';
|
|
11
11
|
import { slide } from 'svelte/transition';
|
|
12
|
-
import { twMerge } from 'tailwind-merge';
|
|
13
12
|
import { tv } from 'tailwind-variants';
|
|
14
13
|
|
|
15
14
|
type Props = HTMLAttributes<HTMLDivElement> & {
|
|
@@ -87,14 +86,12 @@
|
|
|
87
86
|
const headerPadding = $derived(headerBorder || !expanded);
|
|
88
87
|
|
|
89
88
|
const headerContainerClasses = $derived(
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
headerChild?.class,
|
|
97
|
-
),
|
|
89
|
+
cleanClass(
|
|
90
|
+
headerContainerStyles({
|
|
91
|
+
padding: headerPadding,
|
|
92
|
+
border: headerBorder,
|
|
93
|
+
}),
|
|
94
|
+
headerChild?.class,
|
|
98
95
|
),
|
|
99
96
|
);
|
|
100
97
|
</script>
|
|
@@ -137,14 +134,14 @@
|
|
|
137
134
|
{#if bodyChild && expanded}
|
|
138
135
|
<div
|
|
139
136
|
transition:slide={{ duration: expandable ? 200 : 0, easing: cubicOut }}
|
|
140
|
-
class={
|
|
137
|
+
class={cleanClass('immich-scrollbar h-full w-full overflow-auto p-4', bodyChild?.class)}
|
|
141
138
|
>
|
|
142
139
|
{@render bodyChild?.snippet()}
|
|
143
140
|
</div>
|
|
144
141
|
{/if}
|
|
145
142
|
|
|
146
143
|
{#if footerChild}
|
|
147
|
-
<div class={
|
|
144
|
+
<div class={cleanClass('flex items-center border-t p-4', footerChild.class)}>
|
|
148
145
|
{@render footerChild.snippet()}
|
|
149
146
|
</div>
|
|
150
147
|
{/if}
|
|
@@ -3,7 +3,8 @@
|
|
|
3
3
|
import Text from '../Text/Text.svelte';
|
|
4
4
|
import { zIndex } from '../../constants.js';
|
|
5
5
|
import { styleVariants } from '../../styles.js';
|
|
6
|
-
import {
|
|
6
|
+
import { type ActionItem, type ContextMenuProps, type MenuItems } from '../../types.js';
|
|
7
|
+
import { isMenuItemType } from '../../utilities/common.js';
|
|
7
8
|
import { cleanClass, isEnabled } from '../../utilities/internal.js';
|
|
8
9
|
import { DropdownMenu } from 'bits-ui';
|
|
9
10
|
import { fly } from 'svelte/transition';
|
|
@@ -20,10 +21,6 @@
|
|
|
20
21
|
...restProps
|
|
21
22
|
}: ContextMenuProps = $props();
|
|
22
23
|
|
|
23
|
-
const isDivider = (item: ActionItem | MenuItemType): item is MenuItemType => {
|
|
24
|
-
return item === MenuItemType.Divider;
|
|
25
|
-
};
|
|
26
|
-
|
|
27
24
|
const itemStyles = tv({
|
|
28
25
|
base: 'hover:bg-light-200 flex w-full items-center gap-1 rounded-lg p-1 text-start hover:cursor-pointer',
|
|
29
26
|
variants: {
|
|
@@ -83,10 +80,35 @@
|
|
|
83
80
|
}
|
|
84
81
|
};
|
|
85
82
|
|
|
86
|
-
const
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
83
|
+
const getFilteredItems = (items?: MenuItems) => {
|
|
84
|
+
if (!items) {
|
|
85
|
+
return [];
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const results = [];
|
|
89
|
+
for (const item of items) {
|
|
90
|
+
if (item && (isMenuItemType(item) || isEnabled(item))) {
|
|
91
|
+
results.push(item);
|
|
92
|
+
continue;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// remove trailing dividers
|
|
97
|
+
for (let i = results.length - 1; i >= 0; i--) {
|
|
98
|
+
const item = results[i];
|
|
99
|
+
if (isMenuItemType(item)) {
|
|
100
|
+
results.pop();
|
|
101
|
+
continue;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
break;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
return results;
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
const filteredItems = $derived(getFilteredItems(items));
|
|
111
|
+
const filteredBottomItems = $derived(getFilteredItems(bottomItems) as ActionItem[]);
|
|
90
112
|
|
|
91
113
|
const alignOffset = $derived(target.clientWidth / 2);
|
|
92
114
|
const sideOffset = $derived(-target.clientHeight / 2);
|
|
@@ -100,8 +122,8 @@
|
|
|
100
122
|
{#if open}
|
|
101
123
|
<div {...wrapperProps} class={zIndex.ContextMenu}>
|
|
102
124
|
<div {...props} {...restProps} class={cleanClass(wrapperStyles({ size }), className)} transition:fly>
|
|
103
|
-
{#each filteredItems as item, i (
|
|
104
|
-
{#if
|
|
125
|
+
{#each filteredItems as item, i (isMenuItemType(item) ? i : item.title)}
|
|
126
|
+
{#if isMenuItemType(item)}
|
|
105
127
|
<DropdownMenu.Separator class="dark:border-light-300 my-0.5 border-t" />
|
|
106
128
|
{:else}
|
|
107
129
|
<DropdownMenu.Item
|
|
@@ -118,7 +140,7 @@
|
|
|
118
140
|
{/if}
|
|
119
141
|
{/each}
|
|
120
142
|
|
|
121
|
-
{#if filteredBottomItems}
|
|
143
|
+
{#if filteredBottomItems.length > 0}
|
|
122
144
|
<DropdownMenu.Separator class="dark:border-light-300 my-0.5 border-t" />
|
|
123
145
|
<div class="flex gap-1 px-1">
|
|
124
146
|
{#each filteredBottomItems as item (item.title)}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import IconButton from '../IconButton/IconButton.svelte';
|
|
3
3
|
import { menuManager } from '../../services/menu-manager.svelte.js';
|
|
4
|
+
import { t } from '../../services/translation.svelte.js';
|
|
4
5
|
import type { ContextMenuButtonProps } from '../../types.js';
|
|
5
6
|
import { mdiDotsVertical } from '@mdi/js';
|
|
6
7
|
|
|
@@ -10,8 +11,10 @@
|
|
|
10
11
|
icon = mdiDotsVertical,
|
|
11
12
|
variant = 'ghost',
|
|
12
13
|
shape = 'round',
|
|
14
|
+
'aria-label': ariaLabel,
|
|
13
15
|
items,
|
|
14
16
|
bottomItems,
|
|
17
|
+
translations,
|
|
15
18
|
...rest
|
|
16
19
|
}: ContextMenuButtonProps = $props();
|
|
17
20
|
|
|
@@ -20,4 +23,12 @@
|
|
|
20
23
|
};
|
|
21
24
|
</script>
|
|
22
25
|
|
|
23
|
-
<IconButton
|
|
26
|
+
<IconButton
|
|
27
|
+
{icon}
|
|
28
|
+
{color}
|
|
29
|
+
{shape}
|
|
30
|
+
{variant}
|
|
31
|
+
aria-label={ariaLabel ?? t('open_menu', translations)}
|
|
32
|
+
{...rest}
|
|
33
|
+
{onclick}
|
|
34
|
+
/>
|
|
@@ -2,16 +2,22 @@
|
|
|
2
2
|
import Toast from './Toast.svelte';
|
|
3
3
|
import { zIndex } from '../../constants.js';
|
|
4
4
|
import { isCustomToast } from '../../services/toast-manager.svelte.js';
|
|
5
|
-
import type {
|
|
5
|
+
import type { ToastPanelProps } from '../../types.js';
|
|
6
|
+
import { cleanClass } from '../../utilities/internal.js';
|
|
6
7
|
|
|
7
|
-
|
|
8
|
-
items: Array<ToastItem & ToastId>;
|
|
9
|
-
};
|
|
8
|
+
const { items, class: className, ...props }: ToastPanelProps = $props();
|
|
10
9
|
|
|
11
|
-
const
|
|
10
|
+
const isEmpty = $derived(items.length === 0);
|
|
12
11
|
</script>
|
|
13
12
|
|
|
14
|
-
<div
|
|
13
|
+
<div
|
|
14
|
+
class={cleanClass(
|
|
15
|
+
isEmpty ? 'hidden' : 'absolute top-0 right-0 flex flex-col items-end justify-end gap-2 p-4',
|
|
16
|
+
zIndex.ToastPanel,
|
|
17
|
+
className,
|
|
18
|
+
)}
|
|
19
|
+
{...props}
|
|
20
|
+
>
|
|
15
21
|
{#each items as item (item.id)}
|
|
16
22
|
{#if isCustomToast(item)}
|
|
17
23
|
<item.component {...item.props} />
|
|
@@ -1,7 +1,4 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
|
|
3
|
-
items: Array<ToastItem & ToastId>;
|
|
4
|
-
};
|
|
5
|
-
declare const ToastPanel: import("svelte").Component<Props, {}, "">;
|
|
1
|
+
import type { ToastPanelProps } from '../../types.js';
|
|
2
|
+
declare const ToastPanel: import("svelte").Component<ToastPanelProps, {}, "">;
|
|
6
3
|
type ToastPanel = ReturnType<typeof ToastPanel>;
|
|
7
4
|
export default ToastPanel;
|
|
@@ -7,7 +7,6 @@
|
|
|
7
7
|
import { cleanClass } from '../utilities/internal.js';
|
|
8
8
|
import { Button as ButtonPrimitive } from 'bits-ui';
|
|
9
9
|
import type { HTMLAnchorAttributes, HTMLButtonAttributes } from 'svelte/elements';
|
|
10
|
-
import { twMerge } from 'tailwind-merge';
|
|
11
10
|
import { tv } from 'tailwind-variants';
|
|
12
11
|
|
|
13
12
|
type InternalButtonProps = ButtonProps & {
|
|
@@ -99,22 +98,20 @@
|
|
|
99
98
|
|
|
100
99
|
const classList = $derived(
|
|
101
100
|
cleanClass(
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
className,
|
|
117
|
-
),
|
|
101
|
+
buttonVariants({
|
|
102
|
+
shape,
|
|
103
|
+
fullWidth,
|
|
104
|
+
textPadding: icon ? undefined : size,
|
|
105
|
+
textSize: size,
|
|
106
|
+
iconSize: icon ? size : undefined,
|
|
107
|
+
disabled,
|
|
108
|
+
roundedSize: shape === 'semi-round' ? size : undefined,
|
|
109
|
+
filledColor: variant === 'filled' ? color : undefined,
|
|
110
|
+
filledColorHover: variant === 'filled' ? color : undefined,
|
|
111
|
+
outlineColor: variant === 'outline' ? color : undefined,
|
|
112
|
+
ghostColor: variant === 'ghost' ? color : undefined,
|
|
113
|
+
}),
|
|
114
|
+
className,
|
|
118
115
|
),
|
|
119
116
|
);
|
|
120
117
|
|
|
@@ -4,16 +4,76 @@ export declare const asText: (...items: unknown[]) => string;
|
|
|
4
4
|
declare class CommandPaletteManager {
|
|
5
5
|
#private;
|
|
6
6
|
selectedIndex: number;
|
|
7
|
-
items: (
|
|
7
|
+
items: ({
|
|
8
|
+
title: string;
|
|
9
|
+
description?: string;
|
|
10
|
+
type?: string;
|
|
11
|
+
searchText?: string;
|
|
12
|
+
icon: import("../types.js").IconLike;
|
|
13
|
+
iconClass?: string;
|
|
14
|
+
color?: import("../types.js").Color;
|
|
15
|
+
onAction: import("../types.js").ActionItemHandler;
|
|
16
|
+
shortcuts?: MaybeArray<import("../actions/shortcut.js").Shortcut>;
|
|
17
|
+
shortcutOptions?: {
|
|
18
|
+
ignoreInputFields?: boolean;
|
|
19
|
+
preventDefault?: boolean;
|
|
20
|
+
};
|
|
21
|
+
isGlobal?: boolean;
|
|
22
|
+
} & import("../types.js").IfLike & {
|
|
8
23
|
id: string;
|
|
9
24
|
})[];
|
|
10
|
-
filteredItems: (
|
|
25
|
+
filteredItems: ({
|
|
26
|
+
title: string;
|
|
27
|
+
description?: string;
|
|
28
|
+
type?: string;
|
|
29
|
+
searchText?: string;
|
|
30
|
+
icon: import("../types.js").IconLike;
|
|
31
|
+
iconClass?: string;
|
|
32
|
+
color?: import("../types.js").Color;
|
|
33
|
+
onAction: import("../types.js").ActionItemHandler;
|
|
34
|
+
shortcuts?: MaybeArray<import("../actions/shortcut.js").Shortcut>;
|
|
35
|
+
shortcutOptions?: {
|
|
36
|
+
ignoreInputFields?: boolean;
|
|
37
|
+
preventDefault?: boolean;
|
|
38
|
+
};
|
|
39
|
+
isGlobal?: boolean;
|
|
40
|
+
} & import("../types.js").IfLike & {
|
|
11
41
|
id: string;
|
|
12
42
|
})[];
|
|
13
|
-
recentItems: (
|
|
43
|
+
recentItems: ({
|
|
44
|
+
title: string;
|
|
45
|
+
description?: string;
|
|
46
|
+
type?: string;
|
|
47
|
+
searchText?: string;
|
|
48
|
+
icon: import("../types.js").IconLike;
|
|
49
|
+
iconClass?: string;
|
|
50
|
+
color?: import("../types.js").Color;
|
|
51
|
+
onAction: import("../types.js").ActionItemHandler;
|
|
52
|
+
shortcuts?: MaybeArray<import("../actions/shortcut.js").Shortcut>;
|
|
53
|
+
shortcutOptions?: {
|
|
54
|
+
ignoreInputFields?: boolean;
|
|
55
|
+
preventDefault?: boolean;
|
|
56
|
+
};
|
|
57
|
+
isGlobal?: boolean;
|
|
58
|
+
} & import("../types.js").IfLike & {
|
|
14
59
|
id: string;
|
|
15
60
|
})[];
|
|
16
|
-
results: (
|
|
61
|
+
results: ({
|
|
62
|
+
title: string;
|
|
63
|
+
description?: string;
|
|
64
|
+
type?: string;
|
|
65
|
+
searchText?: string;
|
|
66
|
+
icon: import("../types.js").IconLike;
|
|
67
|
+
iconClass?: string;
|
|
68
|
+
color?: import("../types.js").Color;
|
|
69
|
+
onAction: import("../types.js").ActionItemHandler;
|
|
70
|
+
shortcuts?: MaybeArray<import("../actions/shortcut.js").Shortcut>;
|
|
71
|
+
shortcutOptions?: {
|
|
72
|
+
ignoreInputFields?: boolean;
|
|
73
|
+
preventDefault?: boolean;
|
|
74
|
+
};
|
|
75
|
+
isGlobal?: boolean;
|
|
76
|
+
} & import("../types.js").IfLike & {
|
|
17
77
|
id: string;
|
|
18
78
|
})[];
|
|
19
79
|
get isEnabled(): boolean;
|
|
@@ -1,16 +1,17 @@
|
|
|
1
|
-
import type { ToastCustom, ToastItem, ToastOptions, ToastShow } from '../types.js';
|
|
1
|
+
import type { ToastCustom, ToastItem, ToastOptions, ToastPanelProps, ToastShow } from '../types.js';
|
|
2
2
|
export declare const isCustomToast: (item: ToastItem) => item is ToastCustom;
|
|
3
3
|
declare class ToastManager {
|
|
4
4
|
#private;
|
|
5
5
|
show(item: ToastShow, options?: ToastOptions): void;
|
|
6
6
|
custom(item: ToastCustom, options?: ToastOptions): void;
|
|
7
|
+
setOptions(options: Omit<ToastPanelProps, 'items'>): void;
|
|
7
8
|
open(item: ToastItem, options?: ToastOptions): void;
|
|
9
|
+
mount(): Promise<void>;
|
|
8
10
|
unmount(): Promise<void>;
|
|
9
11
|
success(item?: string | ToastShow, options?: ToastOptions): void;
|
|
10
12
|
info(item?: string | ToastShow, options?: ToastOptions): void;
|
|
11
13
|
warning(item?: string | ToastShow, options?: ToastOptions): void;
|
|
12
14
|
danger(item?: string | ToastShow, options?: ToastOptions): void;
|
|
13
|
-
mount(): Promise<void>;
|
|
14
15
|
private remove;
|
|
15
16
|
}
|
|
16
17
|
export declare const toastManager: ToastManager;
|
|
@@ -13,6 +13,9 @@ class ToastManager {
|
|
|
13
13
|
custom(item, options) {
|
|
14
14
|
return this.open(item, options);
|
|
15
15
|
}
|
|
16
|
+
setOptions(options) {
|
|
17
|
+
Object.assign(this.#props, options);
|
|
18
|
+
}
|
|
16
19
|
open(item, options) {
|
|
17
20
|
const { timeout = 3000, closable = true, id = generateId() } = options || {};
|
|
18
21
|
const toast = item;
|
|
@@ -32,6 +35,14 @@ class ToastManager {
|
|
|
32
35
|
setTimeout(() => this.remove(toast), timeout);
|
|
33
36
|
}
|
|
34
37
|
}
|
|
38
|
+
async mount() {
|
|
39
|
+
if (!this.#ref) {
|
|
40
|
+
this.#ref = await mount(ToastPanel, {
|
|
41
|
+
target: document.body,
|
|
42
|
+
props: this.#props,
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
}
|
|
35
46
|
async unmount() {
|
|
36
47
|
if (this.#ref) {
|
|
37
48
|
await unmount(this.#ref);
|
|
@@ -49,14 +60,6 @@ class ToastManager {
|
|
|
49
60
|
danger(item, options) {
|
|
50
61
|
this.show({ title: t('toast_danger_title'), color: 'danger', ...expand(item) }, options);
|
|
51
62
|
}
|
|
52
|
-
async mount() {
|
|
53
|
-
if (!this.#ref) {
|
|
54
|
-
this.#ref = await mount(ToastPanel, {
|
|
55
|
-
target: document.body,
|
|
56
|
-
props: this.#props,
|
|
57
|
-
});
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
63
|
remove(target) {
|
|
61
64
|
this.#props.items = this.#props.items.filter((item) => item.id !== target.id);
|
|
62
65
|
}
|
|
@@ -17,6 +17,8 @@ const defaultTranslations = {
|
|
|
17
17
|
hide_password: 'Hide password',
|
|
18
18
|
// theme switcher
|
|
19
19
|
dark_theme: 'Toggle dark theme',
|
|
20
|
+
// context menu
|
|
21
|
+
open_menu: 'Open menu',
|
|
20
22
|
// command palette
|
|
21
23
|
command_palette_prompt_default: 'Quickly find pages, actions, or commands',
|
|
22
24
|
command_palette_to_select: 'to select',
|
package/dist/types.d.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
+
import type { Shortcut } from './actions/shortcut.js';
|
|
1
2
|
import type { Translations } from './services/translation.svelte.js';
|
|
2
3
|
import type { DateTime } from 'luxon';
|
|
3
4
|
import type { Component, Snippet } from 'svelte';
|
|
4
5
|
import type { HTMLAnchorAttributes, HTMLAttributes, HTMLButtonAttributes, HTMLInputAttributes, HTMLLabelAttributes, HTMLTextareaAttributes } from 'svelte/elements';
|
|
5
|
-
import type { Shortcut } from './actions/shortcut.js';
|
|
6
6
|
export type Color = 'primary' | 'secondary' | 'success' | 'danger' | 'warning' | 'info';
|
|
7
7
|
export type TextColor = Color | 'muted';
|
|
8
8
|
export type TextVariant = 'italic';
|
|
@@ -71,9 +71,9 @@ export type CloseButtonProps = {
|
|
|
71
71
|
export type ContextMenuButtonProps = ButtonBase & {
|
|
72
72
|
icon?: IconLike;
|
|
73
73
|
position?: ContextMenuPosition;
|
|
74
|
-
'aria-label': string;
|
|
75
74
|
items: MenuItems;
|
|
76
75
|
bottomItems?: Array<ActionItem | undefined>;
|
|
76
|
+
translations?: TranslationProps<'open_menu'>;
|
|
77
77
|
} & Omit<HTMLButtonAttributes, 'color' | 'size'>;
|
|
78
78
|
export type IconButtonProps = ButtonBase & {
|
|
79
79
|
icon: IconLike;
|
|
@@ -181,6 +181,9 @@ export type ToastContainerProps = ToastCommonProps & {
|
|
|
181
181
|
shape?: Shape;
|
|
182
182
|
size?: ContainerSize;
|
|
183
183
|
} & Omit<HTMLAttributes<HTMLElement>, 'title' | 'color' | 'size'>;
|
|
184
|
+
export type ToastPanelProps = {
|
|
185
|
+
items: Array<ToastItem & ToastId>;
|
|
186
|
+
} & HTMLAttributes<HTMLDivElement>;
|
|
184
187
|
export type ToastProps = ToastContentProps & ToastContainerProps;
|
|
185
188
|
type Closable = {
|
|
186
189
|
onClose: () => void;
|
|
@@ -240,8 +243,8 @@ export type DatePickerProps = {
|
|
|
240
243
|
export type IfLike = {
|
|
241
244
|
$if?: () => boolean;
|
|
242
245
|
};
|
|
243
|
-
export type ActionItemHandler<T =
|
|
244
|
-
export type ActionItem
|
|
246
|
+
export type ActionItemHandler<T extends ActionItem = ActionItem> = (item: T) => void | Promise<void>;
|
|
247
|
+
export type ActionItem = {
|
|
245
248
|
title: string;
|
|
246
249
|
description?: string;
|
|
247
250
|
type?: string;
|
|
@@ -249,17 +252,14 @@ export type ActionItem<T = never> = Omit<{
|
|
|
249
252
|
icon: IconLike;
|
|
250
253
|
iconClass?: string;
|
|
251
254
|
color?: Color;
|
|
252
|
-
onAction: ActionItemHandler
|
|
253
|
-
data: T;
|
|
255
|
+
onAction: ActionItemHandler;
|
|
254
256
|
shortcuts?: MaybeArray<Shortcut>;
|
|
255
257
|
shortcutOptions?: {
|
|
256
258
|
ignoreInputFields?: boolean;
|
|
257
259
|
preventDefault?: boolean;
|
|
258
260
|
};
|
|
259
261
|
isGlobal?: boolean;
|
|
260
|
-
} & IfLike
|
|
261
|
-
T
|
|
262
|
-
] extends [never] ? 'data' : ''>;
|
|
262
|
+
} & IfLike;
|
|
263
263
|
export type BreadcrumbsProps = {
|
|
264
264
|
separator?: IconLike | {
|
|
265
265
|
text: string;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { MenuItemType, type ActionItem } from '../types.js';
|
|
1
2
|
import type { DateTime } from 'luxon';
|
|
2
3
|
export declare const resolveUrl: (url: string, currentHostname?: string) => string;
|
|
3
4
|
export declare const isExternalLink: (href: string) => boolean;
|
|
@@ -14,6 +15,7 @@ export type ArticleMetadata = {
|
|
|
14
15
|
section?: string;
|
|
15
16
|
tags?: string[];
|
|
16
17
|
};
|
|
18
|
+
export declare const isMenuItemType: (item: ActionItem | MenuItemType) => item is MenuItemType;
|
|
17
19
|
export declare const resolveMetadata: (site: Metadata, page?: Metadata, article?: ArticleMetadata) => {
|
|
18
20
|
type: string;
|
|
19
21
|
siteName: string;
|
package/dist/utilities/common.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { env } from '$env/dynamic/public';
|
|
2
|
+
import { MenuItemType } from '../types.js';
|
|
2
3
|
const getImmichApp = (host) => {
|
|
3
4
|
if (!host || !host.endsWith('immich.app')) {
|
|
4
5
|
return false;
|
|
@@ -25,6 +26,9 @@ export const resolveUrl = (url, currentHostname) => {
|
|
|
25
26
|
export const isExternalLink = (href) => {
|
|
26
27
|
return !(href.startsWith('/') || href.startsWith('#'));
|
|
27
28
|
};
|
|
29
|
+
export const isMenuItemType = (item) => {
|
|
30
|
+
return item === MenuItemType.Divider;
|
|
31
|
+
};
|
|
28
32
|
export const resolveMetadata = (site, page, article) => {
|
|
29
33
|
const title = page ? `${page.title} | ${site.title}` : site.title;
|
|
30
34
|
const description = page?.description ?? site.description;
|