@immich/ui 0.58.4 → 0.59.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,24 +4,34 @@
4
4
  import Child from '../../internal/Child.svelte';
5
5
  import { cleanClass } from '../../utilities/internal.js';
6
6
  import { type Snippet } from 'svelte';
7
+ import { tv } from 'tailwind-variants';
7
8
 
8
9
  type Props = {
9
10
  class?: string;
10
11
  children: Snippet;
11
12
  open?: boolean;
13
+ border?: boolean;
12
14
  };
13
15
 
14
- let { class: className, children, open = $bindable(true) }: Props = $props();
16
+ let { class: className, border = true, children, open = $bindable(true) }: Props = $props();
17
+
18
+ const styles = tv({
19
+ base: 'bg-light text-dark absolute shrink-0 transition-all duration-200 md:relative',
20
+ variants: {
21
+ border: {
22
+ true: 'border-e shadow-lg',
23
+ false: '',
24
+ },
25
+ open: {
26
+ true: `${zIndex.AppShellSidebar} w-[min(100vw,16rem)]`,
27
+ false: 'w-0 border-e-0',
28
+ },
29
+ },
30
+ });
15
31
  </script>
16
32
 
17
33
  <Child for={ChildKey.AppShell} as={ChildKey.AppShellSidebar}>
18
- <Scrollable
19
- class={cleanClass(
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-0 border-e-0',
22
- className,
23
- )}
24
- >
34
+ <Scrollable class={cleanClass(styles({ border, open }), className)}>
25
35
  {@render children?.()}
26
36
  </Scrollable>
27
37
  </Child>
@@ -3,6 +3,7 @@ type Props = {
3
3
  class?: string;
4
4
  children: Snippet;
5
5
  open?: boolean;
6
+ border?: boolean;
6
7
  };
7
8
  declare const AppShellSidebar: import("svelte").Component<Props, {}, "open">;
8
9
  type AppShellSidebar = ReturnType<typeof AppShellSidebar>;
@@ -1,11 +1,32 @@
1
1
  <script lang="ts">
2
+ import Text from '../Text/Text.svelte';
3
+ import type { FontWeight, NavbarVariant, Size, TextColor } from '../../types.js';
4
+ import { cleanClass } from '../../utilities/internal.js';
5
+ import { tv } from 'tailwind-variants';
6
+
2
7
  type Props = {
3
8
  title: string;
9
+ size?: Size;
10
+ color?: TextColor;
11
+ fontWeight?: FontWeight;
12
+ variant?: NavbarVariant;
13
+ inline?: boolean;
14
+ class?: string;
4
15
  };
5
16
 
6
- let { title }: Props = $props();
17
+ let { title, size = 'small', variant, fontWeight, color, class: className }: Props = $props();
18
+
19
+ const styles = tv({
20
+ base: 'ps-6 transition-all duration-200',
21
+ variants: {
22
+ variant: {
23
+ compact: 'py-4',
24
+ default: 'py-6',
25
+ },
26
+ },
27
+ });
7
28
  </script>
8
29
 
9
- <div class="text-sm transition-all duration-200 md:text-sm">
10
- <p class="py-2 ps-4">{title}</p>
30
+ <div class={cleanClass(styles({ variant: variant ?? 'default' }), className)}>
31
+ <Text {color} {size} {fontWeight}>{title}</Text>
11
32
  </div>
@@ -1,5 +1,12 @@
1
+ import type { FontWeight, NavbarVariant, Size, TextColor } from '../../types.js';
1
2
  type Props = {
2
3
  title: string;
4
+ size?: Size;
5
+ color?: TextColor;
6
+ fontWeight?: FontWeight;
7
+ variant?: NavbarVariant;
8
+ inline?: boolean;
9
+ class?: string;
3
10
  };
4
11
  declare const NavbarGroup: import("svelte").Component<Props, {}, "">;
5
12
  type NavbarGroup = ReturnType<typeof NavbarGroup>;
@@ -2,59 +2,94 @@
2
2
  import { page } from '$app/state';
3
3
  import Icon from '../Icon/Icon.svelte';
4
4
  import Link from '../Link/Link.svelte';
5
- import type { IconProps } from '../../types.js';
5
+ import NavbarItem from './NavbarItem.svelte';
6
+ import { t } from '../../services/translation.svelte.js';
7
+ import type { NavbarProps } from '../../types.js';
8
+ import { cleanClass } from '../../utilities/internal.js';
9
+ import { mdiChevronDown, mdiChevronRight } from '@mdi/js';
6
10
  import { tv } from 'tailwind-variants';
7
11
 
8
- type Props = {
9
- title: string;
10
- href: string;
11
- active?: boolean;
12
- variant?: 'compact';
13
- isActive?: () => boolean;
14
- icon?: string | IconProps;
15
- activeIcon?: string | IconProps;
16
- };
17
-
18
12
  const startsWithHref = () => page.url.pathname.startsWith(href);
19
13
 
20
- let { href, isActive: isActiveOverride, title, variant, active: activeOverride, icon, activeIcon }: Props = $props();
14
+ let {
15
+ href,
16
+ isActive: isActiveOverride,
17
+ title,
18
+ variant,
19
+ active: activeOverride,
20
+ icon,
21
+ activeIcon,
22
+ expanded = $bindable(false),
23
+ items,
24
+ class: className,
25
+ }: NavbarProps = $props();
21
26
 
22
- const isActive = isActiveOverride ?? startsWithHref;
27
+ const isActive = $derived(isActiveOverride ?? startsWithHref);
23
28
  let active = $derived(activeOverride ?? isActive());
24
-
25
29
  const iconProps = $derived(typeof icon === 'string' ? { icon } : icon);
26
30
  const activeIconProps = $derived(typeof activeIcon === 'string' ? { icon: activeIcon } : activeIcon);
