@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,122 +1,121 @@
1
1
  <script lang="ts">
2
- import { getFieldContext } from '../../common/context.svelte.js';
3
- import Icon from '../Icon/Icon.svelte';
4
- import Label from '../Label/Label.svelte';
5
- import Text from '../Text/Text.svelte';
6
- import type { Color, Shape, Size } from '../../types.js';
7
- import { cleanClass, generateId } from '../../utils.js';
8
- import { mdiCheck, mdiMinus } from '@mdi/js';
9
- import { Checkbox as CheckboxPrimitive, type WithoutChildrenOrChild } from 'bits-ui';
10
- import { tv } from 'tailwind-variants';
2
+ import { getFieldContext } from '../../common/context.svelte.js';
3
+ import Icon from '../Icon/Icon.svelte';
4
+ import Label from '../Label/Label.svelte';
5
+ import Text from '../Text/Text.svelte';
6
+ import type { Color, Shape, Size } from '../../types.js';
7
+ import { cleanClass, generateId } from '../../utils.js';
8
+ import { mdiCheck, mdiMinus } from '@mdi/js';
9
+ import { Checkbox as CheckboxPrimitive, type WithoutChildrenOrChild } from 'bits-ui';
10
+ import { tv } from 'tailwind-variants';
11
11
 
12
- type CheckboxProps = WithoutChildrenOrChild<CheckboxPrimitive.RootProps> & {
13
- color?: Color;
14
- shape?: Shape;
15
- size?: Size;
16
- };
12
+ type CheckboxProps = WithoutChildrenOrChild<CheckboxPrimitive.RootProps> & {
13
+ color?: Color;
14
+ shape?: Shape;
15
+ size?: Size;
16
+ };
17
17
 
18
- let {
19
- ref = $bindable(null),
20
- checked = $bindable(false),
21
- class: className,
22
- color = 'primary',
23
- shape = 'semi-round',
24
- size = 'small',
25
- ...restProps
26
- }: CheckboxProps = $props();
18
+ let {
19
+ ref = $bindable(null),
20
+ checked = $bindable(false),
21
+ class: className,
22
+ color = 'primary',
23
+ shape = 'semi-round',
24
+ size = 'small',
25
+ ...restProps
26
+ }: CheckboxProps = $props();
27
27
 
28
- const { readOnly, required, invalid, disabled, label, description, ...labelProps } =
29
- $derived(getFieldContext());
28
+ const { readOnly, required, invalid, disabled, label, description, ...labelProps } = $derived(getFieldContext());
30
29
 
31
- const containerStyles = tv({
32
- base: 'ring-offset-background focus-visible:ring-ring peer data-[state=checked]:bg-primary box-content overflow-hidden border-2 focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:outline-none disabled:cursor-not-allowed disabled:opacity-50 data-[disabled=true]:cursor-not-allowed data-[disabled=true]:opacity-50',
33
- variants: {
34
- shape: {
35
- rectangle: 'rounded-none',
36
- 'semi-round': 'rounded-lg',
37
- round: 'rounded-full',
38
- },
39
- color: {
40
- primary: 'border-primary',
41
- secondary: 'border-dark',
42
- success: 'border-success',
43
- danger: 'border-danger',
44
- warning: 'border-warning',
45
- info: 'border-info',
46
- },
47
- size: {
48
- tiny: 'size-4',
49
- small: 'size-5',
50
- medium: 'size-6',
51
- large: 'size-8',
52
- giant: 'size-10',
53
- },
54
- roundedSize: {
55
- tiny: 'rounded-md',
56
- small: 'rounded-md',
57
- medium: 'rounded-md',
58
- large: 'rounded-lg',
59
- giant: 'rounded-xl',
60
- },
61
- },
62
- });
30
+ const containerStyles = tv({
31
+ base: 'ring-offset-background focus-visible:ring-ring peer data-[state=checked]:bg-primary box-content overflow-hidden border-2 focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:outline-none disabled:cursor-not-allowed disabled:opacity-50 data-[disabled=true]:cursor-not-allowed data-[disabled=true]:opacity-50',
32
+ variants: {
33
+ shape: {
34
+ rectangle: 'rounded-none',
35
+ 'semi-round': 'rounded-lg',
36
+ round: 'rounded-full',
37
+ },
38
+ color: {
39
+ primary: 'border-primary',
40
+ secondary: 'border-dark',
41
+ success: 'border-success',
42
+ danger: 'border-danger',
43
+ warning: 'border-warning',
44
+ info: 'border-info',
45
+ },
46
+ size: {
47
+ tiny: 'size-4',
48
+ small: 'size-5',
49
+ medium: 'size-6',
50
+ large: 'size-8',
51
+ giant: 'size-10',
52
+ },
53
+ roundedSize: {
54
+ tiny: 'rounded-md',
55
+ small: 'rounded-md',
56
+ medium: 'rounded-md',
57
+ large: 'rounded-lg',
58
+ giant: 'rounded-xl',
59
+ },
60
+ },
61
+ });
63
62
 
