@immich/ui 0.19.0 → 0.20.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.
@@ -1,48 +1,25 @@
1
1
  <script lang="ts">
2
- import { afterNavigate } from '$app/navigation';
3
- import IconButton from '../IconButton/IconButton.svelte';
4
2
  import Scrollable from '../Scrollable/Scrollable.svelte';
5
3
  import { ChildKey } from '../../constants.js';
6
4
  import Child from '../../internal/Child.svelte';
7
5
  import { cleanClass } from '../../utils.js';
8
- import { mdiClose, mdiMenu } from '@mdi/js';
9
- import type { Snippet } from 'svelte';
6
+ import { type Snippet } from 'svelte';
10
7
 
11
8
  type Props = {
12
9
  class?: string;
13
10
  children: Snippet;
14
- noBorder?: boolean;
11
+ open?: boolean;
15
12
  };
16
13
 
17
- let { class: className, children, noBorder = false }: Props = $props();
18
-
19
- afterNavigate(() => {
20
- if (!hidden) {
21
- hidden = true;
22
- }
23
- });
24
-
25
- let hidden = $state(true);
14
+ let { class: className, children, open = $bindable(true) }: Props = $props();
26
15
  </script>
27
16
 
28
17
  <Child for={ChildKey.AppShell} as={ChildKey.AppShellSidebar}>
29
- <IconButton
30
- size="giant"
31
- onclick={() => (hidden = !hidden)}
32
- icon={hidden ? mdiMenu : mdiClose}
33
- shape="round"
34
- color={hidden ? 'primary' : 'secondary'}
35
- variant="filled"
36
- class="absolute bottom-2 end-4 m-2 opacity-100 md:hidden"
37
- aria-label="Menu"
38
- />
39
18
  <Scrollable
40
19
  class={cleanClass(
41
- 'h-dvh w-full shrink-0 bg-light pb-16 text-dark md:relative md:block md:w-min md:pb-0',
42
-
43
- hidden ? 'hidden' : '',
20
+ 'relative shrink-0 bg-light text-dark transition-all duration-200',
21
+ open ? 'w-[min(100vw,16rem)]' : 'w-[0px]',
44
22
  className,
45
- noBorder || 'border-e',
46
23
  )}
47
24
  >
48
25
  {@render children?.()}
@@ -1,9 +1,9 @@
1
- import type { Snippet } from 'svelte';
1
+ import { type Snippet } from 'svelte';
2
2
  type Props = {
3
3
  class?: string;
4
4
  children: Snippet;
5
- noBorder?: boolean;
5
+ open?: boolean;
6
6
  };
7
- declare const AppShellSidebar: import("svelte").Component<Props, {}, "">;
7
+ declare const AppShellSidebar: import("svelte").Component<Props, {}, "open">;
8
8
  type AppShellSidebar = ReturnType<typeof AppShellSidebar>;
9
9
  export default AppShellSidebar;
@@ -138,11 +138,7 @@
138
138
  {/if}
139
139
 
140
140
  {#if footerChild}
141
- <div
142
- class={twMerge(
143
- cleanClass('flex items-center border-t border-t-subtle p-4', footerChild.class),
144
- )}
145
- >
141
+ <div class={twMerge(cleanClass('flex items-center border-t p-4', footerChild.class))}>
146
142
  {@render footerChild.snippet()}
147
143
  </div>
148
144
  {/if}
@@ -23,7 +23,7 @@
23
23
  shape="round"
24
24
  color="secondary"
25
25
  {size}
26
- class="mr-1"
26
+ class="me-1"
27
27
  icon={isVisible ? mdiEyeOffOutline : mdiEyeOutline}
28
28
  onclick={() => (isVisible = !isVisible)}
29
29
  title={isVisible ? t('hidePassword', translations) : t('showPassword', translations)}
@@ -27,7 +27,7 @@
27
27
  }: Props = $props();
28
28
 
