@immich/ui 0.65.3 → 0.67.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.
@@ -0,0 +1,124 @@
1
+ <script lang="ts">
2
+ import { getFieldContext } from '../../common/context.svelte.js';
3
+ import Label from '../Label/Label.svelte';
4
+ import { styleVariants } from '../../styles.js';
5
+ import type { PinInputProps } from '../../types.js';
6
+ import { cleanClass } from '../../utilities/internal.js';
7
+ import { PinInput, REGEXP_ONLY_DIGITS } from 'bits-ui';
8
+ import { tv } from 'tailwind-variants';
9
+
10
+ let {
11
+ shape = 'semi-round',
12
+ size: initialSize,
13
+ value = $bindable<string>(''),
14
+ length = 6,
15
+ password,
16
+ onComplete,
17
+ class: className,
18
+ ...props
19
+ }: PinInputProps = $props();
20
+
21
+ const context = getFieldContext();
22
+
23
+ const { label, disabled, ...labelProps } = $derived(context());
24
+ const size = $derived(initialSize ?? labelProps.size ?? 'large');
25
+
26
+ const inputStyles = tv({
27
+ base: 'group-has-disabled:text-dark data-active:border-primary dark:data-active:border-primary flex items-center justify-center border-2 bg-gray-100 font-mono transition-all duration-75 group-has-disabled:bg-gray-300 data-active:border-3 dark:bg-gray-800 dark:group-not-has-disabled:border-gray-700 dark:group-has-disabled:bg-gray-900 dark:group-has-disabled:text-gray-200',
28
+ variants: {
29
+ shape: styleVariants.shape,
30
+ size: {
31
+ tiny: 'h-9 w-7',
32
+ small: 'h-10 w-8',
33
+ medium: 'h-11 w-9',
34
+ large: 'h-12 w-10',
35
+ giant: 'h-14 w-12',
36
+ },
37
+ textSize: styleVariants.textSize,
38
+ roundedSize: {
39
+ tiny: 'rounded-lg',
40
+ small: 'rounded-lg',
41
+ medium: 'rounded-xl',
42
+ large: 'rounded-xl',
43
+ giant: 'rounded-2xl',
44
+ },
45
+ },
46
+ });
47
+
48
+ const caretStyles = tv({
49
+ base: 'caret bg-dark h-1/2',
50
+ variants: {
51
+ size: {
52
+ tiny: 'w-px',
53
+ small: 'w-px',
54
+ medium: 'w-[1.5px]',
55
+ large: 'w-[1.5px]',
56
+ giant: 'w-0.5',
57
+ },
58
+ },
59
+ });
60
+
61
+ const id = $props.id();
62
+ const inputId = `input-${id}`;
63
+ const labelId = `label-${id}`;
64
+ </script>
65
+
66
+ <div class={cleanClass('flex flex-col gap-1', className)}>
67
+ {#if label}
68
+ <Label id={labelId} for={inputId} {label} {...labelProps} {size} />
69
+ {/if}
70
+
71
+ <PinInput.Root
72
+ {inputId}
73
+ aria-labelledby={label && labelId}
74
+ {disabled}
75
+ aria-disabled={disabled}
76
+ class="group flex w-fit items-center gap-2"
77
+ maxlength={length}
78
+ pattern={REGEXP_ONLY_DIGITS}
79
+ type={password ? 'password' : 'text'}
80
+ {onComplete}
81
+ bind:value
82
+ {...props}
83
+ >
84
+ {#snippet children({ cells })}
85
+ {#each cells as cell, i (i)}
86
+ <PinInput.Cell
87
+ {cell}
88
+ class={inputStyles({
89
+ shape,
90
+ size,
91
+ textSize: size,
92
+ roundedSize: shape === 'semi-round' ? size : undefined,
93
+ })}
94
+ >
95
+ {#if cell.char !== null}
96
+ <div>
97
+ {password ? '●' : cell.char}
98
+ </div>
99
+ {/if}
100
+ {#if cell.hasFakeCaret}
101
+ <div class="absolute flex h-full items-center justify-center">
102
+ <div class={caretStyles({ size })}></div>
103
+ </div>
104
+ {/if}
105
+ </PinInput.Cell>
106
+ {/each}
107
+ {/snippet}
108
+ </PinInput.Root>
109
+ </div>
110
+
111
+ <style>
112
+ .caret {
113
+ animation: blink 1.5s step-end infinite;
114
+ }
115
+ @keyframes blink {
116
+ 0%,
117
+ 100% {
118
+ opacity: 1;
119
+ }
120
+ 50% {
121
+ opacity: 0;
122
+ }
123
+ }
124
+ </style>
@@ -0,0 +1,5 @@
1
+ import type { PinInputProps } from '../../types.js';
2
+ import { PinInput } from 'bits-ui';
3
+ declare const PinInput: import("svelte").Component<PinInputProps, {}, "value">;
4
+ type PinInput = ReturnType<typeof PinInput>;
5
+ export default PinInput;
@@ -1,15 +1,25 @@
1
1
  <script lang="ts">
2
+ import Button from '../Button/Button.svelte';
2
3
  import ToastContainer from './ToastContainer.svelte';
3
4
  import ToastContent from './ToastContent.svelte';
4
5
  import type { ToastProps } from '../../types.js';
5
6
 
6
- let { children, title, description, icon, onClose, ...props }: ToastProps = $props();
7
+ let { children, title, description, icon, onClose, button, ...props }: ToastProps = $props();
7
8
  </script>
8
9
 
9
10
  <ToastContainer {...props}>
10
11
  {#if children}
11
12
  {@render children()}
12
13
  {:else if title}
13
- <ToastContent {title} {description} {icon} {onClose} {...props} />
14
+ <ToastContent {title} {description} {icon} {onClose} {...props}>
15
+ {#if button}
16
+ {@const { label, ...rest } = button}
17
+ <div class="flex justify-end px-3 pt-2">
18
+ <Button color="secondary" size="small" {...rest}>
19
+ {label}
20
+ </Button>
21
+ </div>
22
+ {/if}
23
+ </ToastContent>
14
24
  {/if}
15
25
  </ToastContainer>
@@ -15,7 +15,7 @@
15
15
  }: ToastContainerProps = $props();
16
16
 
17
17
  const containerStyles = tv({
18
- base: 'bg-light text-dark overflow-hidden border py-1.5 shadow-xs transition-all',
18
+ base: 'bg-light text-dark overflow-hidden border py-3 shadow-xs transition-all',
19
19
  variants: {
20
20
  color: {
21
21
  primary: 'border-primary-100 bg-primary-50 dark:bg-primary-100 dark:border-primary-200',
@@ -58,7 +58,7 @@
58
58
  {/if}
59
59
  </div>
60
60
  <div class="ms-1 flex grow justify-between">
61
- <div class="flex flex-col p-2">
61
+ <div class="flex flex-col px-2">
62
62
  {#if title}
63
63
  <Text fontWeight="semi-bold" class={titleStyles({ color })}>{@render resolve(title)}</Text>
64
64
  {/if}
package/dist/index.d.ts CHANGED
@@ -70,6 +70,7 @@ export { default as NavbarGroup } from './components/Navbar/NavbarGroup.svelte';
70
70
  export { default as NavbarItem } from './components/Navbar/NavbarItem.svelte';
71
71
  export { default as NumberInput } from './components/NumberInput/NumberInput.svelte';
72
72
  export { default as PasswordInput } from './components/PasswordInput/PasswordInput.svelte';
73
+ export { default as PinInput } from './components/PinInput/PinInput.svelte';
73
74
  export { default as ProgressBar } from './components/ProgressBar/ProgressBar.svelte';
74
75
  export { default as Scrollable } from './components/Scrollable/Scrollable.svelte';
75
76
  export { default as Select } from './components/Select/Select.svelte';
@@ -103,9 +104,9 @@ export * from './services/theme.svelte.js';
103
104
  export * from './services/toast-manager.svelte.js';
104
105
  export * from './services/translation.svelte.js';
105
106
  export * from './state/locale-state.svelte.js';
107
+ export { isModalOpen } from './state/modal-state.svelte.js';
106
108
  export * from './types.js';
107
109
  export * from './utilities/byte-units.js';
108
110
  export * from './utilities/common.js';
109
- export { isModalOpen } from './state/modal-state.svelte.js';
110
111
  export * from './site/constants.js';
111
112
  export { default as SiteFooter } from './site/SiteFooter.svelte';
package/dist/index.js CHANGED
@@ -72,6 +72,7 @@ export { default as NavbarGroup } from './components/Navbar/NavbarGroup.svelte';
72
72
  export { default as NavbarItem } from './components/Navbar/NavbarItem.svelte';
73
73
  export { default as NumberInput } from './components/NumberInput/NumberInput.svelte';
74
74
  export { default as PasswordInput } from './components/PasswordInput/PasswordInput.svelte';
75
+ export { default as PinInput } from './components/PinInput/PinInput.svelte';
75
76
  export { default as ProgressBar } from './components/ProgressBar/ProgressBar.svelte';
76
77
  export { default as Scrollable } from './components/Scrollable/Scrollable.svelte';
77
78
  export { default as Select } from './components/Select/Select.svelte';
@@ -106,10 +107,10 @@ export * from './services/theme.svelte.js';
106
107
  export * from './services/toast-manager.svelte.js';
107
108
  export * from './services/translation.svelte.js';
108
109
  export * from './state/locale-state.svelte.js';
110
+ export { isModalOpen } from './state/modal-state.svelte.js';
109
111
  export * from './types.js';
110
112
  export * from './utilities/byte-units.js';
111
113
  export * from './utilities/common.js';
112
- export { isModalOpen } from './state/modal-state.svelte.js';
113
114
  // site
114
115
  export * from './site/constants.js';
115
116
  export { default as SiteFooter } from './site/SiteFooter.svelte';
package/dist/types.d.ts CHANGED
@@ -156,6 +156,17 @@ export type PasswordInputProps = BaseInputProps<string> & {
156
156
  translations?: TranslationProps<'show_password' | 'hide_password'>;
157
157
  isVisible?: boolean;
158
158
  };
159
+ export type PinInputProps = {
160
+ ref?: HTMLInputElement | null;
161
+ class?: string;
162
+ size?: Size;
163
+ value?: string;
164
+ shape?: Shape;
165
+ disabled?: boolean;
166
+ length?: number;
167
+ password?: boolean;
168
+ onComplete?: (value: string) => void;
169
+ };
159
170
  export type TextareaProps = {
160
171
  ref?: HTMLTextAreaElement | null;
161
172
  containerRef?: HTMLElement | null;
@@ -188,7 +199,7 @@ export type MultiSelectProps<T extends string> = SelectCommonProps<T> & {
188
199
  onChange?: (values: T[]) => void;
189
200
  onSelect?: (options: SelectOption<T>[]) => void;
190
201
  };
191
- export type ToastId = {
202
+ export type ToastWithId = ToastItem & {
192
203
  id: string;
193
204
  };
194
205
  type ToastCommonProps = {
@@ -200,13 +211,14 @@ export type ToastContentProps = ToastCommonProps & {
200
211
  icon?: IconLike | false;
201
212
  onClose?: () => void;
202
213
  children?: Snippet;
214
+ button?: ToastButton;
203
215
  };
204
216
  export type ToastContainerProps = ToastCommonProps & {
205
217
  shape?: Shape;
206
218
  size?: ContainerSize;
207
219
  } & Omit<HTMLAttributes<HTMLElement>, 'title' | 'color' | 'size'>;
208
220
  export type ToastPanelProps = {
209
- items: Array<ToastItem & ToastId>;
221
+ items: Array<ToastWithId>;
210
222
  } & HTMLAttributes<HTMLDivElement>;
211
223
  export type ToastProps = ToastContentProps & ToastContainerProps;
212
224
  type Closable = {
@@ -223,6 +235,7 @@ export type ToastShow = {
223
235
  shape?: Shape;
224
236
  icon?: IconLike | false;
225
237
  size?: ContainerSize;
238
+ button?: ToastButton;
226
239
  };
227
240
  export type ToastOptions = {
228
241
  id?: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@immich/ui",
3
- "version": "0.65.3",
3
+ "version": "0.67.0",
4
4
  "license": "GNU Affero General Public License version 3",
5
5
  "repository": {
6
6
  "type": "git",