64
- const iconStyles = tv({
65
- variants: {
66
- fullWidth: {
67
- true: 'w-full',
68
- },
69
- color: {
70
- primary: 'bg-primary text-light hover:bg-primary/80',
71
- secondary: 'bg-dark text-light hover:bg-dark/80',
72
- success: 'bg-success text-light hover:bg-success/80',
73
- danger: 'bg-danger text-light hover:bg-danger/80',
74
- warning: 'bg-warning text-light hover:bg-warning/80',
75
- info: 'bg-info text-light hover:bg-info/80',
76
- },
77
- },
78
- });
63
+ const iconStyles = tv({
64
+ variants: {
65
+ fullWidth: {
66
+ true: 'w-full',
67
+ },
68
+ color: {
69
+ primary: 'bg-primary text-light hover:bg-primary/80',
70
+ secondary: 'bg-dark text-light hover:bg-dark/80',
71
+ success: 'bg-success text-light hover:bg-success/80',
72
+ danger: 'bg-danger text-light hover:bg-danger/80',
73
+ warning: 'bg-warning text-light hover:bg-warning/80',
74
+ info: 'bg-info text-light hover:bg-info/80',
75
+ },
76
+ },
77
+ });
79
78
 
80
- const id = generateId();
81
- const inputId = `input-${id}`;
82
- const labelId = `label-${id}`;
83
- const descriptionId = $derived(description ? `description-${id}` : undefined);
79
+ const id = generateId();
80
+ const inputId = `input-${id}`;
81
+ const labelId = `label-${id}`;
82
+ const descriptionId = $derived(description ? `description-${id}` : undefined);
84
83
  </script>
85
84
 
86
85
  <div class="flex flex-col gap-1">
