@immich/ui 0.42.2 → 0.43.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.
@@ -4,7 +4,7 @@
4
4
  import { zIndex } from '../../constants.js';
5
5
  import { styleVariants } from '../../styles.js';
6
6
  import { MenuItemType, type ContextMenuProps, type MenuItem } from '../../types.js';
7
- import { cleanClass } from '../../utilities/internal.js';
7
+ import { cleanClass, isEnabled } from '../../utilities/internal.js';
8
8
  import { DropdownMenu } from 'bits-ui';
9
9
  import { fly } from 'svelte/transition';
10
10
  import { tv } from 'tailwind-variants';
@@ -14,7 +14,7 @@
14
14
  items,
15
15
  bottomItems,
16
16
  size = 'medium',
17
- anchor,
17
+ target,
18
18
  position = 'top-left',
19
19
  class: className,
20
20
  ...restProps
@@ -83,19 +83,24 @@
83
83
  }
84
84
  };
85
85
 
86
- const alignOffset = $derived(anchor.clientWidth / 2);
87
- const sideOffset = $derived(-anchor.clientHeight / 2);
86
+ const filteredItems = $derived(
87
+ items.filter((item) => item !== undefined).filter((item) => isDivider(item) || isEnabled(item)),
88
+ );
89
+ const filteredBottomItems = $derived(bottomItems?.filter((item) => item !== undefined).filter(isEnabled));
90
+
91
+ const alignOffset = $derived(target.clientWidth / 2);
92
+ const sideOffset = $derived(-target.clientHeight / 2);
88
93
  const { side, align } = $derived(getAlignment(position));
89
94
  </script>
90
95
 
91
96
  <DropdownMenu.Root open={true} onOpenChange={() => onClose()}>
92
97
  <DropdownMenu.Portal>
