@immich/ui 0.7.0 → 0.9.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.
- package/dist/assets/appstore-badge.svg +46 -0
- package/dist/assets/fdroid-badge.svg +124 -0
- package/dist/assets/playstore-badge.png +0 -0
- package/dist/components/Alert/Alert.svelte +73 -19
- package/dist/components/Alert/Alert.svelte.d.ts +7 -1
- package/dist/components/AppShell/AppShell.svelte +34 -0
- package/dist/components/AppShell/AppShell.svelte.d.ts +6 -0
- package/dist/components/AppShell/AppShellHeader.svelte +15 -0
- package/dist/components/AppShell/AppShellHeader.svelte.d.ts +5 -0
- package/dist/components/AppShell/AppShellSidebar.svelte +23 -0
- package/dist/components/AppShell/AppShellSidebar.svelte.d.ts +7 -0
- package/dist/components/AppShell/PageLayout.svelte +44 -0
- package/dist/components/AppShell/PageLayout.svelte.d.ts +9 -0
- package/dist/components/Avatar/Avatar.svelte +66 -0
- package/dist/components/Avatar/Avatar.svelte.d.ts +7 -0
- package/dist/components/Card/Card.svelte +9 -11
- package/dist/components/Card/CardBody.svelte +1 -1
- package/dist/components/Card/CardFooter.svelte +6 -2
- package/dist/components/Card/CardFooter.svelte.d.ts +1 -0
- package/dist/components/CloseButton/CloseButton.svelte +2 -2
- package/dist/components/Code/Code.svelte +62 -0
- package/dist/components/Code/Code.svelte.d.ts +9 -0
- package/dist/components/Form/Checkbox.svelte +67 -27
- package/dist/components/Form/Checkbox.svelte.d.ts +1 -1
- package/dist/components/Form/HelperText.svelte +3 -3
- package/dist/components/Form/HelperText.svelte.d.ts +2 -2
- package/dist/components/Form/Input.svelte +2 -1
- package/dist/components/Form/Input.svelte.d.ts +1 -1
- package/dist/components/Form/PasswordInput.svelte +3 -1
- package/dist/components/Form/PasswordInput.svelte.d.ts +1 -1
- package/dist/components/FormatBytes/FormatBytes.svelte +16 -0
- package/dist/components/FormatBytes/FormatBytes.svelte.d.ts +6 -0
- package/dist/components/Heading/Heading.svelte +3 -2
- package/dist/components/Heading/Heading.svelte.d.ts +2 -2
- package/dist/components/Logo/Logo.svelte +8 -8
- package/dist/components/Logo/Logo.svelte.d.ts +1 -2
- package/dist/components/MultiSelect/MultiSelect.svelte +15 -0
- package/dist/components/MultiSelect/MultiSelect.svelte.d.ts +3 -0
- package/dist/components/Navbar/NavbarGroup.svelte +12 -0
- package/dist/components/Navbar/NavbarGroup.svelte.d.ts +4 -0
- package/dist/components/Navbar/NavbarItem.svelte +30 -0
- package/dist/components/Navbar/NavbarItem.svelte.d.ts +7 -0
- package/dist/components/Scrollable/Scrollable.svelte +41 -0
- package/dist/components/Scrollable/Scrollable.svelte.d.ts +6 -0
- package/dist/components/Select/Select.svelte +15 -0
- package/dist/components/Select/Select.svelte.d.ts +3 -0
- package/dist/components/Switch/Switch.svelte +99 -0
- package/dist/components/Switch/Switch.svelte.d.ts +10 -0
- package/dist/components/Text/Text.svelte +16 -4
- package/dist/components/Text/Text.svelte.d.ts +2 -2
- package/dist/constants.d.ts +3 -0
- package/dist/constants.js +3 -0
- package/dist/index.d.ts +23 -0
- package/dist/index.js +26 -0
- package/dist/internal/Button.svelte +1 -10
- package/dist/internal/Select.svelte +174 -0
- package/dist/internal/Select.svelte.d.ts +9 -0
- package/dist/services/theme.svelte.d.ts +5 -0
- package/dist/services/theme.svelte.js +13 -0
- package/dist/types.d.ts +53 -18
- package/dist/types.js +5 -1
- package/dist/utilities/byte-units.d.ts +52 -0
- package/dist/utilities/byte-units.js +75 -0
- package/package.json +6 -4
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
import type { CloseButtonProps } from '../../types.js';
|
|
4
4
|
import { mdiClose } from '@mdi/js';
|
|
5
5
|
|
|
6
|
-
const { size = 'medium', variant = 'ghost' }: CloseButtonProps = $props();
|
|
6
|
+
const { size = 'medium', variant = 'ghost', ...restProps }: CloseButtonProps = $props();
|
|
7
7
|
</script>
|
|
8
8
|
|
|
9
|
-
<IconButton icon={mdiClose}
|
|
9
|
+
<IconButton {...restProps} icon={mdiClose} shape="round" {variant} {size} color="secondary" />
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { Size, TextColor } from '../../types.js';
|
|
3
|
+
import type { Snippet } from 'svelte';
|
|
4
|
+
import { tv } from 'tailwind-variants';
|
|
5
|
+
|
|
6
|
+
type Props = {
|
|
7
|
+
color?: TextColor;
|
|
8
|
+
size?: Size;
|
|
9
|
+
variant?: 'filled';
|
|
10
|
+
children: Snippet;
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
const { size = 'medium', variant, color = 'secondary', children }: Props = $props();
|
|
14
|
+
|
|
15
|
+
const styles = tv({
|
|
16
|
+
base: 'font-monospace',
|
|
17
|
+
variants: {
|
|
18
|
+
textColor: {
|
|
19
|
+
muted: 'text-gray-600 dark:text-gray-400',
|
|
20
|
+
primary: 'text-primary',
|
|
21
|
+
secondary: 'text-dark',
|
|
22
|
+
success: 'text-success',
|
|
23
|
+
danger: 'text-danger',
|
|
24
|
+
warning: 'text-warning',
|
|
25
|
+
info: 'text-info',
|
|
26
|
+
},
|
|
27
|
+
|
|
28
|
+
filled: {
|
|
29
|
+
true: 'rounded px-2 py-1',
|
|
30
|
+
false: '',
|
|
31
|
+
},
|
|
32
|
+
|
|
33
|
+
filledColor: {
|
|
34
|
+
false: '',
|
|
35
|
+
muted: 'bg-gray-600 dark:bg-gray-800 text-light',
|
|
36
|
+
primary: 'bg-primary text-light',
|
|
37
|
+
secondary: 'bg-dark text-light',
|
|
38
|
+
success: 'bg-success text-light',
|
|
39
|
+
danger: 'bg-danger text-light',
|
|
40
|
+
warning: 'bg-warning text-light',
|
|
41
|
+
info: 'bg-info text-dark',
|
|
42
|
+
},
|
|
43
|
+
|
|
44
|
+
size: {
|
|
45
|
+
tiny: 'text-xs',
|
|
46
|
+
small: 'text-sm',
|
|
47
|
+
medium: 'text-md',
|
|
48
|
+
large: 'text-lg',
|
|
49
|
+
giant: 'text-xl',
|
|
50
|
+
},
|
|
51
|
+
},
|
|
52
|
+
});
|
|
53
|
+
</script>
|
|
54
|
+
|
|
55
|
+
<code
|
|
56
|
+
class={styles({
|
|
57
|
+
filled: variant === 'filled',
|
|
58
|
+
filledColor: variant === 'filled' && color,
|
|
59
|
+
textColor: color,
|
|
60
|
+
size,
|
|
61
|
+
})}>{@render children()}</code
|
|
62
|
+
>
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
-
import {
|
|
3
|
-
import { mdiCheck, mdiMinus } from '@mdi/js';
|
|
2
|
+
import { getFieldContext } from '../../common/context.svelte.js';
|
|
4
3
|
import Icon from '../Icon/Icon.svelte';
|
|
5
|
-
import { tv } from 'tailwind-variants';
|
|
6
4
|
import type { Color, Shape, Size } from '../../types.js';
|
|
7
|
-
import { cleanClass } from '../../utils.js';
|
|
5
|
+
import { cleanClass, generateId } from '../../utils.js';
|
|
6
|
+
import { mdiCheck, mdiMinus } from '@mdi/js';
|
|
7
|
+
import { Checkbox as CheckboxPrimitive, type WithoutChildrenOrChild } from 'bits-ui';
|
|
8
|
+
import { tv } from 'tailwind-variants';
|
|
8
9
|
|
|
9
10
|
type CheckboxProps = WithoutChildrenOrChild<CheckboxPrimitive.RootProps> & {
|
|
10
11
|
color?: Color;
|
|
@@ -22,8 +23,16 @@
|
|
|
22
23
|
...restProps
|
|
23
24
|
}: CheckboxProps = $props();
|
|
24
25
|
|
|
25
|
-
const
|
|
26
|
-
|
|
26
|
+
const {
|
|
27
|
+
label,
|
|
28
|
+
readOnly = false,
|
|
29
|
+
required = false,
|
|
30
|
+
invalid = false,
|
|
31
|
+
disabled = false,
|
|
32
|
+
} = $derived(getFieldContext());
|
|
33
|
+
|
|
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',
|
|
27
36
|
variants: {
|
|
28
37
|
shape: {
|
|
29
38
|
rectangle: 'rounded-none',
|
|
@@ -32,7 +41,7 @@
|
|
|
32
41
|
},
|
|
33
42
|
color: {
|
|
34
43
|
primary: 'border-primary',
|
|
35
|
-
secondary: 'border-
|
|
44
|
+
secondary: 'border-dark',
|
|
36
45
|
success: 'border-success',
|
|
37
46
|
danger: 'border-danger',
|
|
38
47
|
warning: 'border-warning',
|
|
@@ -55,7 +64,7 @@
|
|
|
55
64
|
},
|
|
56
65
|
});
|
|
57
66
|
|
|
58
|
-
const
|
|
67
|
+
const iconStyles = tv({
|
|
59
68
|
variants: {
|
|
60
69
|
fullWidth: {
|
|
61
70
|
true: 'w-full',
|
|
@@ -70,24 +79,55 @@
|
|
|
70
79
|
},
|
|
71
80
|
},
|
|
72
81
|
});
|
|
82
|
+
|
|
83
|
+
const labelStyles = tv({
|
|
84
|
+
base: '',
|
|
85
|
+
variants: {
|
|
86
|
+
size: {
|
|
87
|
+
tiny: 'text-xs',
|
|
88
|
+
small: 'text-sm',
|
|
89
|
+
medium: 'text-md',
|
|
90
|
+
large: 'text-lg',
|
|
91
|
+
giant: 'text-xl',
|
|
92
|
+
},
|
|
93
|
+
},
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
const id = generateId();
|
|
97
|
+
const inputId = `input-${id}`;
|
|
98
|
+
const labelId = `label-${id}`;
|
|
73
99
|
</script>
|
|
74
100
|
|
|
75
|
-
<
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
101
|
+
<div class="flex flex-col gap-1">
|
|
102
|
+
{#if label}
|
|
103
|
+
<label id={labelId} for={inputId} class={labelStyles({ size })}>{label}</label>
|
|
104
|
+
{/if}
|
|
105
|
+
|
|
106
|
+
<CheckboxPrimitive.Root
|
|
107
|
+
bind:ref
|
|
108
|
+
class={cleanClass(
|
|
109
|
+
containerStyles({
|
|
110
|
+
size,
|
|
111
|
+
color: invalid ? 'danger' : color,
|
|
112
|
+
shape,
|
|
113
|
+
roundedSize: shape === 'semi-round' ? size : undefined,
|
|
114
|
+
}),
|
|
115
|
+
className,
|
|
116
|
+
)}
|
|
117
|
+
bind:checked
|
|
118
|
+
disabled={disabled || readOnly}
|
|
119
|
+
{required}
|
|
120
|
+
aria-readonly={disabled || readOnly}
|
|
121
|
+
{...restProps}
|
|
122
|
+
>
|
|
123
|
+
{#snippet children({ checked, indeterminate })}
|
|
124
|
+
<div class={cleanClass('flex items-center justify-center text-current')}>
|
|
125
|
+
{#if indeterminate}
|
|
126
|
+
<Icon icon={mdiMinus} size="100%" class={cleanClass(iconStyles({ color }))} />
|
|
127
|
+
{:else if checked}
|
|
128
|
+
<Icon icon={mdiCheck} size="100%" class={cleanClass(iconStyles({ color }))} />
|
|
129
|
+
{/if}
|
|
130
|
+
</div>
|
|
131
|
+
{/snippet}
|
|
132
|
+
</CheckboxPrimitive.Root>
|
|
133
|
+
</div>
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { Checkbox as CheckboxPrimitive } from 'bits-ui';
|
|
2
1
|
import type { Color, Shape, Size } from '../../types.js';
|
|
2
|
+
import { Checkbox as CheckboxPrimitive } from 'bits-ui';
|
|
3
3
|
declare const Checkbox: import("svelte").Component<Omit<Omit<CheckboxPrimitive.RootProps, "child">, "children"> & {
|
|
4
4
|
color?: Color;
|
|
5
5
|
shape?: Shape;
|
|
@@ -2,17 +2,17 @@
|
|
|
2
2
|
import Text from '../Text/Text.svelte';
|
|
3
3
|
import { ChildKey } from '../../constants.js';
|
|
4
4
|
import Child from '../../internal/Child.svelte';
|
|
5
|
-
import type {
|
|
5
|
+
import type { TextColor } from '../../types.js';
|
|
6
6
|
import { cleanClass } from '../../utils.js';
|
|
7
7
|
import type { Snippet } from 'svelte';
|
|
8
8
|
|
|
9
9
|
type Props = {
|
|
10
|
-
color?:
|
|
10
|
+
color?: TextColor;
|
|
11
11
|
class?: string;
|
|
12
12
|
children?: Snippet;
|
|
13
13
|
};
|
|
14
14
|
|
|
15
|
-
let { class: className, children, color }: Props = $props();
|
|
15
|
+
let { class: className, children, color = 'muted' }: Props = $props();
|
|
16
16
|
</script>
|
|
17
17
|
|
|
18
18
|
<Child for={ChildKey.Field} as={ChildKey.HelperText}>
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { TextColor } from '../../types.js';
|
|
2
2
|
import type { Snippet } from 'svelte';
|
|
3
3
|
declare const HelperText: import("svelte").Component<{
|
|
4
|
-
color?:
|
|
4
|
+
color?: TextColor;
|
|
5
5
|
class?: string;
|
|
6
6
|
children?: Snippet;
|
|
7
7
|
}, {}, "">;
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
import { tv } from 'tailwind-variants';
|
|
6
6
|
|
|
7
7
|
let {
|
|
8
|
+
containerRef = $bindable(null),
|
|
8
9
|
shape = 'semi-round',
|
|
9
10
|
size = 'medium',
|
|
10
11
|
class: className,
|
|
@@ -72,7 +73,7 @@
|
|
|
72
73
|
const labelId = `label-${id}`;
|
|
73
74
|
</script>
|
|
74
75
|
|
|
75
|
-
<div class="flex flex-col gap-1">
|
|
76
|
+
<div class="flex flex-col gap-1" bind:this={containerRef}>
|
|
76
77
|
{#if label}
|
|
77
78
|
<label id={labelId} for={inputId} class={labelStyles({ size })}>{label}</label>
|
|
78
79
|
{/if}
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
-
import
|
|
2
|
+
import Input from './Input.svelte';
|
|
3
|
+
import IconButton from '../IconButton/IconButton.svelte';
|
|
4
|
+
import type { PasswordInputProps } from '../../types.js';
|
|
3
5
|
import { mdiEyeOffOutline, mdiEyeOutline } from '@mdi/js';
|
|
4
6
|
|
|
5
7
|
let {
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { getBytesWithUnit } from '../../utilities/byte-units.js';
|
|
3
|
+
|
|
4
|
+
type Props = {
|
|
5
|
+
bytes: number;
|
|
6
|
+
precision?: number;
|
|
7
|
+
variant?: 'short' | 'narrow';
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
const { bytes, precision, variant = 'short' }: Props = $props();
|
|
11
|
+
|
|
12
|
+
const [value, unit] = $derived(getBytesWithUnit(bytes, precision));
|
|
13
|
+
const separator = $derived(variant === 'narrow' ? '' : ' ');
|
|
14
|
+
</script>
|
|
15
|
+
|
|
16
|
+
<span>{value}{separator}{unit}</span>
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
-
import type {
|
|
2
|
+
import type { HeadingColor, HeadingSize } from '../../types.js';
|
|
3
3
|
import { cleanClass } from '../../utils.js';
|
|
4
4
|
import type { Snippet } from 'svelte';
|
|
5
5
|
import { tv } from 'tailwind-variants';
|
|
6
6
|
|
|
7
7
|
type Props = {
|
|
8
8
|
size: HeadingSize;
|
|
9
|
-
color?:
|
|
9
|
+
color?: HeadingColor;
|
|
10
10
|
class?: string;
|
|
11
11
|
|
|
12
12
|
children: Snippet;
|
|
@@ -27,6 +27,7 @@
|
|
|
27
27
|
base: 'font-bold leading-none tracking-tight',
|
|
28
28
|
variants: {
|
|
29
29
|
color: {
|
|
30
|
+
muted: 'text-gray-600 dark:text-gray-400',
|
|
30
31
|
primary: 'text-primary',
|
|
31
32
|
secondary: 'text-dark',
|
|
32
33
|
success: 'text-success',
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { HeadingColor, HeadingSize } from '../../types.js';
|
|
2
2
|
import type { Snippet } from 'svelte';
|
|
3
3
|
declare const Heading: import("svelte").Component<{
|
|
4
4
|
size: HeadingSize;
|
|
5
|
-
color?:
|
|
5
|
+
color?: HeadingColor;
|
|
6
6
|
class?: string;
|
|
7
7
|
children: Snippet;
|
|
8
8
|
}, {}, "">;
|
|
@@ -1,30 +1,30 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
-
import icon from '../../assets/immich-logo.svg';
|
|
3
2
|
import inlineDark from '../../assets/immich-logo-inline-dark.svg';
|
|
4
3
|
import inlineLight from '../../assets/immich-logo-inline-light.svg';
|
|
5
4
|
import stackedDark from '../../assets/immich-logo-stacked-dark.svg';
|
|
6
5
|
import stackedLight from '../../assets/immich-logo-stacked-light.svg';
|
|
7
|
-
import
|
|
6
|
+
import icon from '../../assets/immich-logo.svg';
|
|
7
|
+
import { theme } from '../../services/theme.svelte.js';
|
|
8
|
+
import { Theme, type Size } from '../../types.js';
|
|
8
9
|
import { cleanClass } from '../../utils.js';
|
|
9
10
|
import { tv } from 'tailwind-variants';
|
|
10
11
|
|
|
11
12
|
type Props = {
|
|
12
13
|
size?: Size | 'landing';
|
|
13
|
-
theme?: 'dark' | 'light';
|
|
14
14
|
variant?: 'stacked' | 'inline' | 'logo' | 'icon';
|
|
15
15
|
class?: string;
|
|
16
16
|
};
|
|
17
17
|
|
|
18
|
-
const {
|
|
18
|
+
const { variant = 'logo', size = 'medium', class: className }: Props = $props();
|
|
19
19
|
|
|
20
|
-
const getUrl = (
|
|
20
|
+
const getUrl = (variant: Props['variant']): string => {
|
|
21
21
|
switch (variant) {
|
|
22
22
|
case 'stacked': {
|
|
23
|
-
return theme ===
|
|
23
|
+
return theme.value === Theme.Light ? stackedLight : stackedDark;
|
|
24
24
|
}
|
|
25
25
|
|
|
26
26
|
case 'inline': {
|
|
27
|
-
return theme ===
|
|
27
|
+
return theme.value === Theme.Light ? inlineLight : inlineDark;
|
|
28
28
|
}
|
|
29
29
|
|
|
30
30
|
default: {
|
|
@@ -53,7 +53,7 @@
|
|
|
53
53
|
},
|
|
54
54
|
});
|
|
55
55
|
|
|
56
|
-
const src = $derived(getUrl(
|
|
56
|
+
const src = $derived(getUrl(variant));
|
|
57
57
|
</script>
|
|
58
58
|
|
|
59
59
|
<img {src} class={cleanClass(styles({ size, variant }), className)} alt="Immich logo" />
|
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { type Size } from '../../types.js';
|
|
2
2
|
declare const Logo: import("svelte").Component<{
|
|
3
3
|
size?: Size | "landing";
|
|
4
|
-
theme?: "dark" | "light";
|
|
5
4
|
variant?: "stacked" | "inline" | "logo" | "icon";
|
|
6
5
|
class?: string;
|
|
7
6
|
}, {}, "">;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import InternalSelect from '../../internal/Select.svelte';
|
|
3
|
+
import type { MultiSelectProps, SelectItem } from '../../types.js';
|
|
4
|
+
|
|
5
|
+
type T = SelectItem;
|
|
6
|
+
|
|
7
|
+
let { value = $bindable(), onChange, ...restProps }: MultiSelectProps<T> = $props();
|
|
8
|
+
|
|
9
|
+
const handleChange = (items: T[]) => {
|
|
10
|
+
value = items;
|
|
11
|
+
onChange?.(value);
|
|
12
|
+
};
|
|
13
|
+
</script>
|
|
14
|
+
|
|
15
|
+
<InternalSelect multiple onChange={handleChange} {...restProps} />
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
type Props = {
|
|
3
|
+
title: string;
|
|
4
|
+
};
|
|
5
|
+
|
|
6
|
+
let { title }: Props = $props();
|
|
7
|
+
</script>
|
|
8
|
+
|
|
9
|
+
<div class="text-xs transition-all duration-200">
|
|
10
|
+
<p class="hidden px-6 py-4 uppercase group-hover:sm:block md:block">{title}</p>
|
|
11
|
+
<hr class="mx-4 mb-[31px] mt-8 block group-hover:sm:hidden md:hidden" />
|
|
12
|
+
</div>
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import Icon from '../Icon/Icon.svelte';
|
|
3
|
+
import type { IconProps } from '../../types.js';
|
|
4
|
+
import { tv } from 'tailwind-variants';
|
|
5
|
+
|
|
6
|
+
type Props = {
|
|
7
|
+
title: string;
|
|
8
|
+
active?: boolean;
|
|
9
|
+
href: string;
|
|
10
|
+
} & IconProps;
|
|
11
|
+
|
|
12
|
+
let { href, title, active = false, ...iconProps }: Props = $props();
|
|
13
|
+
|
|
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',
|
|
16
|
+
variants: {
|
|
17
|
+
active: {
|
|
18
|
+
true: 'bg-primary/10 text-primary',
|
|
19
|
+
false: '',
|
|
20
|
+
},
|
|
21
|
+
},
|
|
22
|
+
});
|
|
23
|
+
</script>
|
|
24
|
+
|
|
25
|
+
<a {href} draggable="false" aria-current={active ? 'page' : undefined} class={styles({ active })}>
|
|
26
|
+
<div class="flex w-full place-items-center gap-4 overflow-hidden truncate">
|
|
27
|
+
<Icon size="1.5em" class="shrink-0" aria-hidden={true} {...iconProps} />
|
|
28
|
+
<span class="text-sm font-medium">{title}</span>
|
|
29
|
+
</div>
|
|
30
|
+
</a>
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { cleanClass } from '../../utils.js';
|
|
3
|
+
import type { Snippet } from 'svelte';
|
|
4
|
+
|
|
5
|
+
type Props = {
|
|
6
|
+
class?: string;
|
|
7
|
+
children?: Snippet;
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
const { class: className, children }: Props = $props();
|
|
11
|
+
</script>
|
|
12
|
+
|
|
13
|
+
<div class={cleanClass('immich-scrollbar h-full w-full overflow-auto', className)}>
|
|
14
|
+
{@render children?.()}
|
|
15
|
+
</div>
|
|
16
|
+
|
|
17
|
+
<style>
|
|
18
|
+
/* width */
|
|
19
|
+
.immich-scrollbar::-webkit-scrollbar {
|
|
20
|
+
width: 8px;
|
|
21
|
+
height: 8px;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/* Track */
|
|
25
|
+
.immich-scrollbar::-webkit-scrollbar-track {
|
|
26
|
+
background: #f1f1f1;
|
|
27
|
+
border-radius: 16px;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/* Handle */
|
|
31
|
+
.immich-scrollbar::-webkit-scrollbar-thumb {
|
|
32
|
+
background: rgba(85, 86, 87, 0.408);
|
|
33
|
+
border-radius: 16px;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/* Handle on hover */
|
|
37
|
+
.immich-scrollbar::-webkit-scrollbar-thumb:hover {
|
|
38
|
+
background: #4250afad;
|
|
39
|
+
border-radius: 16px;
|
|
40
|
+
}
|
|
41
|
+
</style>
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import InternalSelect from '../../internal/Select.svelte';
|
|
3
|
+
import type { SelectItem, SelectProps } from '../../types.js';
|
|
4
|
+
|
|
5
|
+
type T = SelectItem;
|
|
6
|
+
|
|
7
|
+
let { value = $bindable(), onChange, ...restProps }: SelectProps<T> = $props();
|
|
8
|
+
|
|
9
|
+
const handleChange = (items: T[]) => {
|
|
10
|
+
value = items[0] as T;
|
|
11
|
+
onChange?.(value);
|
|
12
|
+
};
|
|
13
|
+
</script>
|
|
14
|
+
|
|
15
|
+
<InternalSelect onChange={handleChange} {...restProps} />
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { getFieldContext } from '../../common/context.svelte.js';
|
|
3
|
+
import type { Color } from '../../types.js';
|
|
4
|
+
import { cleanClass } from '../../utils.js';
|
|
5
|
+
import type { HTMLInputAttributes } from 'svelte/elements';
|
|
6
|
+
import { tv } from 'tailwind-variants';
|
|
7
|
+
|
|
8
|
+
type Props = {
|
|
9
|
+
checked?: boolean;
|
|
10
|
+
color?: Color;
|
|
11
|
+
disabled?: boolean;
|
|
12
|
+
class?: string;
|
|
13
|
+
onToggle?: ((checked: boolean) => void) | undefined;
|
|
14
|
+
} & HTMLInputAttributes;
|
|
15
|
+
|
|
16
|
+
let {
|
|
17
|
+
checked = $bindable(false),
|
|
18
|
+
class: className,
|
|
19
|
+
color = 'primary',
|
|
20
|
+
onToggle = undefined,
|
|
21
|
+
...restProps
|
|
22
|
+
}: Props = $props();
|
|
23
|
+
|
|
24
|
+
const {
|
|
25
|
+
label,
|
|
26
|
+
readOnly = false,
|
|
27
|
+
required = false,
|
|
28
|
+
disabled = false,
|
|
29
|
+
} = $derived(getFieldContext());
|
|
30
|
+
|
|
31
|
+
const enabled = $derived(checked && !disabled);
|
|
32
|
+
|
|
33
|
+
const handleToggle = (event: Event) => onToggle?.((event.target as HTMLInputElement).checked);
|
|
34
|
+
|
|
35
|
+
const wrapper = tv({
|
|
36
|
+
base: 'relative flex flex-col justify-center',
|
|
37
|
+
variants: {
|
|
38
|
+
disabled: {
|
|
39
|
+
true: 'cursor-not-allowed',
|
|
40
|
+
false: 'cursor-pointer',
|
|
41
|
+
},
|
|
42
|
+
},
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
const bar = tv({
|
|
46
|
+
base: 'w-12 h-3 my-2 rounded-full border border-transparent',
|
|
47
|
+
variants: {
|
|
48
|
+
fillColor: {
|
|
49
|
+
default: 'bg-gray-400',
|
|
50
|
+
primary: 'bg-primary/50',
|
|
51
|
+
secondary: 'bg-dark/50',
|
|
52
|
+
success: 'bg-success/50',
|
|
53
|
+
danger: 'bg-danger/50',
|
|
54
|
+
warning: 'bg-warning/50',
|
|
55
|
+
info: 'bg-info/50',
|
|
56
|
+
},
|
|
57
|
+
},
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
const dot = tv({
|
|
61
|
+
base: 'absolute transition-colors h-6 w-6 rounded-full transition-transform duration-[400ms]',
|
|
62
|
+
variants: {
|
|
63
|
+
checked: {
|
|
64
|
+
true: 'translate-x-6',
|
|
65
|
+
false: '',
|
|
66
|
+
},
|
|
67
|
+
fillColor: {
|
|
68
|
+
default: 'bg-gray-600',
|
|
69
|
+
primary: 'bg-primary',
|
|
70
|
+
secondary: 'bg-dark',
|
|
71
|
+
success: 'bg-success',
|
|
72
|
+
danger: 'bg-danger',
|
|
73
|
+
warning: 'bg-warning',
|
|
74
|
+
info: 'bg-info',
|
|
75
|
+
},
|
|
76
|
+
},
|
|
77
|
+
});
|
|
78
|
+
</script>
|
|
79
|
+
|
|
80
|
+
<label class={cleanClass(className)}>
|
|
81
|
+
{label}
|
|
82
|
+
<span class={wrapper({ disabled })}>
|
|
83
|
+
<input
|
|
84
|
+
class="hidden"
|
|
85
|
+
type="checkbox"
|
|
86
|
+
bind:checked
|
|
87
|
+
onclick={handleToggle}
|
|
88
|
+
{required}
|
|
89
|
+
aria-required={required}
|
|
90
|
+
{disabled}
|
|
91
|
+
aria-disabled={disabled}
|
|
92
|
+
readonly={readOnly}
|
|
93
|
+
aria-readonly={readOnly}
|
|
94
|
+
{...restProps}
|
|
95
|
+
/>
|
|
96
|
+
<span class={bar({ fillColor: enabled ? color : 'default' })}> </span>
|
|
97
|
+
<span class={dot({ checked: enabled, fillColor: enabled ? color : 'default' })}></span>
|
|
98
|
+
</span>
|
|
99
|
+
</label>
|