@immich/ui 0.30.0 → 0.31.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.
Files changed (71) hide show
  1. package/README.md +9 -17
  2. package/dist/actions/shortcut.js +1 -1
  3. package/dist/components/Alert/Alert.svelte +92 -94
  4. package/dist/components/AppShell/AppShell.svelte +26 -26
  5. package/dist/components/AppShell/AppShellHeader.svelte +8 -8
  6. package/dist/components/AppShell/AppShellSidebar.svelte +20 -20
  7. package/dist/components/AppShell/PageLayout.svelte +29 -35
  8. package/dist/components/Avatar/Avatar.svelte +45 -55
  9. package/dist/components/Button/Button.svelte +3 -3
  10. package/dist/components/Card/Card.svelte +131 -135
  11. package/dist/components/Card/CardBody.svelte +9 -9
  12. package/dist/components/Card/CardDescription.svelte +9 -9
  13. package/dist/components/Card/CardFooter.svelte +9 -9
  14. package/dist/components/Card/CardHeader.svelte +9 -9
  15. package/dist/components/Card/CardTitle.svelte +14 -14
  16. package/dist/components/Checkbox/Checkbox.svelte +109 -110
  17. package/dist/components/CloseButton/CloseButton.svelte +12 -17
  18. package/dist/components/Code/Code.svelte +72 -65
  19. package/dist/components/Code/Code.svelte.d.ts +1 -1
  20. package/dist/components/CodeBlock/CodeBlock.svelte +74 -0
  21. package/dist/components/CodeBlock/CodeBlock.svelte.d.ts +14 -0
  22. package/dist/components/CommandPalette/CommandPalette.svelte +126 -131
  23. package/dist/components/CommandPalette/CommandPaletteItem.svelte +61 -61
  24. package/dist/components/ConfirmModal/ConfirmModal.svelte +48 -48
  25. package/dist/components/Container/Container.svelte +25 -25
  26. package/dist/components/Field/Field.svelte +21 -21
  27. package/dist/components/FormatBytes/FormatBytes.svelte +9 -9
  28. package/dist/components/Heading/Heading.svelte +41 -47
  29. package/dist/components/HelperText/HelperText.svelte +17 -17
  30. package/dist/components/Icon/Icon.svelte +37 -42
  31. package/dist/components/IconButton/IconButton.svelte +6 -13
  32. package/dist/components/Input/Input.svelte +149 -152
  33. package/dist/components/Kbd/Kbd.svelte +25 -25
  34. package/dist/components/Label/Label.svelte +6 -6
  35. package/dist/components/Link/Link.svelte +18 -25
  36. package/dist/components/LoadingSpinner/LoadingSpinner.svelte +46 -46
  37. package/dist/components/Logo/Logo.svelte +53 -53
  38. package/dist/components/Modal/Modal.svelte +110 -114
  39. package/dist/components/Modal/ModalBody.svelte +8 -8
  40. package/dist/components/Modal/ModalFooter.svelte +8 -8
  41. package/dist/components/Modal/ModalHeader.svelte +8 -8
  42. package/dist/components/MultiSelect/MultiSelect.svelte +7 -7
  43. package/dist/components/Navbar/NavbarGroup.svelte +5 -5
  44. package/dist/components/Navbar/NavbarItem.svelte +59 -61
  45. package/dist/components/PasswordInput/PasswordInput.svelte +29 -31
  46. package/dist/components/Scrollable/Scrollable.svelte +49 -49
  47. package/dist/components/Select/Select.svelte +9 -9
  48. package/dist/components/Stack/HStack.svelte +4 -4
  49. package/dist/components/Stack/Stack.svelte +62 -62
  50. package/dist/components/Stack/VStack.svelte +4 -4
  51. package/dist/components/SupporterBadge/SupporterBadge.svelte +80 -80
  52. package/dist/components/Switch/Switch.svelte +95 -96
  53. package/dist/components/Text/Text.svelte +27 -34
  54. package/dist/components/Textarea/Textarea.svelte +103 -104
  55. package/dist/components/ThemeSwitcher/ThemeSwitcher.svelte +30 -43
  56. package/dist/index.d.ts +1 -0
  57. package/dist/index.js +1 -0
  58. package/dist/internal/Button.svelte +177 -177
  59. package/dist/internal/Child.svelte +21 -21
  60. package/dist/internal/Select.svelte +151 -158
  61. package/dist/internal/Text.svelte +50 -50
  62. package/dist/internal/Text.svelte.d.ts +2 -1
  63. package/dist/services/translation.svelte.d.ts +2 -0
  64. package/dist/services/translation.svelte.js +4 -0
  65. package/dist/site/SiteFooter.svelte +60 -76
  66. package/dist/site/SiteFooterLink.svelte +14 -14
  67. package/dist/site/constants.d.ts +7 -3
  68. package/dist/site/constants.js +17 -3
  69. package/dist/theme/default.css +40 -40
  70. package/dist/utilities/byte-units.js +2 -13
  71. package/package.json +77 -77