87
- {#if label}
88
- <Label id={labelId} for={inputId} {label} {...labelProps} />
89
- {#if description}
90
- <Text color="secondary" size="small" id={descriptionId}>{description}</Text>
91
- {/if}
92
- {/if}
86
+ {#if label}
87
+ <Label id={labelId} for={inputId} {label} {...labelProps} />
88
+ {#if description}
89
+ <Text color="secondary" size="small" id={descriptionId}>{description}</Text>
90
+ {/if}
91
+ {/if}
93
92
 
94
- <CheckboxPrimitive.Root
95
- bind:ref
96
- class={cleanClass(
97
- containerStyles({
98
- size,
99
- color: invalid ? 'danger' : color,
100
- shape,
101
- roundedSize: shape === 'semi-round' ? size : undefined,
102
- }),
103
- className,
104
- )}
105
- bind:checked
106
- disabled={disabled || readOnly}
107
- {required}
108
- aria-readonly={disabled || readOnly}
109
- aria-describedby={descriptionId}
110
- {...restProps}
111
- >
112
- {#snippet children({ checked, indeterminate })}
113
- <div class={cleanClass('flex items-center justify-center text-current')}>
114
- {#if indeterminate}
115
- <Icon icon={mdiMinus} size="100%" class={cleanClass(iconStyles({ color }))} />
116
- {:else if checked}
117
- <Icon icon={mdiCheck} size="100%" class={cleanClass(iconStyles({ color }))} />
118
- {/if}
119
- </div>
120
- {/snippet}
121
- </CheckboxPrimitive.Root>
93
+ <CheckboxPrimitive.Root
94
+ bind:ref
95
+ class={cleanClass(
96
+ containerStyles({
97
+ size,
98
+ color: invalid ? 'danger' : color,
99
+ shape,
100
+ roundedSize: shape === 'semi-round' ? size : undefined,
101
+ }),
102
+ className,
103
+ )}
104
+ bind:checked
105
+ disabled={disabled || readOnly}
106
+ {required}
107
+ aria-readonly={disabled || readOnly}
108
+ aria-describedby={descriptionId}
109
+ {...restProps}
110
+ >
111
+ {#snippet children({ checked, indeterminate })}
112
+ <div class={cleanClass('flex items-center justify-center text-current')}>
113
+ {#if indeterminate}
114
+ <Icon icon={mdiMinus} size="100%" class={cleanClass(iconStyles({ color }))} />
115
+ {:else if checked}
116
+ <Icon icon={mdiCheck} size="100%" class={cleanClass(iconStyles({ color }))} />
117
+ {/if}
118
+ </div>
119
+ {/snippet}
120
+ </CheckboxPrimitive.Root>
122
121
  </div>
@@ -1,23 +1,18 @@
1
1
  <script lang="ts">
2
- import IconButton from '../IconButton/IconButton.svelte';
3
- import { t } from '../../services/translation.svelte.js';
4
- import type { CloseButtonProps } from '../../types.js';
5
- import { mdiClose } from '@mdi/js';
2
+ import IconButton from '../IconButton/IconButton.svelte';
3
+ import { t } from '../../services/translation.svelte.js';
4
+ import type { CloseButtonProps } from '../../types.js';
5
+ import { mdiClose } from '@mdi/js';
6
6
 
7
- const {
8
- size = 'medium',
9
- variant = 'ghost',
10
- translations,
11
- ...restProps
12
- }: CloseButtonProps = $props();
7
+ const { size = 'medium', variant = 'ghost', translations, ...restProps }: CloseButtonProps = $props();
13
8
  </script>
14
9
 
15
10
  <IconButton
16
- {...restProps}
17
- icon={mdiClose}
18
- shape="round"
19
- {variant}
20
- {size}
21
- color="secondary"
22
- aria-label={t('close', translations)}
11
+ {...restProps}
12
+ icon={mdiClose}
13
+ shape="round"
14
+ {variant}
15
+ {size}
16
+ color="secondary"
17
+ aria-label={t('close', translations)}
23
18
  />
@@ -1,76 +1,83 @@
1
1
  <script lang="ts">
2
- import type { Size, TextColor } from '../../types.js';
3
- import { cleanClass } from '../../utils.js';
4
- import type { Snippet } from 'svelte';
5
- import type { HTMLAttributes } from 'svelte/elements';
6
- import { tv } from 'tailwind-variants';
2
+ import type { Size, TextColor } from '../../types.js';
3
+ import { cleanClass } from '../../utils.js';
4
+ import type { Snippet } from 'svelte';
5
+ import type { HTMLAttributes } from 'svelte/elements';
6
+ import { tv } from 'tailwind-variants';
7
7
 
8
- type Props = {
9
- color?: TextColor;
10
- size?: Size;
11
- variant?: 'filled';
12
- class?: string;
13
- children: Snippet;
14
- } & HTMLAttributes<HTMLElement>;
8
+ type Props = {
9
+ color?: TextColor;
10
+ size?: Size;
11
+ variant?: 'filled' | 'ghost' | 'outline';
12
+ class?: string;
13
+ children: Snippet;
14
+ } & HTMLAttributes<HTMLElement>;
15
15
 
16
- const {
17
- class: className,
18
- size = 'medium',
19
- variant,
20
- color = 'secondary',
21
- children,
22
- ...restProps
23
- }: Props = $props();
16
+ const {
17
+ class: className,
18
+ size = 'medium',
19
+ variant = 'filled',
20
+ color = 'primary',
21
+ children,
22
+ ...restProps
23
+ }: Props = $props();
24
24
 
25
- const styles = tv({
26
- base: 'font-monospace',
27
- variants: {
28
- textColor: {
29
- muted: 'text-gray-600 dark:text-gray-400',
30
- primary: 'text-primary',
31
- secondary: 'text-dark',
32
- success: 'text-success',
33
- danger: 'text-danger',
34
- warning: 'text-warning',
35
- info: 'text-info',
36
- },
25
+ const styles = tv({
26
+ base: 'font-monospace rounded-lg px-2 py-1',
27
+ variants: {
28
+ ghostTheme: {
29
+ false: '',
30
+ muted: 'text-gray-600 dark:text-gray-400',
31
+ primary: 'text-primary',
32
+ secondary: 'text-dark',
33
+ success: 'text-success',
34
+ danger: 'text-danger',
35
+ warning: 'text-warning',
36
+ info: 'text-info',
37
+ },
37
38
 
38
- filled: {
39
- true: 'rounded-lg p-3',
40
- false: '',
41
- },
39
+ filledTheme: {
40
+ false: '',
41
+ muted: 'text-dark bg-subtle',
42
+ primary: 'text-dark dark:bg-primary/20 bg-gray-200 dark:text-gray-200',
43
+ secondary: 'text-light bg-gray-700 dark:bg-gray-200',
44
+ success: 'bg-success text-light',
45
+ danger: 'bg-danger text-light',
46
+ warning: 'bg-warning text-light',
47
+ info: 'bg-info text-light',
48
+ },
42
49
 
43
- filledColor: {
44
- false: '',
45
- muted: 'text-light bg-gray-600 dark:bg-gray-800',
46
- primary: 'text-primary dark:bg-primary/20 bg-gray-200 dark:text-gray-200',
47
- secondary: 'text-light bg-gray-700 dark:bg-gray-200',
48
- success: 'bg-success text-light',
49
- danger: 'bg-danger text-light',
50
- warning: 'bg-warning text-light',
51
- info: 'bg-info text-dark',
52
- },
50
+ outlineTheme: {
51
+ false: '',
52
+ muted: 'border border-gray-600 text-gray-600 dark:border-gray-400 dark:text-gray-400',
53
+ primary: 'border-primary text-primary border',
54
+ secondary: 'border-dark text-dark border',
55
+ success: 'border-success text-success border',
56
+ danger: 'border-danger text-danger border',
57
+ warning: 'border-warning text-warning border',
58
+ info: 'border-info text-info border',
59
+ },
53
60
 
54
- size: {
55
- tiny: 'text-xs',
56
- small: 'text-sm',
57
- medium: 'text-base',
58
- large: 'text-lg',
59
- giant: 'text-xl',
60
- },
61
- },
62
- });
61
+ size: {
62
+ tiny: 'text-xs',
63
+ small: 'text-sm',
64
+ medium: 'text-base',
65
+ large: 'text-lg',
66
+ giant: 'text-xl',
67
+ },
68
+ },
69
+ });
63
70
  </script>
64
71
 
65
72
  <code
66
- class={cleanClass(
67
- styles({
68
- filled: variant === 'filled',
69
- filledColor: variant === 'filled' && color,
70
- textColor: color,
71
- size,
72
- }),
73
- className,
74
- )}
75
- {...restProps}>{@render children()}</code
73
+ class={cleanClass(
74
+ styles({
75
+ filledTheme: variant === 'filled' ? color : false,
76
+ outlineTheme: variant === 'outline' ? color : false,
77
+ ghostTheme: variant === 'ghost' ? color : false,
78
+ size,
79
+ }),
80
+ className,
81
+ )}
82
+ {...restProps}>{@render children()}</code
76
83
  >
@@ -4,7 +4,7 @@ import type { HTMLAttributes } from 'svelte/elements';
4
4
  type Props = {
5
5
  color?: TextColor;
6
6
  size?: Size;
7
- variant?: 'filled';
7
+ variant?: 'filled' | 'ghost' | 'outline';
8
8
  class?: string;
9
9
  children: Snippet;
10
10
  } & HTMLAttributes<HTMLElement>;
@@ -0,0 +1,74 @@
1
+ <script lang="ts">
2
+ import Card from '../Card/Card.svelte';
3
+ import IconButton from '../IconButton/IconButton.svelte';
4
+ import { theme } from '../../services/theme.svelte.js';
5
+ import { Theme, type TranslationProps } from '../../types.js';
6
+ import { t } from '../../index.ts/services/translation.svelte.js';
7
+ import { mdiCheckCircle, mdiContentCopy } from '@mdi/js';
8
+ import { Highlight, LineNumbers } from 'svelte-highlight';
9
+ import { typescript, type LanguageType } from 'svelte-highlight/languages';
10
+ import vsLight from 'svelte-highlight/styles/vs';
11
+ import vsDark from 'svelte-highlight/styles/vs2015';
12
+
13
+ type Props = {
14
+ code: string;
15
+ language?: LanguageType<string>;
16
+ lineNumbers?: boolean;
17
+ lightTheme?: string;
18
+ darkTheme?: string;
19
+ copy?: boolean;
20
+ translations?: TranslationProps<'code_copy' | 'code_copied'>;
21
+ };
22
+
23
+ let {
24
+ code,
25
+ copy = true,
26
+ language = typescript,
27
+ lineNumbers,
28
+ lightTheme = vsLight,
29
+ darkTheme = vsDark,
30
+ translations,
31
+ }: Props = $props();
32
+
33
+ let copied = $state(false);
34
+ let canCopy = $derived<boolean>(copy && !!(navigator.clipboard && globalThis.ClipboardItem));
35
+
36
+ const handleCopy = async () => {
37
+ try {
38
+ await navigator.clipboard.writeText(code);
39
+ copied = true;
40
+ setTimeout(() => (copied = false), 3000);
41
+ } catch (error) {
42
+ console.error('Failed to copy code to clipboard', error);
43
+ }
44
+ };
45
+ </script>
46
+
47
+ <svelte:head>
48
+ <!-- eslint-disable-next-line svelte/no-at-html-tags -->
49
+ {@html theme.value === Theme.Dark ? darkTheme : lightTheme}
50
+ </svelte:head>
51
+
52
+ <Card class="relative">
53
+ <div class="text-sm">
54
+ {#if canCopy}
55
+ <span class="absolute top-2 right-2 cursor-pointer">
56
+ <IconButton
57
+ icon={copied ? mdiCheckCircle : mdiContentCopy}
58
+ size="small"
59
+ aria-label={copied ? t('code_copied', translations) : t('code_copy', translations)}
60
+ variant="ghost"
61
+ color={copied ? 'success' : 'secondary'}
62
+ onclick={handleCopy}
63
+ />
64
+ </span>
65
+ {/if}
66
+ {#if lineNumbers}
67
+ <Highlight {language} {code} let:highlighted>
68
+ <LineNumbers {highlighted} hideBorder wrapLines />
69
+ </Highlight>
70
+ {:else}
71
+ <Highlight {language} {code} />
72
+ {/if}
73
+ </div>
74
+ </Card>
@@ -0,0 +1,14 @@
1
+ import { type TranslationProps } from '../../types.js';
2
+ import { type LanguageType } from 'svelte-highlight/languages';
3
+ type Props = {
4
+ code: string;
5
+ language?: LanguageType<string>;
6
+ lineNumbers?: boolean;
7
+ lightTheme?: string;
8
+ darkTheme?: string;
9
+ copy?: boolean;
10
+ translations?: TranslationProps<'code_copy' | 'code_copied'>;
11
+ };
12
+ declare const CodeBlock: import("svelte").Component<Props, {}, "">;
13
+ type CodeBlock = ReturnType<typeof CodeBlock>;
14
+ export default CodeBlock;