27
31
 
28
32
  const styles = tv({
29
- base: 'hover:bg-subtle hover:text-primary flex w-full place-items-center gap-4 rounded-e-full px-5 transition-[padding] delay-100 duration-100 group-hover:sm:px-5',
33
+ base: 'hover:bg-subtle hover:text-primary flex w-full place-items-center gap-4 rounded-e-full ps-5 transition-[padding] delay-100 duration-100',
30
34
  variants: {
31
35
  active: {
32
36
  true: 'bg-primary/10 text-primary',
33
37
  false: '',
34
38
  },
35
39
  variant: {
36
- default: 'py-3 ps-5',
37
- compact: 'py-2 ps-3',
40
+ default: 'py-3',
41
+ compact: 'py-2',
38
42
  },
39
43
  },
40
44
  });
41
45
  </script>
42
46
 
43
- <Link
44
- {href}
45
- aria-current={active ? 'page' : undefined}
46
- underline={false}
47
- class={styles({ active, variant: variant ?? 'default' })}
48
- >
49
- <div class="flex w-full place-items-center gap-4 truncate overflow-hidden">
50
- {#if iconProps}
51
- <Icon
52
- size="1.375em"
53
- class="shrink-0"
54
- aria-hidden={true}
55
- {...active && activeIconProps ? activeIconProps : iconProps}
56
- />
47
+ <div>
48
+ <div class="relative flex items-center">
49
+ {#if items}
50
+ <button
51
+ type="button"
52
+ aria-label={expanded ? t('collapse') : t('expand')}
53
+ class="hover:bg-subtle hover:text-primary absolute me-2 hidden h-full rounded-lg px-0.5 md:block"
54
+ onclick={() => (expanded = !expanded)}
55
+ >
56
+ <Icon
57
+ icon={expanded ? mdiChevronDown : mdiChevronRight}
58
+ size="1em"
59
+ class="shrink-0 delay-100 duration-100 "
60
+ aria-hidden
61
+ />
62
+ </button>
57
63
  {/if}
58
- <span class="text-sm font-medium">{title}</span>
64
+ <Link
65
+ {href}
66
+ aria-current={active ? 'page' : undefined}
67
+ underline={false}
68
+ class={cleanClass(styles({ active, variant: variant ?? 'default' }), className)}
69
+ >
70
+ <div class="relative flex w-full place-items-center {variant === 'compact' ? 'gap-2' : 'gap-4'}">
71
+ {#if iconProps}
72
+ <Icon
73
+ size="1.375em"
74
+ class="shrink-0"
75
+ aria-hidden={true}
76
+ {...active && activeIconProps ? activeIconProps : iconProps}
77
+ />
78
+ {/if}
79
+ <span class="truncate text-sm font-medium">{title}</span>
80
+ </div>
81
+ </Link>
59
82
  </div>
60
- </Link>
83
+
84
+ {#if expanded}
85
+ <div>
86
+ {#if Array.isArray(items)}
87
+ {#each items as { class: className, ...item }, i (i)}
88
+ <NavbarItem {variant} {...item} class={cleanClass('ps-8', className)} />
89
+ {/each}
90
+ {:else if items}
91
+ {@render items()}
92
+ {/if}
93
+ </div>
94
+ {/if}
95
+ </div>
@@ -1,13 +1,5 @@
1
- import type { IconProps } from '../../types.js';
2
- type Props = {
3
- title: string;
4
- href: string;
5
- active?: boolean;
6
- variant?: 'compact';
7
- isActive?: () => boolean;
8
- icon?: string | IconProps;
9
- activeIcon?: string | IconProps;
10
- };
11
- declare const NavbarItem: import("svelte").Component<Props, {}, "">;
1
+ import NavbarItem from './NavbarItem.svelte';
2
+ import type { NavbarProps } from '../../types.js';
3
+ declare const NavbarItem: import("svelte").Component<NavbarProps, {}, "expanded">;
12
4
  type NavbarItem = ReturnType<typeof NavbarItem>;
13
5
  export default NavbarItem;
@@ -3,6 +3,8 @@ declare const defaultTranslations: {
3
3
  cancel: string;
4
4
  close: string;
5
5
  confirm: string;
6
+ expand: string;
7
+ collapse: string;
6
8
  code_copy: string;
7
9
  code_copied: string;
8
10
  search_placeholder: string;
@@ -3,6 +3,8 @@ const defaultTranslations = {
3
3
  cancel: 'Cancel',
4
4
  close: 'Close',
5
5
  confirm: 'Confirm',
6
+ expand: 'Expand',
7
+ collapse: 'Collapse',
6
8
  // code
7
9
  code_copy: 'Copy',
8
10
  code_copied: 'Copied',
package/dist/types.d.ts CHANGED
@@ -29,6 +29,19 @@ export type IconLike = string | {
29
29
  };
30
30
  export type MaybeArray<T> = T | T[];
31
31
  export type MaybePromise<T> = T | Promise<T>;
32
+ export type NavbarVariant = 'compact';
33
+ export type NavbarProps = {
34
+ title: string;
35
+ href: string;
36
+ active?: boolean;
37
+ variant?: NavbarVariant;
38
+ isActive?: () => boolean;
39
+ icon?: string | IconProps;
40
+ activeIcon?: string | IconProps;
41
+ expanded?: boolean;
42
+ items?: NavbarProps[] | Snippet;
43
+ class?: string;
44
+ };
32
45
  export type IconProps = {
33
46
  icon: IconLike;
34
47
  title?: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@immich/ui",
3
- "version": "0.58.4",
3
+ "version": "0.59.0",
4
4
  "license": "GNU Affero General Public License version 3",
5
5
  "repository": {
6
6
  "type": "git",