@@ -1,15 +1,15 @@
1
1
  <script lang="ts">
2
- import { ChildKey } from '../../constants.js';
3
- import Child from '../../internal/Child.svelte';
4
- import type { Snippet } from 'svelte';
2
+ import { ChildKey } from '../../constants.js';
3
+ import Child from '../../internal/Child.svelte';
4
+ import type { Snippet } from 'svelte';
5
5
 
6
- type Props = {
7
- children: Snippet;
8
- };
6
+ type Props = {
7
+ children: Snippet;
8
+ };
9
9
 
10
- let { children }: Props = $props();
10
+ let { children }: Props = $props();
11
11
  </script>
12
12
 
13
13
  <Child for={ChildKey.Modal} as={ChildKey.ModalHeader}>
14
- {@render children?.()}
14
+ {@render children?.()}
15
15
  </Child>
@@ -1,14 +1,14 @@
1
1
  <script lang="ts">
2
- import InternalSelect from '../../internal/Select.svelte';
3
- import type { MultiSelectProps, SelectItem } from '../../types.js';
2
+ import InternalSelect from '../../internal/Select.svelte';
3
+ import type { MultiSelectProps, SelectItem } from '../../types.js';
4
4
 
5
- type T = SelectItem;
5
+ type T = SelectItem;
6
6
 
7
- let { onChange, values = $bindable([]), ...restProps }: MultiSelectProps<T> = $props();
7
+ let { onChange, values = $bindable([]), ...restProps }: MultiSelectProps<T> = $props();
8
8
 
9
- const handleChange = (items: T[]) => {
10
- onChange?.(items);
11
- };
9
+ const handleChange = (items: T[]) => {
10
+ onChange?.(items);
11
+ };
12
12
  </script>
13
13
 
14
14
  <InternalSelect multiple bind:values onChange={handleChange} {...restProps} />
@@ -1,11 +1,11 @@
1
1
  <script lang="ts">
2
- type Props = {
3
- title: string;
4
- };
2
+ type Props = {
3
+ title: string;
4
+ };
5
5
 
6
- let { title }: Props = $props();
6
+ let { title }: Props = $props();
7
7
  </script>
8
8
 
9
9
  <div class="text-sm transition-all duration-200 md:text-sm">
10
- <p class="py-2 ps-4">{title}</p>
10
+ <p class="py-2 ps-4">{title}</p>
11
11
  </div>
@@ -1,73 +1,71 @@
1
1
  <script lang="ts">
2
- import { page } from '$app/state';
3
- import Icon from '../Icon/Icon.svelte';
4
- import Link from '../Link/Link.svelte';
5
- import type { IconProps } from '../../types.js';
6
- import { tv } from 'tailwind-variants';
2
+ import { page } from '$app/state';
3
+ import Icon from '../Icon/Icon.svelte';
4
+ import Link from '../Link/Link.svelte';
5
+ import type { IconProps } from '../../types.js';
6
+ import { tv } from 'tailwind-variants';
7
7
 
8
- type Props = {
9
- title: string;
10
- href: string;
11
- external?: boolean;
12
- active?: boolean;
13
- variant?: 'compact';
14
- isActive?: () => boolean;
15
- icon?: string | IconProps;
16
- activeIcon?: string | IconProps;
17
- };
8
+ type Props = {
9
+ title: string;
10
+ href: string;
11
+ external?: boolean;
12
+ active?: boolean;
13
+ variant?: 'compact';
14
+ isActive?: () => boolean;
15
+ icon?: string | IconProps;
16
+ activeIcon?: string | IconProps;
17
+ };
18
18
 
19
- const startsWithHref = () => page.url.pathname.startsWith(href);
19
+ const startsWithHref = () => page.url.pathname.startsWith(href);
20
20
 
21
- let {
22
- href,
23
- external,
24
- isActive: isActiveOverride,
25
- title,
26
- variant,
27
- active: activeOverride,
28
- icon,
29
- activeIcon,
30
- }: Props = $props();
21
+ let {
22
+ href,
23
+ external,
24
+ isActive: isActiveOverride,
25
+ title,
26
+ variant,
27
+ active: activeOverride,
28
+ icon,
29
+ activeIcon,
30
+ }: Props = $props();
31
31
 