93
- <DropdownMenu.Content forceMount customAnchor={anchor} {side} {align} {alignOffset} {sideOffset}>
98
+ <DropdownMenu.Content forceMount customAnchor={target} {side} {align} {alignOffset} {sideOffset}>
94
99
  {#snippet child({ wrapperProps, props, open })}
95
100
  {#if open}
96
101
  <div {...wrapperProps} class={zIndex.ContextMenu}>
97
102
  <div {...props} {...restProps} class={cleanClass(wrapperStyles({ size }), className)} transition:fly>
98
- {#each items as item, i (isDivider(item) ? i : item.title)}
103
+ {#each filteredItems as item, i (isDivider(item) ? i : item.title)}
99
104
  {#if isDivider(item)}
100
105
  <DropdownMenu.Separator class="my-0.5 border-t" />
101
106
  {:else}
@@ -113,10 +118,10 @@
113
118
  {/if}
114
119
  {/each}
115
120
 
116
- {#if bottomItems}
121
+ {#if filteredBottomItems}
117
122
  <DropdownMenu.Separator class="my-0.5 border-t" />
118
123
  <div class="flex gap-1 px-1">
119
- {#each bottomItems as item (item.title)}
124
+ {#each filteredBottomItems as item (item.title)}
120
125
  <DropdownMenu.Item
121
126
  textValue={item.title}
122
127
  closeOnSelect
@@ -1,21 +1,21 @@
1
1
  import { type Shortcut } from '../actions/shortcut.js';
2
- import type { MaybeArray, TranslationProps } from '../types.js';
2
+ import type { IfLike, MaybeArray, TranslationProps } from '../types.js';
3
3
  export type CommandItem = {
4
4
  icon: string;
5
5
  iconClass?: string;
6
6
  type: string;
7
7
  title: string;
8
8
  description?: string;
9
- text: string;
9
+ text?: string;
10
10
  shortcuts?: MaybeArray<Shortcut>;
11
11
  shortcutOptions?: {
12
12
  ignoreInputFields?: boolean;
13
13
  preventDefault?: boolean;
14
14
  };
15
- } & ({
15
+ } & IfLike & ({
16
16
  href: string;
17
17
  } | {
18
- action: () => void | Promise<void>;
18
+ action: (command: CommandItem) => void;
19
19
  });
20
20
  export type CommandPaletteTranslations = TranslationProps<'search_placeholder' | 'search_no_results' | 'search_recently_used' | 'command_palette_prompt_default'>;
21
21
  export declare const asText: (...items: unknown[]) => string;
@@ -1,7 +1,7 @@
1
1
  import { goto } from '$app/navigation';
2
2
  import { matchesShortcut, shortcuts, shouldIgnoreEvent } from '../actions/shortcut.js';
3
3
  import CommandPaletteModal from '../internal/CommandPaletteModal.svelte';
4
- import { generateId } from '../utilities/internal.js';
4
+ import { generateId, isEnabled } from '../utilities/internal.js';
5
5
  import { modalManager } from './modal-manager.svelte.js';
6
6
  export const asText = (...items) => {
7
7
  return items
@@ -10,11 +10,11 @@ export const asText = (...items) => {
10
10
  .join('|')
11
11
  .toLowerCase();
12
12
  };
13
- const isMatch = (item, query) => {
13
+ const isMatch = ({ title, description, type, text = asText(title, description, type) }, query) => {
14
14
  if (!query) {
15
15
  return true;
16
16
  }
17
- return item.text.includes(query);
17
+ return text.includes(query);
18
18
  };
19
19
  class CommandPaletteManager {
20
20
  query = $state('');
@@ -26,9 +26,9 @@ class CommandPaletteManager {
26
26
  #isOpen = false;
27
27
  #globalLayer = $state({ items: [], recentItems: [] });
28
28
  #layers = $state([{ items: [], recentItems: [] }]);
29
- items = $derived([...this.#globalLayer.items, ...this.#layers.at(-1).items]);
29
+ items = $derived([...this.#globalLayer.items, ...this.#layers.at(-1).items].filter(isEnabled));
30
30
  filteredItems = $derived(this.items.filter((item) => isMatch(item, this.#normalizedQuery)).slice(0, 100));
31
- recentItems = $derived([...this.#globalLayer.recentItems, ...this.#layers.at(-1).recentItems]);
31
+ recentItems = $derived([...this.#globalLayer.recentItems, ...this.#layers.at(-1).recentItems].filter(isEnabled));
32
32
  results = $derived(this.query ? this.filteredItems : this.recentItems);
33
33
  get isEnabled() {
34
34
  return this.#isEnabled;
@@ -73,14 +73,12 @@ class CommandPaletteManager {
73
73
  if ('href' in command) {
74
74
  if (!command.href.startsWith('/')) {
75
75
  window.open(command.href, '_blank');
76
+ return;
76
77
  }
77
- else {
78
- await goto(command.href);
79
- }
80
- }
81
- else {
82
- await command.action();
78
+ await goto(command.href);
79
+ return;
83
80
  }
81
+ command.action(command);
84
82
  }
85
83
  setTranslations(translations = {}) {
86
84
  this.#translations = translations;
@@ -1,6 +1,6 @@
1
1
  import type { ContextMenuBaseProps } from '../types.js';
2
2
  declare class MenuManager {
3
- show(event: MouseEvent, props: ContextMenuBaseProps): Promise<void>;
3
+ show(props: ContextMenuBaseProps): Promise<void>;
4
4
  }
5
5
  export declare const menuManager: MenuManager;
6
6
  export {};
@@ -1,10 +1,9 @@
1
1
  import ContextMenu from '../components/ContextMenu/ContextMenu.svelte';
2
2
  import { modalManager } from './modal-manager.svelte.js';
3
3
  class MenuManager {
4
- show(event, props) {
4
+ show(props) {
5
5
  return modalManager.show(ContextMenu, {
6
6
  ...props,
7
- anchor: event.currentTarget,
8
7
  });
9
8
  }
10
9
  }
package/dist/types.d.ts CHANGED
@@ -213,22 +213,25 @@ export type MenuItem = {
213
213
  icon: IconLike;
214
214
  color?: Color;
215
215
  onSelect?: MenuSelectHandler;
216
- };
216
+ } & IfLike;
217
217
  export declare enum MenuItemType {
218
218
  Divider = "divider"
219
219
  }
220
- export type MenuItems = Array<MenuItem | MenuItemType>;
220
+ export type MenuItems = Array<MenuItem | MenuItemType | undefined>;
221
221
  export type MenuProps = {
222
222
  items: MenuItems;
223
- bottomItems?: MenuItem[];
223
+ bottomItems?: (MenuItem | undefined)[];
224
224
  size?: MenuSize;
225
225
  } & HTMLAttributes<HTMLDivElement>;
226
226
  export type ContextMenuPosition = 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right';
227
227
  export type ContextMenuBaseProps = MenuProps & {
228
+ target: HTMLElement;
228
229
  position?: ContextMenuPosition;
229
230
  };
230
231
  export type ContextMenuProps = ContextMenuBaseProps & {
231
232
  onClose: () => void;
232
- anchor: HTMLElement;
233
+ };
234
+ export type IfLike = {
235
+ $if?: () => boolean;
233
236
  };
234
237
  export {};
@@ -1,4 +1,4 @@
1
- import type { Color, IconLike, TextColor } from '../types.js';
1
+ import type { Color, IconLike, IfLike, TextColor } from '../types.js';
2
2
  export declare const cleanClass: (...classNames: unknown[]) => string;
3
3
  export declare const withPrefix: (key: string) => string;
4
4
  export declare const generateId: () => string;
@@ -9,3 +9,4 @@ export declare const resolveIcon: ({ icons, color, override, fallback, }: {
9
9
  override?: IconLike | false;
10
10
  icons: Partial<Record<Color | TextColor, string>>;
11
11
  }) => IconLike | undefined;
12
+ export declare const isEnabled: ({ $if }: IfLike) => boolean;
@@ -24,3 +24,4 @@ export const resolveIcon = ({ icons, color, override, fallback, }) => {
24
24
  }
25
25
  return icons[color] ?? fallback;
26
26
  };
27
+ export const isEnabled = ({ $if }) => $if?.() ?? true;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@immich/ui",
3
- "version": "0.42.2",
3
+ "version": "0.43.0",
4
4
  "license": "GNU Affero General Public License version 3",
5
5
  "repository": {
6
6
  "type": "git",