@immich/ui 0.10.0 → 0.11.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.
@@ -24,11 +24,11 @@
24
24
  base: 'flex h-full w-full select-none items-center justify-center font-medium',
25
25
  variants: {
26
26
  size: {
27
- tiny: 'w-5 h-5 text-xs',
28
- small: 'w-7 h-7 text-sm',
29
- medium: 'w-10 h-10 text-md',
30
- large: 'w-12 h-12 text-lg',
31
- giant: 'w-16 h-16 text-xl',
27
+ tiny: 'h-5 w-5 text-xs',
28
+ small: 'h-7 w-7 text-sm',
29
+ medium: 'text-md h-10 w-10',
30
+ large: 'h-12 w-12 text-lg',
31
+ giant: 'h-16 w-16 text-xl',
32
32
  },
33
33
  color: {
34
34
  primary: 'bg-primary text-light',
@@ -46,7 +46,7 @@
46
46
  });
47
47
 
48
48
  const wrapper = tv({
49
- base: 'overflow-hidden shadow-md rounded rounded-full',
49
+ base: 'overflow-hidden rounded-full shadow-md',
50
50
  });
51
51
 
52
52
  const getInitials = (name: string) => {
@@ -44,11 +44,11 @@
44
44
  });
45
45
 
46
46
  const cardStyles = tv({
47
- base: 'flex flex-col w-full h-full',
47
+ base: 'flex h-full w-full flex-col',
48
48
  variants: {
49
49
  color: {
50
50
  primary: 'bg-primary/25 dark:bg-primary/25',
51
- secondary: 'bg-dark/5 dark:bg-dark/25 text-dark',
51
+ secondary: 'bg-dark/5 text-dark dark:bg-dark/25',
52
52
  success: 'bg-success/15 dark:bg-success/30',
53
53
  danger: 'bg-danger/15 dark:bg-danger/50',
54
54
  warning: 'bg-warning/25 dark:bg-warning/50',
@@ -1,9 +1,23 @@
1
1
  <script lang="ts">
2
2
  import IconButton from '../IconButton/IconButton.svelte';
3
+ import { t } from '../../services/translation.svelte.js';
3
4
  import type { CloseButtonProps } from '../../types.js';
4
5
  import { mdiClose } from '@mdi/js';
5
6
 
6
- const { size = 'medium', variant = 'ghost', ...restProps }: CloseButtonProps = $props();
7
+ const {
8
+ size = 'medium',
9
+ variant = 'ghost',
10
+ translations,
11
+ ...restProps
12
+ }: CloseButtonProps = $props();
7
13
  </script>
8
14
 
9
- <IconButton {...restProps} icon={mdiClose} shape="round" {variant} {size} color="secondary" />
15
+ <IconButton
16
+ {...restProps}
17
+ icon={mdiClose}
18
+ shape="round"
19
+ {variant}
20
+ {size}
21
+ color="secondary"
22
+ title={t('close', translations)}
23
+ />
@@ -32,7 +32,7 @@
32
32
 
33
33
  filledColor: {
34
34
  false: '',
35
- muted: 'bg-gray-600 dark:bg-gray-800 text-light',
35
+ muted: 'bg-gray-600 text-light dark:bg-gray-800',
36
36
  primary: 'bg-primary text-light',
37
37
  secondary: 'bg-dark text-light',
38
38
  success: 'bg-success text-light',
@@ -32,7 +32,7 @@
32
32
  } = $derived(getFieldContext());
33
33
 
34
34
  const containerStyles = tv({
35
- base: 'border-2 ring-offset-background focus-visible:ring-ring peer box-content focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 data-[disabled=true]:cursor-not-allowed data-[state=checked]:bg-primary data-[disabled=true]:opacity-50 overflow-hidden',
35
+ base: 'ring-offset-background focus-visible:ring-ring peer box-content overflow-hidden border-2 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 data-[disabled=true]:cursor-not-allowed data-[state=checked]:bg-primary data-[disabled=true]:opacity-50',
36
36
  variants: {
37
37
  shape: {
38
38
  rectangle: 'rounded-none',
@@ -36,7 +36,7 @@
36
36
  });
37
37
 
38
38
  const inputStyles = tv({
39
- base: 'w-full outline-none disabled:cursor-not-allowed bg-gray-200 dark:bg-gray-600 disabled:bg-gray-300 disabled:text-gray-200 dark:disabled:bg-gray-800 aria-readonly:text-dark/50 dark:aria-readonly:text-dark/75',
39
+ base: 'w-full bg-gray-200 outline-none disabled:cursor-not-allowed disabled:bg-gray-300 disabled:text-gray-200 aria-readonly:text-dark/50 dark:bg-gray-600 dark:disabled:bg-gray-800 dark:aria-readonly:text-dark/75',
40
40
  variants: {
41
41
  shape: {
42
42
  rectangle: 'rounded-none',
@@ -1,13 +1,13 @@
1
1
  <script lang="ts">
2
2
  import Input from './Input.svelte';
3
3
  import IconButton from '../IconButton/IconButton.svelte';
4
+ import { t } from '../../services/translation.svelte.js';
4
5
  import type { PasswordInputProps } from '../../types.js';
5
6
  import { mdiEyeOffOutline, mdiEyeOutline } from '@mdi/js';
6
7
 
7
8
  let {
8
9
  value = $bindable<string>(),
9
- showLabel = 'Show password',
10
- hideLabel = 'Hide password',
10
+ translations,
11
11
  isVisible = $bindable<boolean>(false),
12
12
  color = 'secondary',
13
13
  ...props
@@ -24,7 +24,7 @@
24
24
  class="m-1"
25
25
  icon={isVisible ? mdiEyeOffOutline : mdiEyeOutline}
26
26
  onclick={() => (isVisible = !isVisible)}
27
- title={isVisible ? hideLabel : showLabel}
27
+ title={isVisible ? t('hidePassword', translations) : t('showPassword', translations)}
28
28
  ></IconButton>
29
29
  {/if}
30
30
  {/snippet}
@@ -0,0 +1,33 @@
1
+ <script lang="ts">
2
+ import type { Color, Size } 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
+
8
+ type Props = {
9
+ size?: Size;
10
+ color?: Color;
11
+ class?: string;
12
+ children?: Snippet;
13
+ } & HTMLAttributes<HTMLElement>;
14
+
15
+ const { class: className, size = 'small', children, ...restProps }: Props = $props();
16
+
17
+ const styles = tv({
18
+ base: 'flex flex-col rounded-md border border-b-2 bg-subtle px-1 font-mono text-dark shadow',
19
+ variants: {
20
+ size: {
21
+ tiny: 'text-xs',
22
+ small: 'text-sm',
23
+ medium: 'text-md',
24
+ large: 'text-lg',
25
+ giant: 'text-xl',
26
+ },
27
+ },
28
+ });
29
+ </script>
30
+
31
+ <kbd class={cleanClass(styles({ size }), className)} {...restProps}>
32
+ {@render children?.()}
33
+ </kbd>
@@ -0,0 +1,10 @@
1
+ import type { Color, Size } from '../../types.js';
2
+ import type { Snippet } from 'svelte';
3
+ import type { HTMLAttributes } from 'svelte/elements';
4
+ declare const Kbd: import("svelte").Component<{
5
+ size?: Size;
6
+ color?: Color;
7
+ class?: string;
8
+ children?: Snippet;
9
+ } & HTMLAttributes<HTMLElement>, {}, "">;
10
+ export default Kbd;
@@ -32,7 +32,7 @@
32
32
  }: Props = $props();
33
33
 
34
34
  const modalStyles = tv({
35
- base: 'flex rounded-none border border-subtle bg-subtle sm:rounded-2xl',
35
+ base: 'flex rounded-none border border-subtle bg-subtle sm:rounded-2xl',
36
36
  variants: {
37
37
  size: {
38
38
  tiny: 'h-full sm:h-min md:max-w-64',
@@ -3,7 +3,6 @@ import { type Snippet } from 'svelte';
3
3
  declare const Modal: import("svelte").Component<{
4
4
  open?: boolean;
5
5
  onOpenChange?: import("bits-ui/dist/internal/types").OnChangeFn<boolean>;
6
- controlledOpen?: boolean;
7
6
  } & {
8
7
  children?: Snippet | undefined;
9
8
  } & {
@@ -12,7 +12,7 @@
12
12
  let { href, title, active = false, ...iconProps }: Props = $props();
13
13
 
14
14
  const styles = tv({
15
- base: 'hover:bg-subtle hover:text-primary flex w-full place-items-center gap-4 rounded-r-full py-3 transition-[padding] delay-100 duration-100 pl-5 group-hover:sm:px-5 md:px-5',
15
+ base: 'flex w-full place-items-center gap-4 rounded-r-full py-3 pl-5 transition-[padding] delay-100 duration-100 hover:bg-subtle hover:text-primary group-hover:sm:px-5 md:px-5',
16
16
  variants: {
17
17
  active: {
18
18
  true: 'bg-primary/10 text-primary',
@@ -24,7 +24,7 @@
24
24
  };
25
25
 
26
26
  const containerStyles = tv({
27
- base: 'bg-secondary flex place-items-center gap-2 overflow-hidden rounded-lg transition-all',
27
+ base: 'bg-secondary flex place-items-center gap-2 overflow-hidden rounded-lg transition-all',
28
28
  variants: {
29
29
  size: {
30
30
  tiny: 'px-2 py-1',
@@ -34,8 +34,8 @@
34
34
  giant: 'px-3 py-2',
35
35
  },
36
36
  effect: {
37
- hover: 'border border-dark/25 supporter-effect-hover',
38
- always: 'shadow supporter-effect',
37
+ hover: 'supporter-effect-hover border border-dark/25',
38
+ always: 'supporter-effect shadow',
39
39
  },
40
40
  },
41
41
  });
@@ -43,7 +43,7 @@
43
43
  });
44
44
 
45
45
  const bar = tv({
46
- base: 'w-12 h-3 my-2 rounded-full border border-transparent',
46
+ base: 'my-2 h-3 w-12 rounded-full border border-transparent',
47
47
  variants: {
48
48
  fillColor: {
49
49
  default: 'bg-gray-400',
@@ -58,7 +58,7 @@
58
58
  });
59
59
 
60
60
  const dot = tv({
61
- base: 'absolute transition-colors h-6 w-6 rounded-full transition-transform duration-[400ms]',
61
+ base: 'absolute h-6 w-6 rounded-full transition-transform duration-[400ms]',
62
62
  variants: {
63
63
  checked: {
64
64
  true: 'translate-x-6',
@@ -0,0 +1,33 @@
1
+ <script lang="ts">
2
+ import IconButton from '../IconButton/IconButton.svelte';
3
+ import { theme } from '../../services/theme.svelte.js';
4
+ import { Theme, type Size, type Variants } from '../../types.js';
5
+ import { cleanClass } from '../../utils.js';
6
+ import { mdiWeatherNight, mdiWeatherSunny } from '@mdi/js';
7
+
8
+ type Props = {
9
+ size?: Size;
10
+ class?: string;
11
+ variant?: Variants;
12
+ onChange?: (theme: Theme) => void;
13
+ };
14
+
15
+ const { variant = 'ghost', size, class: className, onChange }: Props = $props();
16
+
17
+ const handleToggleTheme = () => {
18
+ theme.value = theme.value === Theme.Dark ? Theme.Light : Theme.Dark;
19
+ onChange?.(theme.value);
20
+ };
21
+
22
+ const themeIcon = $derived(theme.value === Theme.Light ? mdiWeatherSunny : mdiWeatherNight);
23
+ </script>
24
+
25
+ <IconButton
26
+ shape="round"
27
+ color="primary"
28
+ {size}
29
+ {variant}
30
+ icon={themeIcon}
31
+ onclick={handleToggleTheme}
32
+ class={cleanClass(className)}
33
+ />
@@ -0,0 +1,8 @@
1
+ import { Theme, type Size, type Variants } from '../../types.js';
2
+ declare const ThemeSwitcher: import("svelte").Component<{
3
+ size?: Size;
4
+ class?: string;
5
+ variant?: Variants;
6
+ onChange?: (theme: Theme) => void;
7
+ }, {}, "">;
8
+ export default ThemeSwitcher;
package/dist/index.d.ts CHANGED
@@ -19,9 +19,6 @@ export { default as CardDescription } from './components/Card/CardDescription.sv
19
19
  export { default as CardFooter } from './components/Card/CardFooter.svelte';
20
20
  export { default as CardHeader } from './components/Card/CardHeader.svelte';
21
21
  export { default as CardTitle } from './components/Card/CardTitle.svelte';
22
- export { default as Modal } from './components/Modal/Modal.svelte';
23
- export { default as ModalBody } from './components/Modal/ModalBody.svelte';
24
- export { default as ModalFooter } from './components/Modal/ModalFooter.svelte';
25
22
  export { default as CloseButton } from './components/CloseButton/CloseButton.svelte';
26
23
  export { default as Code } from './components/Code/Code.svelte';
27
24
  export { default as Checkbox } from './components/Form/Checkbox.svelte';
@@ -34,9 +31,13 @@ export { default as FormatBytes } from './components/FormatBytes/FormatBytes.sve
34
31
  export { default as Heading } from './components/Heading/Heading.svelte';
35
32
  export { default as Icon } from './components/Icon/Icon.svelte';
36
33
  export { default as IconButton } from './components/IconButton/IconButton.svelte';
34
+ export { default as Kbd } from './components/Kbd/Kbd.svelte';
37
35
  export { default as Link } from './components/Link/Link.svelte';
38
36
  export { default as LoadingSpinner } from './components/LoadingSpinner/LoadingSpinner.svelte';
39
37
  export { default as Logo } from './components/Logo/Logo.svelte';
38
+ export { default as Modal } from './components/Modal/Modal.svelte';
39
+ export { default as ModalBody } from './components/Modal/ModalBody.svelte';
40
+ export { default as ModalFooter } from './components/Modal/ModalFooter.svelte';
40
41
  export { default as MultiSelect } from './components/MultiSelect/MultiSelect.svelte';
41
42
  export { default as NavbarGroup } from './components/Navbar/NavbarGroup.svelte';
42
43
  export { default as NavbarItem } from './components/Navbar/NavbarItem.svelte';
@@ -48,6 +49,8 @@ export { default as VStack } from './components/Stack/VStack.svelte';
48
49
  export { default as SupporterBadge } from './components/SupporterBadge/SupporterBadge.svelte';
49
50
  export { default as Switch } from './components/Switch/Switch.svelte';
50
51
  export { default as Text } from './components/Text/Text.svelte';
52
+ export { default as ThemeSwitcher } from './components/ThemeSwitcher/ThemeSwitcher.svelte';
51
53
  export * from './services/theme.svelte.js';
52
54
  export * from './types.js';
53
55
  export * from './utilities/byte-units.js';
56
+ export * from './services/translation.svelte.js';
package/dist/index.js CHANGED
@@ -21,9 +21,6 @@ export { default as CardDescription } from './components/Card/CardDescription.sv
21
21
  export { default as CardFooter } from './components/Card/CardFooter.svelte';
22
22
  export { default as CardHeader } from './components/Card/CardHeader.svelte';
23
23
  export { default as CardTitle } from './components/Card/CardTitle.svelte';
24
- export { default as Modal } from './components/Modal/Modal.svelte';
25
- export { default as ModalBody } from './components/Modal/ModalBody.svelte';
26
- export { default as ModalFooter } from './components/Modal/ModalFooter.svelte';
27
24
  export { default as CloseButton } from './components/CloseButton/CloseButton.svelte';
28
25
  export { default as Code } from './components/Code/Code.svelte';
29
26
  export { default as Checkbox } from './components/Form/Checkbox.svelte';
@@ -36,9 +33,13 @@ export { default as FormatBytes } from './components/FormatBytes/FormatBytes.sve
36
33
  export { default as Heading } from './components/Heading/Heading.svelte';
37
34
  export { default as Icon } from './components/Icon/Icon.svelte';
38
35
  export { default as IconButton } from './components/IconButton/IconButton.svelte';
36
+ export { default as Kbd } from './components/Kbd/Kbd.svelte';
39
37
  export { default as Link } from './components/Link/Link.svelte';
40
38
  export { default as LoadingSpinner } from './components/LoadingSpinner/LoadingSpinner.svelte';
41
39
  export { default as Logo } from './components/Logo/Logo.svelte';
40
+ export { default as Modal } from './components/Modal/Modal.svelte';
41
+ export { default as ModalBody } from './components/Modal/ModalBody.svelte';
42
+ export { default as ModalFooter } from './components/Modal/ModalFooter.svelte';
42
43
  export { default as MultiSelect } from './components/MultiSelect/MultiSelect.svelte';
43
44
  export { default as NavbarGroup } from './components/Navbar/NavbarGroup.svelte';
44
45
  export { default as NavbarItem } from './components/Navbar/NavbarItem.svelte';
@@ -50,7 +51,9 @@ export { default as VStack } from './components/Stack/VStack.svelte';
50
51
  export { default as SupporterBadge } from './components/SupporterBadge/SupporterBadge.svelte';
51
52
  export { default as Switch } from './components/Switch/Switch.svelte';
52
53
  export { default as Text } from './components/Text/Text.svelte';
54
+ export { default as ThemeSwitcher } from './components/ThemeSwitcher/ThemeSwitcher.svelte';
53
55
  // helpers
54
56
  export * from './services/theme.svelte.js';
55
57
  export * from './types.js';
56
58
  export * from './utilities/byte-units.js';
59
+ export * from './services/translation.svelte.js';
@@ -57,7 +57,7 @@
57
57
  iconSize: {
58
58
  tiny: 'h-4 w-4 text-xs',
59
59
  small: 'h-6 w-6 text-sm',
60
- medium: 'h-8 w-8 text-md',
60
+ medium: 'text-md h-8 w-8',
61
61
  large: 'h-10 w-10 text-lg',
62
62
  giant: 'h-12 w-12 text-lg',
63
63
  },
@@ -77,12 +77,12 @@
77
77
  info: 'bg-info text-light hover:bg-info/80',
78
78
  },
79
79
  outlineColor: {
80
- primary: 'bg-primary/10 text-primary border border-primary hover:bg-primary/20',
81
- secondary: 'bg-dark/10 text-dark border border-dark hover:bg-dark/20',
82
- success: 'bg-success/10 text-success border border-success hover:bg-success/20',
83
- danger: 'bg-danger/10 text-danger border border-danger hover:bg-danger/20',
84
- warning: 'bg-warning/10 text-warning border border-warning hover:bg-warning/20',
85
- info: 'bg-info/10 text-info border border-info hover:bg-info/20',
80
+ primary: 'border border-primary bg-primary/10 text-primary hover:bg-primary/20',
81
+ secondary: 'border border-dark bg-dark/10 text-dark hover:bg-dark/20',
82
+ success: 'border border-success bg-success/10 text-success hover:bg-success/20',
83
+ danger: 'border border-danger bg-danger/10 text-danger hover:bg-danger/20',
84
+ warning: 'border border-warning bg-warning/10 text-warning hover:bg-warning/20',
85
+ info: 'border border-info bg-info/10 text-info hover:bg-info/20',
86
86
  },
87
87
  ghostColor: {
88
88
  primary: 'text-primary hover:bg-primary/10',
@@ -0,0 +1,11 @@
1
+ import type { TranslationProps } from '../types.js';
2
+ declare const defaultTranslations: {
3
+ close: string;
4
+ showPassword: string;
5
+ hidePassword: string;
6
+ };
7
+ export type Translations = typeof defaultTranslations;
8
+ export declare const translate: <T extends keyof Translations>(key: T, overrides?: TranslationProps<T>) => string;
9
+ export declare const t: <T extends keyof Translations>(key: T, overrides?: TranslationProps<T>) => string;
10
+ export declare const setTranslations: (newTranslations: Partial<Translations>) => void;
11
+ export {};
@@ -0,0 +1,11 @@
1
+ const defaultTranslations = {
2
+ close: 'Close',
3
+ showPassword: 'Show password',
4
+ hidePassword: 'Hide password',
5
+ };
6
+ let translations = $state(defaultTranslations);
7
+ export const translate = (key, overrides) => overrides?.[key] ?? translations[key];
8
+ export const t = translate;
9
+ export const setTranslations = (newTranslations) => {
10
+ translations = { ...defaultTranslations, ...newTranslations };
11
+ };
package/dist/types.d.ts CHANGED
@@ -1,3 +1,4 @@
1
+ import type { Translations } from './services/translation.svelte.js';
1
2
  import type { Snippet } from 'svelte';
2
3
  import type { HTMLAnchorAttributes, HTMLButtonAttributes, HTMLInputAttributes } from 'svelte/elements';
3
4
  export type Color = 'primary' | 'secondary' | 'success' | 'danger' | 'warning' | 'info';
@@ -13,6 +14,9 @@ export declare enum Theme {
13
14
  Light = "light",
14
15
  Dark = "dark"
15
16
  }
17
+ export type TranslationProps<T extends keyof Translations> = {
18
+ [K in T]?: string;
19
+ };
16
20
  export type IconProps = {
17
21
  icon: string;
18
22
  title?: string;
@@ -47,6 +51,7 @@ export type CloseButtonProps = {
47
51
  size?: Size;
48
52
  variant?: Variants;
49
53
  class?: string;
54
+ translations?: TranslationProps<'close'>;
50
55
  } & ButtonOrAnchor;
51
56
  export type IconButtonProps = ButtonBase & {
52
57
  icon: string;
@@ -91,8 +96,7 @@ export type InputProps = BaseInputProps & {
91
96
  trailingIcon?: Snippet;
92
97
  };
93
98
  export type PasswordInputProps = BaseInputProps & {
94
- showLabel?: string;
95
- hideLabel?: string;
99
+ translations?: TranslationProps<'showPassword' | 'hidePassword'>;
96
100
  isVisible?: boolean;
97
101
  };
98
102
  export type SelectItem = {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@immich/ui",
3
- "version": "0.10.0",
3
+ "version": "0.11.0",
4
4
  "license": "GNU Affero General Public License version 3",
5
5
  "scripts": {
6
6
  "create": "node scripts/create.js",
@@ -53,7 +53,7 @@
53
53
  "prettier": "^3.3.2",
54
54
  "prettier-plugin-svelte": "^3.2.6",
55
55
  "prettier-plugin-tailwindcss": "^0.6.5",
56
- "publint": "^0.2.0",
56
+ "publint": "^0.3.0",
57
57
  "svelte": "^5.0.0",
58
58
  "svelte-check": "^4.0.0",
59
59
  "svelte-highlight": "^7.8.0",