@immich/ui 0.58.1 → 0.58.2

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.
@@ -10,10 +10,11 @@
10
10
  import Logo from '../Logo/Logo.svelte';
11
11
  import TooltipProvider from '../Tooltip/TooltipProvider.svelte';
12
12
  import { ChildKey, zIndex } from '../../constants.js';
13
+ import { modalState } from '../../state/modal-state.svelte.js';
13
14
  import type { ModalSize } from '../../types.js';
14
15
  import { cleanClass } from '../../utilities/internal.js';
15
16
  import { Dialog } from 'bits-ui';
16
- import { tick, type Snippet } from 'svelte';
17
+ import { onMount, type Snippet } from 'svelte';
17
18
  import { tv } from 'tailwind-variants';
18
19
 
19
20
  type Props = {
@@ -74,21 +75,28 @@
74
75
  const bodyChildren = $derived(getChildSnippet(ChildKey.ModalBody));
75
76
  const footerChildren = $derived(getChildSnippet(ChildKey.ModalFooter));
76
77
 
77
- const handleClose = async () => {
78
- // wait for bits-ui to complete its event cycle
79
- await tick();
80
- await new Promise((resolve) => setTimeout(resolve, 10));
81
-
82
- onClose?.();
83
- };
84
-
85
78
  let cardRef = $state<HTMLElement | null>(null);
86
79
 
87
80
  const interactOutsideBehavior = $derived(closeOnBackdropClick ? 'close' : 'ignore');
88
81
  const escapeKeydownBehavior = $derived(closeOnEsc ? 'close' : 'ignore');
82
+
83
+ let layer = $state<number>();
84
+ const isHidden = $derived(layer !== modalState.layer);
85
+
86
+ const onOpenChangeComplete = (isOpen: boolean) => {
87
+ if (!isOpen && !isHidden) {
88
+ onClose?.();
89
+ }
90
+ };
91
+
92
+ onMount(() => {
93
+ layer = modalState.incrementLayer();
94
+
95
+ return () => modalState.decrementLayer();
96
+ });
89
97
  </script>
90
98
 
91
- <Dialog.Root open={true} onOpenChange={(isOpen: boolean) => !isOpen && handleClose()}>
99
+ <Dialog.Root open={!isHidden} {onOpenChangeComplete}>
92
100
  <Dialog.Portal>
93
101
  <Dialog.Overlay class="{zIndex.ModalBackdrop} fixed start-0 top-0 flex h-dvh max-h-dvh w-screen bg-black/30" />
94
102
  <Dialog.Content
@@ -111,7 +119,7 @@
111
119
  <Logo variant="icon" size="tiny" />
112
120
  {/if}
113
121
  <CardTitle tag="p" class="text-dark/90 grow text-lg font-semibold">{title}</CardTitle>
114
- <CloseButton class="-me-2" onclick={() => handleClose()} />
122
+ <CloseButton class="-me-2" onclick={() => onClose?.()} />
115
123
  </div>
116
124
  {/if}
117
125
  </CardHeader>
@@ -1,5 +1,5 @@
1
- import { type Component, type ComponentProps } from 'svelte';
2
1
  import ConfirmModal from '../components/ConfirmModal/ConfirmModal.svelte';
2
+ import { type Component, type ComponentProps } from 'svelte';
3
3
  type OnCloseData<T> = T extends {
4
4
  onClose: (data: infer R) => void;
5
5
  } ? unknown extends R ? void : R : T extends {
@@ -1,5 +1,5 @@
1
- import { mount, unmount } from 'svelte';
2
1
  import ConfirmModal from '../components/ConfirmModal/ConfirmModal.svelte';
2
+ import { mount, unmount } from 'svelte';
3
3
  class ModalManager {
4
4
  show(Component, ...props) {
5
5
  return this.open(Component, ...props).onClose;
@@ -10,8 +10,7 @@ class ModalManager {
10
10
  const deferred = new Promise((resolve) => {
11
11
  onClose = async (...args) => {
12
12
  await unmount(modal);
13
- // make sure bits-ui clean up finishes before resolving
14
- setTimeout(() => resolve(args[0]), 10);
13
+ resolve(args[0]);
15
14
  };
16
15
  modal = mount(Component, {
17
16
  target: document.body,
@@ -45,6 +45,6 @@ export declare const siteCommands: {
45
45
  iconClass: string;
46
46
  title: string;
47
47
  description: string;
48
- onAction: () => any;
48
+ onAction: () => Promise<void>;
49
49
  searchText: string;
50
50
  }[];
@@ -1,5 +1,4 @@
1
- import { goto } from '$app/navigation';
2
- import { asText } from '../utilities/common.js';
1
+ import { asText, navigateTo } from '../utilities/common.js';
3
2
  import { mdiOpenInNew } from '@mdi/js';
4
3
  export const Constants = {
5
4
  Socials: {
@@ -134,6 +133,6 @@ export const siteCommands = [
134
133
  iconClass: 'text-indigo-700 dark:text-indigo-200',
135
134
  title: site.title,
136
135
  description: site.description,
137
- onAction: () => goto(site.href),
136
+ onAction: () => navigateTo(site.href),
138
137
  searchText: asText('Site', 'Link', site.title, site.description, site.href),
139
138
  }));
@@ -0,0 +1,8 @@
1
+ declare class ModalState {
2
+ #private;
3
+ get layer(): number;
4
+ incrementLayer(): number;
5
+ decrementLayer(): number;
6
+ }
7
+ export declare const modalState: ModalState;
8
+ export {};
@@ -0,0 +1,16 @@
1
+ class ModalState {
2
+ #layer = $state(0);
3
+ get layer() {
4
+ return this.#layer;
5
+ }
6
+ incrementLayer() {
7
+ return ++this.#layer;
8
+ }
9
+ decrementLayer() {
10
+ if (this.#layer < 1) {
11
+ throw new Error('Tried to decrement the modal layer <0');
12
+ }
13
+ return --this.#layer;
14
+ }
15
+ }
16
+ export const modalState = new ModalState();
@@ -1,5 +1,6 @@
1
1
  import { MenuItemType, type ActionItem, type IfLike } from '../types.js';
2
2
  import type { DateTime } from 'luxon';
3
+ export declare const navigateTo: (url: string) => Promise<void>;
3
4
  export declare const resolveUrl: (url: string, currentHostname?: string) => string;
4
5
  export declare const isExternalLink: (href: string) => boolean;
5
6
  export type Metadata = {
@@ -1,3 +1,4 @@
1
+ import { goto } from '$app/navigation';
1
2
  import { env } from '$env/dynamic/public';
2
3
  import { MenuItemType } from '../types.js';
3
4
  const getImmichApp = (host) => {
@@ -9,6 +10,16 @@ const getImmichApp = (host) => {
9
10
  }
10
11
  return host.split('.')[0];
11
12
  };
13
+ export const navigateTo = async (url) => {
14
+ const resolvedUrl = resolveUrl(url);
15
+ const external = isExternalLink(resolvedUrl);
16
+ if (external) {
17
+ window.open(resolvedUrl, '_blank', 'noreferrer');
18
+ }
19
+ else {
20
+ await goto(resolvedUrl);
21
+ }
22
+ };
12
23
  export const resolveUrl = (url, currentHostname) => {
13
24
  if (!isExternalLink(url)) {
14
25
  return url;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@immich/ui",
3
- "version": "0.58.1",
3
+ "version": "0.58.2",
4
4
  "license": "GNU Affero General Public License version 3",
5
5
  "repository": {
6
6
  "type": "git",