32
- const isActive = isActiveOverride ?? startsWithHref;
33
- let active = $derived(activeOverride ?? isActive());
32
+ const isActive = isActiveOverride ?? startsWithHref;
33
+ let active = $derived(activeOverride ?? isActive());
34
34
 
35
- const iconProps = $derived(typeof icon === 'string' ? { icon } : icon);
36
- const activeIconProps = $derived(
37
- typeof activeIcon === 'string' ? { icon: activeIcon } : activeIcon,
38
- );
35
+ const iconProps = $derived(typeof icon === 'string' ? { icon } : icon);
36
+ const activeIconProps = $derived(typeof activeIcon === 'string' ? { icon: activeIcon } : activeIcon);
39
37
 
40
- const styles = tv({
41
- 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',
42
- variants: {
43
- active: {
44
- true: 'bg-primary/10 text-primary',
45
- false: '',
46
- },
47
- variant: {
48
- default: 'py-3 ps-5',
49
- compact: 'py-2 ps-3',
50
- },
51
- },
52
- });
38
+ const styles = tv({
39
+ 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',
40
+ variants: {
41
+ active: {
42
+ true: 'bg-primary/10 text-primary',
43
+ false: '',
44
+ },
45
+ variant: {
46
+ default: 'py-3 ps-5',
47
+ compact: 'py-2 ps-3',
48
+ },
49
+ },
50
+ });
53
51
  </script>
54
52
 
55
53
  <Link
56
- {href}
57
- {external}
58
- aria-current={active ? 'page' : undefined}
59
- underline={false}
60
- class={styles({ active, variant: variant ?? 'default' })}
54
+ {href}
55
+ {external}
56
+ aria-current={active ? 'page' : undefined}
57
+ underline={false}
58
+ class={styles({ active, variant: variant ?? 'default' })}
61
59
  >
