@immich/ui 0.36.0 → 0.37.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.
@@ -5,7 +5,8 @@
5
5
  import type { IconLike, Size, TextColor } from '../../types.js';
6
6
  import { cleanClass, resolveIcon } from '../../utilities/internal.js';
7
7
  import { mdiAlertCircleOutline, mdiAlertOutline, mdiCheckAll, mdiInformationOutline, mdiPartyPopper } from '@mdi/js';
8
- import type { Snippet } from 'svelte';
8
+ import { DateTime } from 'luxon';
9
+ import { onDestroy, onMount, type Snippet } from 'svelte';
9
10
  import type { HTMLAttributes } from 'svelte/elements';
10
11
  import { tv } from 'tailwind-variants';
11
12
 
@@ -13,6 +14,8 @@
13
14
  color?: TextColor;
14
15
  size?: Size;
15
16
  icon?: IconLike | false;
17
+ since?: DateTime;
18
+ until?: DateTime;
16
19
  center?: boolean;
17
20
  children?: Snippet;
18
21
  content?: Snippet;
@@ -26,13 +29,13 @@
26
29
  false: '',
27
30
  },
28
31
  color: {
29
- primary: 'bg-primary/15 text-primary dark:bg-primary/10',
30
- secondary: 'bg-dark/15 text-dark dark:bg-dark/10',
32
+ primary: 'bg-primary/20 text-primary dark:bg-primary/25',
33
+ secondary: 'bg-dark/20 text-dark dark:bg-dark/25',
31
34
  muted: 'bg-subtle text-subtle dark:bg-subtle',
32
- info: 'bg-info/15 text-info dark:bg-info/10',
33
- warning: 'bg-warning/15 text-warning dark:bg-warning/10',
34
- danger: 'bg-danger/15 text-danger dark:bg-danger/10',
35
- success: 'bg-success/15 text-success dark:bg-success/10',
35
+ info: 'bg-info/20 text-info dark:bg-info/25',
36
+ warning: 'bg-warning/20 text-warning dark:bg-warning/25',
37
+ danger: 'bg-danger/20 text-danger dark:bg-danger/25',
38
+ success: 'bg-success/20 text-success dark:bg-success/25',
36
39
  },
37
40
  },
38
41
  });
@@ -43,6 +46,8 @@
43
46
  class: className,
44
47
  center = false,
45
48
  icon: iconOverride,
49
+ since,
50
+ until,
46
51
  content,
47
52
  children,
48
53
  ...restProps
@@ -75,19 +80,37 @@
75
80
  },
76
81
  },
77
82
  });
83
+
84
+ let now = $state(DateTime.now());
85
+ let isStarted = $derived(since ? now >= since : true);
86
+ let isFinished = $derived(until ? now <= until : true);
87
+ let isVisible = $derived(isStarted && isFinished);
88
+ let timer: ReturnType<typeof setInterval>;
89
+
90
+ onMount(() => {
91
+ timer = setInterval(() => (now = DateTime.now()), 1000);
92
+ });
93
+
94
+ onDestroy(() => {
95
+ if (timer) {
96
+ clearInterval(timer);
97
+ }
98
+ });
78
99
  </script>
79
100
 
