@immich/ui 0.47.0 → 0.49.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.
@@ -0,0 +1,39 @@
1
+ <script lang="ts">
2
+ import Icon from '../Icon/Icon.svelte';
3
+ import Link from '../Link/Link.svelte';
4
+ import type { BreadcrumbItem, BreadcrumbsProps } from '../../types.js';
5
+ import { cleanClass } from '../../utilities/internal.js';
6
+ import { mdiChevronRight } from '@mdi/js';
7
+
8
+ let { separator = mdiChevronRight, class: className, items, ...props }: BreadcrumbsProps = $props();
9
+ </script>
10
+
11
+ {#snippet child({ title, icon }: BreadcrumbItem)}
12
+ <span class="flex items-center gap-1">
13
+ {#if icon}
14
+ <Icon {icon} size="1.25rem" />
15
+ {/if}
16
+ {#if title}
17
+ <span>{title}</span>
18
+ {/if}
19
+ </span>
20
+ {/snippet}
21
+
22
+ <nav class={cleanClass('flex items-center gap-1', className)} {...props}>
23
+ {#each items as item, index (index)}
24
+ {#if index > 0}
25
+ {#if typeof separator === 'object' && 'text' in separator}
26
+ <span class="mx-1">{separator.text}</span>
27
+ {:else}
28
+ <Icon icon={separator} size="1rem" />
29
+ {/if}
30
+ {/if}
31
+ {#if item.href}
32
+ <Link href={item.href} class="underline">
33
+ {@render child(item)}
34
+ </Link>
35
+ {:else}
36
+ {@render child(item)}
37
+ {/if}
38
+ {/each}
39
+ </nav>
@@ -0,0 +1,4 @@
1
+ import type { BreadcrumbsProps } from '../../types.js';
2
+ declare const Breadcrumbs: import("svelte").Component<BreadcrumbsProps, {}, "">;
3
+ type Breadcrumbs = ReturnType<typeof Breadcrumbs>;
4
+ export default Breadcrumbs;
@@ -1,15 +1,14 @@
1
1
  <script lang="ts">
2
2
  import { withChildrenSnippets } from '../../common/use-child.svelte.js';
3
3
  import IconButton from '../IconButton/IconButton.svelte';
4
- import Scrollable from '../Scrollable/Scrollable.svelte';
5
4
  import { ChildKey } from '../../constants.js';
6
5
  import type { Color } from '../../types.js';
7
6
  import { cleanClass } from '../../utilities/internal.js';
8
7
  import { mdiChevronDown } from '@mdi/js';
9
- import { slide } from 'svelte/transition';
10
- import { cubicOut } from 'svelte/easing';
11
8
  import { type Snippet } from 'svelte';
9
+ import { cubicOut } from 'svelte/easing';
12
10
  import type { HTMLAttributes } from 'svelte/elements';
11
+ import { slide } from 'svelte/transition';
13
12
  import { twMerge } from 'tailwind-merge';
14
13
  import { tv } from 'tailwind-variants';
15
14
 
@@ -136,10 +135,11 @@
136
135
  {/if}
137
136
 
138
137
  {#if bodyChild && expanded}
139
- <div transition:slide={{ duration: expandable ? 200 : 0, easing: cubicOut }}>
140
- <Scrollable class={twMerge(cleanClass('p-4', bodyChild?.class))}>
141
- {@render bodyChild?.snippet()}
142
- </Scrollable>
138
+ <div
139
+ transition:slide={{ duration: expandable ? 200 : 0, easing: cubicOut }}
140
+ class={twMerge(cleanClass('immich-scrollbar h-full w-full overflow-auto p-4', bodyChild?.class))}
141
+ >
142
+ {@render bodyChild?.snippet()}
143
143
  </div>
144
144
  {/if}
145
145
 
@@ -3,7 +3,7 @@
3
3
  import Text from '../Text/Text.svelte';
4
4
  import { zIndex } from '../../constants.js';
5
5
  import { styleVariants } from '../../styles.js';
6
- import { MenuItemType, type ContextMenuProps, type MenuItem } from '../../types.js';
6
+ import { MenuItemType, type ContextMenuProps, type ActionItem } from '../../types.js';
7
7
  import { cleanClass, isEnabled } from '../../utilities/internal.js';
8
8
  import { DropdownMenu } from 'bits-ui';
9
9
  import { fly } from 'svelte/transition';
@@ -20,7 +20,7 @@
20
20
  ...restProps
21
21
  }: ContextMenuProps = $props();
22
22
 
23
- const isDivider = (item: MenuItem | MenuItemType): item is MenuItemType => {
23
+ const isDivider = (item: ActionItem | MenuItemType): item is MenuItemType => {
24
24
  return item === MenuItemType.Divider;
25
25
  };
26
26
 
@@ -107,7 +107,7 @@
107
107
  <DropdownMenu.Item
108
108
  textValue={item.title}
109
109
  closeOnSelect
110
- onSelect={(event) => item.onSelect?.({ event, item })}
110
+ onSelect={() => item.onAction(item)}
111
111
  class="px-1"
112
112
  >
113
113
  <div class={itemStyles({ color: item.color })}>
@@ -125,7 +125,7 @@
125
125
  <DropdownMenu.Item
126
126
  textValue={item.title}
127
127
  closeOnSelect
128
- onSelect={(event) => item.onSelect?.({ event, item })}
128
+ onSelect={() => item.onAction(item)}
129
129
  title={item.title}
130
130
  >
131
131
  <div class={cleanClass(itemStyles({ color: item.color }))}>
@@ -0,0 +1,23 @@
1
+ <script lang="ts">
2
+ import IconButton from '../IconButton/IconButton.svelte';
3
+ import { menuManager } from '../../services/menu-manager.svelte.js';
4
+ import type { ContextMenuButtonProps } from '../../types.js';
5
+ import { mdiDotsVertical } from '@mdi/js';
6
+
7
+ const {
8
+ color = 'secondary',
9
+ position = 'top-right',
10
+ icon = mdiDotsVertical,
11
+ variant = 'ghost',
12
+ shape = 'round',
13
+ items,
14
+ bottomItems,
15
+ ...rest
16
+ }: ContextMenuButtonProps = $props();
17
+
18
+ const onclick = async (event: Event) => {
19
+ await menuManager.show({ target: event.currentTarget as HTMLElement, position, items, bottomItems });
20
+ };
21
+ </script>
22
+
23
+ <IconButton {icon} {color} {shape} {variant} {...rest} {onclick} />
@@ -0,0 +1,4 @@
1
+ import type { ContextMenuButtonProps } from '../../types.js';
2
+ declare const ContextMenuButton: import("svelte").Component<ContextMenuButtonProps, {}, "">;
3
+ type ContextMenuButton = ReturnType<typeof ContextMenuButton>;
4
+ export default ContextMenuButton;
@@ -23,45 +23,3 @@
23
23
  <div bind:this={ref} class={cleanClass('immich-scrollbar h-full w-full overflow-auto', className)}>
24
24
  {@render children?.()}
25
25
  </div>
26
-
27
- <style>
28
- /* width */
29
- .immich-scrollbar::-webkit-scrollbar {
30
- width: 8px;
31
- height: 8px;
32
- visibility: hidden;
33
- }
34
- /* Track */
35
- .immich-scrollbar::-webkit-scrollbar-track {
36
- background: #f1f1f1;
37
- border-radius: 16px;
38
- visibility: hidden;
39
- }
40
-
41
- /* Handle */
42
- .immich-scrollbar::-webkit-scrollbar-thumb {
43
- background: rgba(85, 86, 87, 0.408);
44
- border-radius: 16px;
45
- visibility: hidden;
46
- }
47
-
48
- /* Handle on hover */
49
- .immich-scrollbar::-webkit-scrollbar-thumb:hover {
50
- background: #4250afad;
51
- border-radius: 16px;
52
- }
53
-
54
- /*
55
- * Show scrollbar elements when hovering or actively scrolling
56
- * Applies to the main scrollbar, track, and thumb components
57
- * Changes visibility from hidden to visible on user interaction
58
- */
59
- .immich-scrollbar:hover::-webkit-scrollbar,
60
- .immich-scrollbar:active::-webkit-scrollbar,
61
- .immich-scrollbar:hover::-webkit-scrollbar-track,
62
- .immich-scrollbar:active::-webkit-scrollbar-track,
63
- .immich-scrollbar:hover::-webkit-scrollbar-thumb,
64
- .immich-scrollbar:active::-webkit-scrollbar-thumb {
65
- visibility: visible;
66
- }
67
- </style>
package/dist/index.d.ts CHANGED
@@ -17,6 +17,7 @@ export { default as AppShellHeader } from './components/AppShell/AppShellHeader.
17
17
  export { default as AppShellSidebar } from './components/AppShell/AppShellSidebar.svelte';
18
18
  export { default as Avatar } from './components/Avatar/Avatar.svelte';
19
19
  export { default as Badge } from './components/Badge/Badge.svelte';
20
+ export { default as Breadcrumbs } from './components/Breadcrumbs/Breadcrumbs.svelte';
20
21
  export { default as Button } from './components/Button/Button.svelte';
21
22
  export { default as Card } from './components/Card/Card.svelte';
22
23
  export { default as CardBody } from './components/Card/CardBody.svelte';
package/dist/index.js CHANGED
@@ -19,6 +19,7 @@ export { default as AppShellHeader } from './components/AppShell/AppShellHeader.
19
19
  export { default as AppShellSidebar } from './components/AppShell/AppShellSidebar.svelte';
20
20
  export { default as Avatar } from './components/Avatar/Avatar.svelte';
21
21
  export { default as Badge } from './components/Badge/Badge.svelte';
22
+ export { default as Breadcrumbs } from './components/Breadcrumbs/Breadcrumbs.svelte';
22
23
  export { default as Button } from './components/Button/Button.svelte';
23
24
  export { default as Card } from './components/Card/Card.svelte';
24
25
  export { default as CardBody } from './components/Card/CardBody.svelte';
@@ -299,3 +299,43 @@
299
299
  border-color: var(--immich-ui-default-border);
300
300
  }
301
301
  }
302
+
303
+ /* width */
304
+ .immich-scrollbar::-webkit-scrollbar {
305
+ width: 8px;
306
+ height: 8px;
307
+ visibility: hidden;
308
+ }
309
+ /* Track */
310
+ .immich-scrollbar::-webkit-scrollbar-track {
311
+ background: #f1f1f1;
312
+ border-radius: 16px;
313
+ visibility: hidden;
314
+ }
315
+
316
+ /* Handle */
317
+ .immich-scrollbar::-webkit-scrollbar-thumb {
318
+ background: rgba(85, 86, 87, 0.408);
319
+ border-radius: 16px;
320
+ visibility: hidden;
321
+ }
322
+
323
+ /* Handle on hover */
324
+ .immich-scrollbar::-webkit-scrollbar-thumb:hover {
325
+ background: #4250afad;
326
+ border-radius: 16px;
327
+ }
328
+
329
+ /*
330
+ * Show scrollbar elements when hovering or actively scrolling
331
+ * Applies to the main scrollbar, track, and thumb components
332
+ * Changes visibility from hidden to visible on user interaction
333
+ */
334
+ .immich-scrollbar:hover::-webkit-scrollbar,
335
+ .immich-scrollbar:active::-webkit-scrollbar,
336
+ .immich-scrollbar:hover::-webkit-scrollbar-track,
337
+ .immich-scrollbar:active::-webkit-scrollbar-track,
338
+ .immich-scrollbar:hover::-webkit-scrollbar-thumb,
339
+ .immich-scrollbar:active::-webkit-scrollbar-thumb {
340
+ visibility: visible;
341
+ }
package/dist/types.d.ts CHANGED
@@ -67,6 +67,13 @@ export type CloseButtonProps = {
67
67
  class?: string;
68
68
  translations?: TranslationProps<'close'>;
69
69
  } & ButtonOrAnchor;
70
+ export type ContextMenuButtonProps = ButtonBase & {
71
+ icon?: IconLike;
72
+ position?: ContextMenuPosition;
73
+ 'aria-label': string;
74
+ items: MenuItems;
75
+ bottomItems?: Array<ActionItem | undefined>;
76
+ } & Omit<HTMLButtonAttributes, 'color' | 'size'>;
70
77
  export type IconButtonProps = ButtonBase & {
71
78
  icon: IconLike;
72
79
  flipped?: boolean;
@@ -203,23 +210,13 @@ export type ToastButton = {
203
210
  variant?: Variants;
204
211
  onClick: () => void;
205
212
  };
206
- export type MenuSelectHandler = (context: {
207
- event: Event;
208
- item: MenuItem;
209
- }) => void;
210
- export type MenuItem = {
211
- title: string;
212
- icon: IconLike;
213
- color?: Color;
214
- onSelect?: MenuSelectHandler;
215
- } & IfLike;
216
213
  export declare enum MenuItemType {
217
214
  Divider = "divider"
218
215
  }
219
- export type MenuItems = Array<MenuItem | MenuItemType | undefined>;
216
+ export type MenuItems = Array<ActionItem | MenuItemType | undefined>;
220
217
  export type MenuProps = {
221
218
  items: MenuItems;
222
- bottomItems?: (MenuItem | undefined)[];
219
+ bottomItems?: (ActionItem | undefined)[];
223
220
  size?: MenuSize;
224
221
  } & HTMLAttributes<HTMLDivElement>;
225
222
  export type ContextMenuPosition = 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right';
@@ -242,4 +239,29 @@ export type DatePickerProps = {
242
239
  export type IfLike = {
243
240
  $if?: () => boolean;
244
241
  };
242
+ export type ActionItemHandler<T = never> = (item: ActionItem<T>) => void | Promise<void>;
243
+ export type ActionItem<T = never> = Omit<{
244
+ title: string;
245
+ icon: IconLike;
246
+ color?: Color;
247
+ onAction: ActionItemHandler<T>;
248
+ data: T;
249
+ } & IfLike, [
250
+ T
251
+ ] extends [never] ? 'data' : ''>;
252
+ export type BreadcrumbsProps = {
253
+ separator?: IconLike | {
254
+ text: string;
255
+ };
256
+ items: BreadcrumbItem[];
257
+ } & HTMLAttributes<HTMLElement>;
258
+ export type BreadcrumbItem = {
259
+ href?: string;
260
+ } & ({
261
+ title: string;
262
+ icon?: IconLike;
263
+ } | {
264
+ title?: string;
265
+ icon: IconLike;
266
+ });
245
267
  export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@immich/ui",
3
- "version": "0.47.0",
3
+ "version": "0.49.0",
4
4
  "license": "GNU Affero General Public License version 3",
5
5
  "repository": {
6
6
  "type": "git",
@@ -58,7 +58,7 @@
58
58
  "@immich/svelte-markdown-preprocess": "^0.1.0"
59
59
  },
60
60
  "volta": {
61
- "node": "24.11.0"
61
+ "node": "24.11.1"
62
62
  },
63
63
  "scripts": {
64
64
  "create": "node scripts/create.js",