62
- <div class="flex w-full place-items-center gap-4 truncate overflow-hidden">
63
- {#if iconProps}
64
- <Icon
65
- size="1.375em"
66
- class="shrink-0"
67
- aria-hidden={true}
68
- {...active && activeIconProps ? activeIconProps : iconProps}
69
- />
70
- {/if}
71
- <span class="text-sm font-medium">{title}</span>
72
- </div>
60
+ <div class="flex w-full place-items-center gap-4 truncate overflow-hidden">
61
+ {#if iconProps}
62
+ <Icon
63
+ size="1.375em"
64
+ class="shrink-0"
65
+ aria-hidden={true}
66
+ {...active && activeIconProps ? activeIconProps : iconProps}
67
+ />
68
+ {/if}
69
+ <span class="text-sm font-medium">{title}</span>
70
+ </div>
73
71
  </Link>
@@ -1,38 +1,36 @@
1
1
  <script lang="ts">
2
- import IconButton from '../IconButton/IconButton.svelte';
3
- import Input from '../Input/Input.svelte';
4
- import { t } from '../../services/translation.svelte.js';
5
- import type { PasswordInputProps } from '../../types.js';
6
- import { mdiEyeOffOutline, mdiEyeOutline } from '@mdi/js';
2
+ import IconButton from '../IconButton/IconButton.svelte';
3
+ import Input from '../Input/Input.svelte';
4
+ import { t } from '../../services/translation.svelte.js';
5
+ import type { PasswordInputProps } from '../../types.js';
6
+ import { mdiEyeOffOutline, mdiEyeOutline } from '@mdi/js';
7
7
 
8
- let {
9
- value = $bindable<string>(),
10
- translations,
11
- isVisible = $bindable<boolean>(false),
12
- color = 'secondary',
13
- size,
14
- ...props
15
- }: PasswordInputProps = $props();
8
+ let {
9
+ value = $bindable<string>(),
10
+ translations,
11
+ isVisible = $bindable<boolean>(false),
12
+ color = 'secondary',
13
+ size,
14
+ ...props
15
+ }: PasswordInputProps = $props();
16
16
 
17
- let labelValue = $derived(
18
- isVisible ? t('hide_password', translations) : t('show_password', translations),
19
- );
17
+ let labelValue = $derived(isVisible ? t('hide_password', translations) : t('show_password', translations));
20
18
  </script>
21
19
 
22
20
  <Input bind:value {size} type={isVisible ? 'text' : 'password'} {color} {...props}>
23
- {#snippet trailingIcon()}
24
- {#if value?.length > 0}
25
- <IconButton
26
- variant="ghost"
27
- shape="round"
28
- color="secondary"
29
- {size}
30
- class="me-1"
31
- icon={isVisible ? mdiEyeOffOutline : mdiEyeOutline}
32
- onclick={() => (isVisible = !isVisible)}
33
- title={labelValue}
34
- aria-label={labelValue}
35
- />
36
- {/if}
37
- {/snippet}
21
+ {#snippet trailingIcon()}
22
+ {#if value?.length > 0}
23
+ <IconButton
24
+ variant="ghost"
25
+ shape="round"
26
+ color="secondary"
27
+ {size}
28
+ class="me-1"
29
+ icon={isVisible ? mdiEyeOffOutline : mdiEyeOutline}
30
+ onclick={() => (isVisible = !isVisible)}
31
+ title={labelValue}
32
+ aria-label={labelValue}
33
+ />
34
+ {/if}
35
+ {/snippet}
38
36
  </Input>
@@ -1,67 +1,67 @@
1
1
  <script lang="ts">
2
- import { afterNavigate } from '$app/navigation';
3
- import { cleanClass } from '../../utils.js';
4
- import type { Snippet } from 'svelte';
2
+ import { afterNavigate } from '$app/navigation';
3
+ import { cleanClass } from '../../utils.js';
4
+ import type { Snippet } from 'svelte';
5
5
 
6
- type Props = {
7
- class?: string;
8
- children?: Snippet;
9
- transition?: TransitionEvent;
10
- ref?: HTMLDivElement;
11
- resetOnNavigate?: boolean;
12
- };
6
+ type Props = {
7
+ class?: string;
8
+ children?: Snippet;
9
+ transition?: TransitionEvent;
10
+ ref?: HTMLDivElement;
11
+ resetOnNavigate?: boolean;
12
+ };
13
13
 
14
- let { resetOnNavigate = false, class: className, children, ref = $bindable() }: Props = $props();
14
+ let { resetOnNavigate = false, class: className, children, ref = $bindable() }: Props = $props();
15
15
 
16
- afterNavigate(() => {
17
- if (resetOnNavigate) {
18
- ref?.scrollTo(0, 0);
19
- }
20
- });
16
+ afterNavigate(() => {
17
+ if (resetOnNavigate) {
18
+ ref?.scrollTo(0, 0);
19
+ }
20
+ });
21
21
  </script>
22
22
 
23
23
  <div bind:this={ref} class={cleanClass('immich-scrollbar h-full w-full overflow-auto', className)}>
24
- {@render children?.()}
24
+ {@render children?.()}
25
25
  </div>
26
26
 
27
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
- }
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
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
- }
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
47
 
48
- /* Handle on hover */
49
- .immich-scrollbar::-webkit-scrollbar-thumb:hover {
50
- background: #4250afad;
51
- border-radius: 16px;
52
- }
48
+ /* Handle on hover */
49
+ .immich-scrollbar::-webkit-scrollbar-thumb:hover {
50
+ background: #4250afad;
51
+ border-radius: 16px;
52
+ }
53
53
 
54
- /*
54
+ /*
55
55
  * Show scrollbar elements when hovering or actively scrolling
56
56
  * Applies to the main scrollbar, track, and thumb components
57
57
  * Changes visibility from hidden to visible on user interaction
58
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
- }
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
67
  </style>
@@ -1,17 +1,17 @@
1
1
  <script lang="ts">
2
- import InternalSelect from '../../internal/Select.svelte';
3
- import type { SelectItem, SelectProps } from '../../types.js';
2
+ import InternalSelect from '../../internal/Select.svelte';
3
+ import type { SelectItem, SelectProps } from '../../types.js';
4
4
 
5
- type T = SelectItem;
5
+ type T = SelectItem;
6
6
 
7
- let { onChange, value = $bindable(), ...restProps }: SelectProps<T> = $props();
7
+ let { onChange, value = $bindable(), ...restProps }: SelectProps<T> = $props();
8
8
 
9
- let values = $derived(value ? [value] : []);
9
+ let values = $derived(value ? [value] : []);
10
10
 
11
- const handleChange = (items: T[]) => {
12
- value = items[0];
13
- onChange?.(value);
14
- };
11
+ const handleChange = (items: T[]) => {
12
+ value = items[0];
13
+ onChange?.(value);
14
+ };
15
15
  </script>
16
16
 
17
17
  <InternalSelect bind:values onChange={handleChange} {...restProps} />
@@ -1,10 +1,10 @@
1
1
  <script lang="ts">
2
- import Stack from './Stack.svelte';
3
- import type { HStackProps } from '../../types.js';
2
+ import Stack from './Stack.svelte';
3
+ import type { HStackProps } from '../../types.js';
4
4
 
5
- const { class: className, children, ...props }: HStackProps = $props();
5
+ const { class: className, children, ...props }: HStackProps = $props();
6
6
  </script>
7
7
 
8
8
  <Stack direction="row" align="center" class={className} {...props}>
9
- {@render children()}
9
+ {@render children()}
10
10
  </Stack>
@@ -1,70 +1,70 @@
1
1
  <script lang="ts">
2
- import type { StackProps } from '../../types.js';
3
- import { cleanClass } from '../../utils.js';
4
- import { tv } from 'tailwind-variants';
2
+ import type { StackProps } from '../../types.js';
3
+ import { cleanClass } from '../../utils.js';
4
+ import { tv } from 'tailwind-variants';
5
5
 
6
- const {
7
- align,
8
- direction = 'column',
9
- wrap = false,
10
- class: className,
11
- fullWidth,
12
- fullHeight,
13
- gap = 2,
14
- children,
15
- }: StackProps = $props();
6
+ const {
7
+ align,
8
+ direction = 'column',
9
+ wrap = false,
10
+ class: className,
11
+ fullWidth,
12
+ fullHeight,
13
+ gap = 2,
14
+ children,
15
+ }: StackProps = $props();
16
16
 
17
- const styles = tv({
18
- base: 'flex gap-2',
19
- variants: {
20
- direction: {
21
- row: 'flex-row',
22
- column: 'flex-col',
23
- },
24
- align: {
25
- start: 'items-start',
26
- center: 'items-center',
27
- end: 'items-end',
28
- },
29
- fullWidth: {
30
- true: 'w-full',
31
- false: '',
32
- },
33
- fullHeight: {
34
- true: 'h-full',
35
- false: '',
36
- },
37
- gap: {
38
- 0: 'gap-0',
39
- 1: 'gap-1',
40
- 2: 'gap-2',
41
- 3: 'gap-3',
42
- 4: 'gap-4',
43
- 5: 'gap-5',
44
- 6: 'gap-6',
45
- 7: 'gap-7',
46
- 8: 'gap-8',
47
- },
48
- wrap: {
49
- true: 'flex-wrap',
50
- false: '',
51
- },
52
- },
53
- });
17
+ const styles = tv({
18
+ base: 'flex gap-2',
19
+ variants: {
20
+ direction: {
21
+ row: 'flex-row',
22
+ column: 'flex-col',
23
+ },
24
+ align: {
25
+ start: 'items-start',
26
+ center: 'items-center',
27
+ end: 'items-end',
28
+ },
29
+ fullWidth: {
30
+ true: 'w-full',
31
+ false: '',
32
+ },
33
+ fullHeight: {
34
+ true: 'h-full',
35
+ false: '',
36
+ },
37
+ gap: {
38
+ 0: 'gap-0',
39
+ 1: 'gap-1',
40
+ 2: 'gap-2',
41
+ 3: 'gap-3',
42
+ 4: 'gap-4',
43
+ 5: 'gap-5',
44
+ 6: 'gap-6',
45
+ 7: 'gap-7',
46
+ 8: 'gap-8',
47
+ },
48
+ wrap: {
49
+ true: 'flex-wrap',
50
+ false: '',
51
+ },
52
+ },
53
+ });
54
54
  </script>
55
55
 
56
56
  <div
57
- class={cleanClass(
58
- styles({
59
- align,
60
- direction,
61
- gap,
62
- wrap,
63
- fullHeight,
64
- fullWidth,
65
- }),
66
- className,
67
- )}
57
+ class={cleanClass(
58
+ styles({
59
+ align,
60
+ direction,
61
+ gap,
62
+ wrap,
63
+ fullHeight,
64
+ fullWidth,
65
+ }),
66
+ className,
67
+ )}
68
68
  >
69
- {@render children()}
69
+ {@render children()}
70
70
  </div>
@@ -1,10 +1,10 @@
1
1
  <script lang="ts">
2
- import Stack from './Stack.svelte';
3
- import type { VStackProps } from '../../types.js';
2
+ import Stack from './Stack.svelte';
3
+ import type { VStackProps } from '../../types.js';
4
4
 
5
- const { class: className, children, ...props }: VStackProps = $props();
5
+ const { class: className, children, ...props }: VStackProps = $props();
6
6
  </script>
7
7
 
8
8
  <Stack direction="column" align="center" class={className} {...props}>
9
- {@render children()}
9
+ {@render children()}
10
10
  </Stack>