80
- <div class={cleanClass(styles({ color, center }), className)} {...restProps}>
81
- {#if content}
82
- {@render content()}
83
- {:else}
84
- <div class="flex items-center gap-2">
85
- {#if icon}
86
- <Icon {icon} class={iconStyles({ color, size })} />
87
- {/if}
88
- <Text color="secondary" {size}>
89
- {@render children?.()}
90
- </Text>
91
- </div>
92
- {/if}
93
- </div>
101
+ {#if isVisible}
102
+ <div class={cleanClass(styles({ color, center }), className)} {...restProps}>
103
+ {#if content}
104
+ {@render content()}
105
+ {:else}
106
+ <div class="flex items-center gap-2">
107
+ {#if icon}
108
+ <Icon {icon} class={iconStyles({ color, size })} />
109
+ {/if}
110
+ <Text color="secondary" {size}>
111
+ {@render children?.()}
112
+ </Text>
113
+ </div>
114
+ {/if}
115
+ </div>
116
+ {/if}
@@ -1,10 +1,13 @@
1
1
  import type { IconLike, Size, TextColor } from '../../types.js';
2
- import type { Snippet } from 'svelte';
2
+ import { DateTime } from 'luxon';
3
+ import { type Snippet } from 'svelte';
3
4
  import type { HTMLAttributes } from 'svelte/elements';
4
5
  type Props = {
5
6
  color?: TextColor;
6
7
  size?: Size;
7
8
  icon?: IconLike | false;
9
+ since?: DateTime;
10
+ until?: DateTime;
8
11
  center?: boolean;
9
12
  children?: Snippet;
10
13
  content?: Snippet;
@@ -58,11 +58,11 @@
58
58
 
59
59
  <svelte:window
60
60
  use:shortcuts={[
61
- { shortcut: { key: 'k', meta: true }, preventDefault: true, onShortcut: handleOpen },
62
- { shortcut: { key: 'k', ctrl: true }, preventDefault: true, onShortcut: handleOpen },
61
+ { shortcut: { key: 'k', meta: true }, onShortcut: handleOpen },
62
+ { shortcut: { key: 'k', ctrl: true }, onShortcut: handleOpen },
63
63
  { shortcut: { key: '/' }, preventDefault: true, onShortcut: handleOpen },
64
- { shortcut: { key: 'ArrowUp' }, ignoreInputFields: false, onShortcut: handleUp },
65
- { shortcut: { key: 'ArrowDown' }, ignoreInputFields: false, onShortcut: handleDown },
64
+ { shortcut: { key: 'ArrowUp' }, preventDefault: false, ignoreInputFields: false, onShortcut: handleUp },
65
+ { shortcut: { key: 'ArrowDown' }, preventDefault: false, ignoreInputFields: false, onShortcut: handleDown },
66
66
  { shortcut: { key: 'k', ctrl: true }, ignoreInputFields: false, onShortcut: handleUp },
67
67
  { shortcut: { key: 'k', meta: true }, ignoreInputFields: false, onShortcut: handleUp },
68
68
  { shortcut: { key: 'j', ctrl: true }, ignoreInputFields: false, onShortcut: handleDown },
@@ -0,0 +1,17 @@
1
+ <script lang="ts">
2
+ import Input from '../Input/Input.svelte';
3
+ import type { NumberInputProps } from '../../types.js';
4
+
5
+ let { value = $bindable<number>(), color = 'secondary', size, ...props }: NumberInputProps = $props();
6
+
7
+ const getValue = () => String(value ?? 0);
8
+ const setValue = (newValue: string) => {
9
+ const parsed = parseFloat(newValue);
10
+ if (isNaN(parsed)) {
11
+ return;
12
+ }
13
+ value = parsed;
14
+ };
15
+ </script>
16
+
17
+ <Input bind:value={getValue, setValue} {size} type="number" {color} {...props} />
@@ -0,0 +1,4 @@
1
+ import type { NumberInputProps } from '../../types.js';
2
+ declare const NumberInput: import("svelte").Component<NumberInputProps, {}, "value">;
3
+ type NumberInput = ReturnType<typeof NumberInput>;
4
+ export default NumberInput;
package/dist/index.d.ts CHANGED
@@ -49,6 +49,7 @@ export { default as ModalHeader } from './components/Modal/ModalHeader.svelte';
49
49
  export { default as MultiSelect } from './components/MultiSelect/MultiSelect.svelte';
50
50
  export { default as NavbarGroup } from './components/Navbar/NavbarGroup.svelte';
51
51
  export { default as NavbarItem } from './components/Navbar/NavbarItem.svelte';
52
+ export { default as NumberInput } from './components/NumberInput/NumberInput.svelte';
52
53
  export { default as PasswordInput } from './components/PasswordInput/PasswordInput.svelte';
53
54
  export { default as Scrollable } from './components/Scrollable/Scrollable.svelte';
54
55
  export { default as Select } from './components/Select/Select.svelte';
package/dist/index.js CHANGED
@@ -51,6 +51,7 @@ export { default as ModalHeader } from './components/Modal/ModalHeader.svelte';
51
51
  export { default as MultiSelect } from './components/MultiSelect/MultiSelect.svelte';
52
52
  export { default as NavbarGroup } from './components/Navbar/NavbarGroup.svelte';
53
53
  export { default as NavbarItem } from './components/Navbar/NavbarItem.svelte';
54
+ export { default as NumberInput } from './components/NumberInput/NumberInput.svelte';
54
55
  export { default as PasswordInput } from './components/PasswordInput/PasswordInput.svelte';
55
56
  export { default as Scrollable } from './components/Scrollable/Scrollable.svelte';
56
57
  export { default as Select } from './components/Select/Select.svelte';
package/dist/types.d.ts CHANGED
@@ -102,23 +102,23 @@ export type FieldContext = {
102
102
  required?: boolean;
103
103
  readOnly?: boolean;
104
104
  } & LabelProps;
105
- type BaseInputProps = {
105
+ type BaseInputProps<T> = {
106
106
  ref?: HTMLInputElement | null;
107
107
  class?: string;
108
- value?: string;
109
108
  size?: Size;
109
+ value?: T;
110
110
  shape?: Shape;
111
111
  inputSize?: HTMLInputAttributes['size'];
112
- } & Omit<HTMLInputAttributes, 'size' | 'type'>;
113
- export type InputProps = BaseInputProps & {
114
- containerRef?: HTMLElement | null;
115
- type?: HTMLInputAttributes['type'];
116
112
  leadingIcon?: IconLike | Snippet;
117
113
  trailingIcon?: IconLike | Snippet;
118
114
  trailingText?: string;
115
+ containerRef?: HTMLElement | null;
116
+ } & Omit<HTMLInputAttributes, 'size' | 'type' | 'value'>;
117
+ export type InputProps = BaseInputProps<string> & {
118
+ type?: HTMLInputAttributes['type'];
119
119
  };
120
- export type PasswordInputProps = BaseInputProps & {
121
- ref?: HTMLInputElement | null;
120
+ export type NumberInputProps = BaseInputProps<number>;
121
+ export type PasswordInputProps = BaseInputProps<string> & {
122
122
  translations?: TranslationProps<'show_password' | 'hide_password'>;
123
123
  isVisible?: boolean;
124
124
  };
@@ -1,4 +1,4 @@
1
- import { DateTime } from 'luxon';
1
+ import type { DateTime } from 'luxon';
2
2
  export declare const resolveUrl: (url: string, currentHostname?: string) => string;
3
3
  export declare const isExternalLink: (href: string) => boolean;
4
4
  export type Metadata = {
@@ -1,5 +1,4 @@
1
1
  import { env } from '$env/dynamic/public';
2
- import { DateTime } from 'luxon';
3
2
  const getImmichApp = (host) => {
4
3
  if (!host || !host.endsWith('immich.app')) {
5
4
  return false;
package/package.json CHANGED
@@ -1,7 +1,11 @@
1
1
  {
2
2
  "name": "@immich/ui",
3
- "version": "0.36.0",
3
+ "version": "0.37.0",
4
4
  "license": "GNU Affero General Public License version 3",
5
+ "repository": {
6
+ "type": "git",
7
+ "url": "git+https://github.com/immich-app/ui.git"
8
+ },
5
9
  "scripts": {
6
10
  "create": "node scripts/create.js",
7
11
  "start": "npm run start:dev",