29
29
  const styles = tv({
30
- base: 'font-bold leading-none tracking-tight',
30
+ base: 'font-semibold leading-none tracking-tight',
31
31
  variants: {
32
32
  color: {
33
33
  muted: 'text-gray-600 dark:text-gray-400',
@@ -63,7 +63,7 @@
63
63
 
64
64
  <Dialog.Root {open} onOpenChange={onChange}>
65
65
  <Dialog.Portal>
66
- <Dialog.Overlay class="absolute start-0 top-0 flex h-dvh w-screen backdrop-blur" />
66
+ <Dialog.Overlay class="absolute start-0 top-0 flex h-dvh w-screen bg-black/30" />
67
67
  <Dialog.Content
68
68
  class={cleanClass(
69
69
  'absolute start-0 top-0 flex h-dvh w-screen items-center justify-center overflow-hidden sm:p-4',
@@ -71,7 +71,7 @@
71
71
  >
72
72
  <div class={cleanClass('flex h-full w-full flex-col items-center justify-center ')}>
73
73
  <Card class={cleanClass(modalStyles({ size }), className)}>
74
- <CardHeader class="border-b border-gray-400 py-2 dark:border-gray-500">
74
+ <CardHeader class="border-b border-gray-400 py-3 dark:border-gray-500">
75
75
  <div class="flex items-center justify-between gap-2">
76
76
  {#if typeof icon === 'string'}
77
77
  <Icon {icon} size="1.5rem" aria-hidden />
@@ -85,7 +85,7 @@
85
85
  </div>
86
86
  </CardHeader>
87
87
 
88
- <CardBody class="grow">
88
+ <CardBody class="grow p-5">
89
89
  {@render bodyChildren?.snippet()}
90
90
  </CardBody>
91
91
 
@@ -12,7 +12,7 @@
12
12
  isActive?: () => boolean;
13
13
  } & { icon?: string & Omit<IconProps, 'icon'> };
14
14
 
15
- const matchesPath = () => page.url.pathname === href;
15
+ const startsWithHref = () => page.url.pathname.startsWith(href);
16
16
 
17
17
  let {
18
18
  href,
@@ -23,11 +23,11 @@
23
23
  ...iconProps
24
24
  }: Props = $props();
25
25
 
26
- const isActive = isActiveOverride ?? matchesPath;
26
+ const isActive = isActiveOverride ?? startsWithHref;
27
27
  let active = $derived(activeOverride ?? isActive());
28
28
 
29
29
  const styles = tv({
30
- base: 'flex w-full place-items-center gap-4 transition-[padding] delay-100 duration-100 hover:bg-subtle hover:text-primary group-hover:sm:px-5 md:rounded-e-full md:px-5',
30
+ base: 'flex w-full place-items-center gap-4 rounded-e-full px-5 transition-[padding] delay-100 duration-100 hover:bg-subtle hover:text-primary group-hover:sm:px-5',
31
31
  variants: {
32
32
  active: {
33
33
  true: 'bg-primary/10 text-primary',
@@ -78,7 +78,7 @@
78
78
  bind:checked
79
79
  bind:ref
80
80
  id={inputId}
81
- {disabled}
81
+ disabled={disabled || readOnly}
82
82
  {required}
83
83
  class={cleanClass(label && 'w-full', className)}
84
84
  aria-readonly={readOnly}
package/dist/utils.js CHANGED
@@ -1,12 +1,13 @@
1
+ import { twMerge } from 'tailwind-merge';
1
2
  export const cleanClass = (...classNames) => {
2
- return classNames
3
+ return twMerge(classNames
3
4
  .filter((className) => {
4
5
  if (!className || typeof className === 'boolean') {
5
6
  return false;
6
7
  }
7
8
  return typeof className === 'string';
8
9
  })
9
- .join(' ');
10
+ .join(' '));
10
11
  };
11
12
  export const withPrefix = (key) => `immich-ui-${key}`;
12
13
  let _count = 0;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@immich/ui",
3
- "version": "0.19.0",
3
+ "version": "0.20.0",
4
4
  "license": "GNU Affero General Public License version 3",
5
5
  "scripts": {
6
6
  "create": "node scripts/create.js",
@@ -68,5 +68,8 @@
68
68
  "bits-ui": "^1.0.0-next.46",
69
69
  "tailwind-merge": "^2.5.4",
70
70
  "tailwind-variants": "^0.3.0"
71
+ },
72
+ "volta": {
73
+ "node": "22.15.0"
71
74
  }
72
75
  }