@immich/ui 0.30.0 → 0.30.1
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/README.md +9 -17
- package/dist/actions/shortcut.js +1 -1
- package/dist/components/Alert/Alert.svelte +92 -94
- package/dist/components/AppShell/AppShell.svelte +26 -26
- package/dist/components/AppShell/AppShellHeader.svelte +8 -8
- package/dist/components/AppShell/AppShellSidebar.svelte +20 -20
- package/dist/components/AppShell/PageLayout.svelte +29 -35
- package/dist/components/Avatar/Avatar.svelte +45 -55
- package/dist/components/Button/Button.svelte +3 -3
- package/dist/components/Card/Card.svelte +131 -135
- package/dist/components/Card/CardBody.svelte +9 -9
- package/dist/components/Card/CardDescription.svelte +9 -9
- package/dist/components/Card/CardFooter.svelte +9 -9
- package/dist/components/Card/CardHeader.svelte +9 -9
- package/dist/components/Card/CardTitle.svelte +14 -14
- package/dist/components/Checkbox/Checkbox.svelte +109 -110
- package/dist/components/CloseButton/CloseButton.svelte +12 -17
- package/dist/components/Code/Code.svelte +58 -65
- package/dist/components/CommandPalette/CommandPalette.svelte +126 -131
- package/dist/components/CommandPalette/CommandPaletteItem.svelte +61 -61
- package/dist/components/ConfirmModal/ConfirmModal.svelte +48 -48
- package/dist/components/Container/Container.svelte +25 -25
- package/dist/components/Field/Field.svelte +21 -21
- package/dist/components/FormatBytes/FormatBytes.svelte +9 -9
- package/dist/components/Heading/Heading.svelte +41 -47
- package/dist/components/HelperText/HelperText.svelte +17 -17
- package/dist/components/Icon/Icon.svelte +37 -42
- package/dist/components/IconButton/IconButton.svelte +6 -13
- package/dist/components/Input/Input.svelte +149 -152
- package/dist/components/Kbd/Kbd.svelte +25 -25
- package/dist/components/Label/Label.svelte +6 -6
- package/dist/components/Link/Link.svelte +18 -25
- package/dist/components/LoadingSpinner/LoadingSpinner.svelte +46 -46
- package/dist/components/Logo/Logo.svelte +53 -53
- package/dist/components/Modal/Modal.svelte +110 -114
- package/dist/components/Modal/ModalBody.svelte +8 -8
- package/dist/components/Modal/ModalFooter.svelte +8 -8
- package/dist/components/Modal/ModalHeader.svelte +8 -8
- package/dist/components/MultiSelect/MultiSelect.svelte +7 -7
- package/dist/components/Navbar/NavbarGroup.svelte +5 -5
- package/dist/components/Navbar/NavbarItem.svelte +59 -61
- package/dist/components/PasswordInput/PasswordInput.svelte +29 -31
- package/dist/components/Scrollable/Scrollable.svelte +49 -49
- package/dist/components/Select/Select.svelte +9 -9
- package/dist/components/Stack/HStack.svelte +4 -4
- package/dist/components/Stack/Stack.svelte +62 -62
- package/dist/components/Stack/VStack.svelte +4 -4
- package/dist/components/SupporterBadge/SupporterBadge.svelte +80 -80
- package/dist/components/Switch/Switch.svelte +95 -96
- package/dist/components/Text/Text.svelte +27 -34
- package/dist/components/Textarea/Textarea.svelte +103 -104
- package/dist/components/ThemeSwitcher/ThemeSwitcher.svelte +30 -43
- package/dist/internal/Button.svelte +177 -177
- package/dist/internal/Child.svelte +21 -21
- package/dist/internal/Select.svelte +151 -158
- package/dist/internal/Text.svelte +42 -50
- package/dist/site/SiteFooter.svelte +60 -76
- package/dist/site/SiteFooterLink.svelte +14 -14
- package/dist/theme/default.css +40 -40
- package/dist/utilities/byte-units.js +2 -13
- package/package.json +77 -77
|
@@ -1,163 +1,156 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
let internalValue = $derived(
|
|
77
|
-
multiple ? values.map(({ value }) => value) : (values[0]?.value ?? ''),
|
|
78
|
-
);
|
|
2
|
+
import { getFieldContext } from '../common/context.svelte.js';
|
|
3
|
+
import Field from '../components/Field/Field.svelte';
|
|
4
|
+
import Icon from '../components/Icon/Icon.svelte';
|
|
5
|
+
import IconButton from '../components/IconButton/IconButton.svelte';
|
|
6
|
+
import Input from '../components/Input/Input.svelte';
|
|
7
|
+
import Label from '../components/Label/Label.svelte';
|
|
8
|
+
import type { SelectCommonProps, SelectItem } from '../types.js';
|
|
9
|
+
import { cleanClass, generateId } from '../utils.js';
|
|
10
|
+
import { mdiArrowDown, mdiArrowUp, mdiCheck, mdiUnfoldMoreHorizontal } from '@mdi/js';
|
|
11
|
+
import { Select } from 'bits-ui';
|
|
12
|
+
|
|
13
|
+
type T = SelectItem;
|
|
14
|
+
|
|
15
|
+
type Props = {
|
|
16
|
+
multiple?: boolean;
|
|
17
|
+
values: T[];
|
|
18
|
+
asLabel?: (items: T[]) => string;
|
|
19
|
+
onChange?: (values: T[]) => void;
|
|
20
|
+
} & SelectCommonProps<T>;
|
|
21
|
+
|
|
22
|
+
let {
|
|
23
|
+
data,
|
|
24
|
+
shape,
|
|
25
|
+
size = 'medium',
|
|
26
|
+
multiple = false,
|
|
27
|
+
values = $bindable([]),
|
|
28
|
+
onChange,
|
|
29
|
+
asLabel = (options: T[]) => options.map(({ label }) => label).join(', '),
|
|
30
|
+
placeholder,
|
|
31
|
+
class: className,
|
|
32
|
+
}: Props = $props();
|
|
33
|
+
|
|
34
|
+
const asOptions = (items: string[] | T[]) => {
|
|
35
|
+
return items.map((item) => {
|
|
36
|
+
if (typeof item === 'string') {
|
|
37
|
+
return { value: item, label: item } as T;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const label = item.label ?? item.value;
|
|
41
|
+
return { ...item, label };
|
|
42
|
+
});
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
const options = $derived(asOptions(data));
|
|
46
|
+
|
|
47
|
+
const { readOnly, required, invalid, disabled, label, ...labelProps } = $derived(getFieldContext());
|
|
48
|
+
|
|
49
|
+
const id = generateId();
|
|
50
|
+
const inputId = `input-${id}`;
|
|
51
|
+
const labelId = `label-${id}`;
|
|
52
|
+
|
|
53
|
+
const selectedLabel = $derived(asLabel(values));
|
|
54
|
+
|
|
55
|
+
let inputRef = $state<HTMLElement | null>(null);
|
|
56
|
+
let contentRef = $state<HTMLElement | null>(null);
|
|
57
|
+
let ref = $state<HTMLElement | null>(null);
|
|
58
|
+
|
|
59
|
+
$effect(() => {
|
|
60
|
+
if (ref && contentRef) {
|
|
61
|
+
contentRef.style.width = `${ref.clientWidth}px`;
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
const findOption = (value: string) => options.find((option) => option.value === value);
|
|
66
|
+
|
|
67
|
+
const onValueChange = (items: string[] | string) => {
|
|
68
|
+
values = multiple
|
|
69
|
+
? ((items as string[]).map(findOption) as T[])
|
|
70
|
+
: [findOption(items as string) as T].filter(Boolean);
|
|
71
|
+
|
|
72
|
+
onChange?.(values);
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
let internalValue = $derived(multiple ? values.map(({ value }) => value) : (values[0]?.value ?? ''));
|
|
79
76
|
</script>
|
|
80
77
|
|
|
81
78
|
<div class={cleanClass('flex flex-col gap-1', className)} bind:this={ref}>
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
</Select.ScrollDownButton>
|
|
160
|
-
</Select.Content>
|
|
161
|
-
</Select.Portal>
|
|
162
|
-
</Select.Root>
|
|
79
|
+
{#if label}
|
|
80
|
+
<Label id={labelId} for={inputId} {label} {...labelProps} />
|
|
81
|
+
{/if}
|
|
82
|
+
|
|
83
|
+
<Select.Root type={multiple ? 'multiple' : 'single'} bind:value={internalValue as never} {onValueChange}>
|
|
84
|
+
<Select.Trigger
|
|
85
|
+
{disabled}
|
|
86
|
+
class="w-full items-center gap-1 rounded-lg focus-visible:outline-none"
|
|
87
|
+
aria-label={placeholder}
|
|
88
|
+
>
|
|
89
|
+
<Field {readOnly} {required} {disabled} {invalid}>
|
|
90
|
+
<Input
|
|
91
|
+
bind:containerRef={inputRef}
|
|
92
|
+
id={inputId}
|
|
93
|
+
{size}
|
|
94
|
+
{shape}
|
|
95
|
+
{placeholder}
|
|
96
|
+
value={selectedLabel}
|
|
97
|
+
readonly
|
|
98
|
+
aria-labelledby={labelId}
|
|
99
|
+
aria-readonly
|
|
100
|
+
>
|
|
101
|
+
{#snippet trailingIcon()}
|
|
102
|
+
<IconButton
|
|
103
|
+
variant="ghost"
|
|
104
|
+
shape="round"
|
|
105
|
+
color="secondary"
|
|
106
|
+
class="m-1"
|
|
107
|
+
icon={mdiUnfoldMoreHorizontal}
|
|
108
|
+
{disabled}
|
|
109
|
+
aria-label="Expand"
|
|
110
|
+
/>
|
|
111
|
+
{/snippet}
|
|
112
|
+
</Input>
|
|
113
|
+
</Field>
|
|
114
|
+
</Select.Trigger>
|
|
115
|
+
<Select.Portal>
|
|
116
|
+
<Select.Content
|
|
117
|
+
bind:ref={contentRef}
|
|
118
|
+
class="bg-light text-dark max-h-96 rounded-xl border py-3 outline-none select-none"
|
|
119
|
+
sideOffset={10}
|
|
120
|
+
>
|
|
121
|
+
<Select.ScrollUpButton class="flex w-full items-center justify-center">
|
|
122
|
+
<Icon icon={mdiArrowUp} />
|
|
123
|
+
</Select.ScrollUpButton>
|
|
124
|
+
<Select.Viewport>
|
|
125
|
+
{#each options as { value, label, disabled }, i (i + value)}
|
|
126
|
+
<Select.Item
|
|
127
|
+
class={cleanClass(
|
|
128
|
+
'hover:bg-subtle data-[selected]:bg-primary/10 flex h-10 w-full items-center px-5 py-3 text-sm duration-75 outline-none select-none data-disabled:opacity-50',
|
|
129
|
+
disabled ? 'cursor-not-allowed' : 'cursor-pointer',
|
|
130
|
+
)}
|
|
131
|
+
{value}
|
|
132
|
+
{label}
|
|
133
|
+
{disabled}
|
|
134
|
+
>
|
|
135
|
+
{#snippet children({ selected })}
|
|
136
|
+
<div
|
|
137
|
+
class="flex cursor-pointer items-center justify-center gap-2 text-sm font-medium whitespace-nowrap transition-colors"
|
|
138
|
+
>
|
|
139
|
+
<span>{label}</span>
|
|
140
|
+
</div>
|
|
141
|
+
{#if selected}
|
|
142
|
+
<div class="ms-auto">
|
|
143
|
+
<Icon icon={mdiCheck} />
|
|
144
|
+
</div>
|
|
145
|
+
{/if}
|
|
146
|
+
{/snippet}
|
|
147
|
+
</Select.Item>
|
|
148
|
+
{/each}
|
|
149
|
+
</Select.Viewport>
|
|
150
|
+
<Select.ScrollDownButton class="flex w-full items-center justify-center">
|
|
151
|
+
<Icon icon={mdiArrowDown} />
|
|
152
|
+
</Select.ScrollDownButton>
|
|
153
|
+
</Select.Content>
|
|
154
|
+
</Select.Portal>
|
|
155
|
+
</Select.Root>
|
|
163
156
|
</div>
|
|
@@ -1,60 +1,52 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
2
|
+
import type { FontWeight, HeadingColor, HeadingTag, TextVariant } 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
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
8
|
+
type Props = {
|
|
9
|
+
/**
|
|
10
|
+
* The HTML element type.
|
|
11
|
+
*/
|
|
12
|
+
tag?: HeadingTag;
|
|
13
|
+
fontWeight?: FontWeight;
|
|
14
|
+
variant?: TextVariant;
|
|
15
|
+
color?: HeadingColor;
|
|
16
|
+
class?: string;
|
|
17
17
|
|
|
18
|
-
|
|
19
|
-
|
|
18
|
+
children: Snippet;
|
|
19
|
+
} & HTMLAttributes<HTMLElement>;
|
|
20
20
|
|
|
21
|
-
|
|
22
|
-
tag = 'p',
|
|
23
|
-
color,
|
|
24
|
-
fontWeight,
|
|
25
|
-
variant,
|
|
26
|
-
class: className,
|
|
27
|
-
children,
|
|
28
|
-
...restProps
|
|
29
|
-
}: Props = $props();
|
|
21
|
+
const { tag = 'p', color, fontWeight, variant, class: className, children, ...restProps }: Props = $props();
|
|
30
22
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
23
|
+
const styles = tv({
|
|
24
|
+
variants: {
|
|
25
|
+
color: {
|
|
26
|
+
muted: 'text-gray-600 dark:text-gray-400',
|
|
27
|
+
primary: 'text-primary',
|
|
28
|
+
secondary: 'text-dark',
|
|
29
|
+
success: 'text-success',
|
|
30
|
+
danger: 'text-danger',
|
|
31
|
+
warning: 'text-warning',
|
|
32
|
+
info: 'text-info',
|
|
33
|
+
},
|
|
34
|
+
fontWeight: {
|
|
35
|
+
light: 'font-light',
|
|
36
|
+
normal: 'font-normal',
|
|
37
|
+
'semi-bold': 'font-semibold',
|
|
38
|
+
bold: 'font-bold',
|
|
39
|
+
'extra-bold': 'font-extrabold',
|
|
40
|
+
},
|
|
41
|
+
variant: {
|
|
42
|
+
italic: 'italic',
|
|
43
|
+
},
|
|
44
|
+
},
|
|
45
|
+
});
|
|
54
46
|
|
|
55
|
-
|
|
47
|
+
const classList = $derived(cleanClass(styles({ color, fontWeight, variant }), className));
|
|
56
48
|
</script>
|
|
57
49
|
|
|
58
50
|
<svelte:element this={tag} class={classList} {...restProps}>
|
|
59
|
-
|
|
51
|
+
{@render children()}
|
|
60
52
|
</svelte:element>
|
|
@@ -1,84 +1,68 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
2
|
+
import Heading from '../components/Heading/Heading.svelte';
|
|
3
|
+
import Stack from '../components/Stack/Stack.svelte';
|
|
4
|
+
import VStack from '../components/Stack/VStack.svelte';
|
|
5
|
+
import Text from '../components/Text/Text.svelte';
|
|
6
|
+
import ThemeSwitcher from '../components/ThemeSwitcher/ThemeSwitcher.svelte';
|
|
7
|
+
import { Constants } from './constants.js';
|
|
8
|
+
import SiteFooterLink from './SiteFooterLink.svelte';
|
|
9
|
+
import {
|
|
10
|
+
mdiBookshelf,
|
|
11
|
+
mdiChartGantt,
|
|
12
|
+
mdiCubeOutline,
|
|
13
|
+
mdiHomeSearchOutline,
|
|
14
|
+
mdiKey,
|
|
15
|
+
mdiOfficeBuildingOutline,
|
|
16
|
+
mdiScriptTextOutline,
|
|
17
|
+
mdiServerOutline,
|
|
18
|
+
mdiShoppingOutline,
|
|
19
|
+
} from '@mdi/js';
|
|
20
|
+
import { siAndroid, siApple, siDiscord, siGithub, siReddit } from 'simple-icons';
|
|
21
21
|
</script>
|
|
22
22
|
|
|
23
|
-
<div class="
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
23
|
+
<div class="mt-16 rounded-t-3xl bg-gray-100 p-8 dark:bg-neutral-900">
|
|
24
|
+
<div class="mx-auto max-w-(--breakpoint-lg) xl:py-8">
|
|
25
|
+
<Stack gap={8}>
|
|
26
|
+
<div class="place-center grid grid-cols-2 gap-8 md:grid-cols-3 lg:grid-cols-5">
|
|
27
|
+
<Stack>
|
|
28
|
+
<Heading size="tiny">Social</Heading>
|
|
29
|
+
<SiteFooterLink href={Constants.Socials.Github} icon={siGithub} text="GitHub" />
|
|
30
|
+
<SiteFooterLink href={Constants.Socials.Discord} icon={siDiscord} text="Discord" />
|
|
31
|
+
<SiteFooterLink href={Constants.Socials.Reddit} icon={siReddit} text="Reddit" />
|
|
32
|
+
</Stack>
|
|
33
33
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
icon={mdiServerOutline}
|
|
41
|
-
text="Server"
|
|
42
|
-
/>
|
|
43
|
-
</Stack>
|
|
34
|
+
<Stack>
|
|
35
|
+
<Heading size="tiny">Download</Heading>
|
|
36
|
+
<SiteFooterLink href={Constants.Get.Android} icon={siAndroid} text="Android" />
|
|
37
|
+
<SiteFooterLink href={Constants.Get.iOS} icon={siApple} text="iOS" />
|
|
38
|
+
<SiteFooterLink href={Constants.Get.DockerCompose} icon={mdiServerOutline} text="Server" />
|
|
39
|
+
</Stack>
|
|
44
40
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
/>
|
|
52
|
-
<SiteFooterLink href={Constants.Sites.Buy} icon={mdiKey} text="Purchase" />
|
|
53
|
-
<SiteFooterLink href={Constants.Sites.Store} icon={mdiShoppingOutline} text="Merch" />
|
|
54
|
-
</Stack>
|
|
41
|
+
<Stack>
|
|
42
|
+
<Heading size="tiny">Company</Heading>
|
|
43
|
+
<SiteFooterLink href={Constants.Socials.Futo} icon={mdiOfficeBuildingOutline} text="FUTO" />
|
|
44
|
+
<SiteFooterLink href={Constants.Sites.Buy} icon={mdiKey} text="Purchase" />
|
|
45
|
+
<SiteFooterLink href={Constants.Sites.Store} icon={mdiShoppingOutline} text="Merch" />
|
|
46
|
+
</Stack>
|
|
55
47
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
/>
|
|
63
|
-
<SiteFooterLink href={Constants.Sites.My} icon={mdiHomeSearchOutline} text="My Immich" />
|
|
64
|
-
<SiteFooterLink href={Constants.Sites.Api} icon={mdiCubeOutline} text="Immich API" />
|
|
65
|
-
</Stack>
|
|
48
|
+
<Stack>
|
|
49
|
+
<Heading size="tiny">Sites</Heading>
|
|
50
|
+
<SiteFooterLink href={Constants.Sites.Docs} icon={mdiScriptTextOutline} text="Documentation" />
|
|
51
|
+
<SiteFooterLink href={Constants.Sites.My} icon={mdiHomeSearchOutline} text="My Immich" />
|
|
52
|
+
<SiteFooterLink href={Constants.Sites.Api} icon={mdiCubeOutline} text="Immich API" />
|
|
53
|
+
</Stack>
|
|
66
54
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
<ThemeSwitcher color="secondary" />
|
|
81
|
-
</VStack>
|
|
82
|
-
</Stack>
|
|
83
|
-
</div>
|
|
55
|
+
<Stack>
|
|
56
|
+
<Heading size="tiny">Miscellaneous</Heading>
|
|
57
|
+
<SiteFooterLink href={Constants.Pages.Roadmap} icon={mdiChartGantt} text="Roadmap" />
|
|
58
|
+
<SiteFooterLink href={Constants.Pages.CursedKnowledge} icon={mdiBookshelf} text="Cursed Knowledge" />
|
|
59
|
+
</Stack>
|
|
60
|
+
</div>
|
|
61
|
+
<VStack class="text-center">
|
|
62
|
+
<Text size="large">This project is available under GNU AGPL v3 license.</Text>
|
|
63
|
+
<Text color="muted" variant="italic">Privacy should not be a luxury</Text>
|
|
64
|
+
<ThemeSwitcher color="secondary" />
|
|
65
|
+
</VStack>
|
|
66
|
+
</Stack>
|
|
67
|
+
</div>
|
|
84
68
|
</div>
|
|
@@ -1,21 +1,21 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
2
|
+
import HStack from '../components/Stack/HStack.svelte';
|
|
3
|
+
import Icon from '../components/Icon/Icon.svelte';
|
|
4
|
+
import Link from '../components/Link/Link.svelte';
|
|
5
|
+
import Text from '../components/Text/Text.svelte';
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
7
|
+
type Props = {
|
|
8
|
+
href: string;
|
|
9
|
+
icon: string | { path: string };
|
|
10
|
+
text: string;
|
|
11
|
+
};
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
let { href, icon, text }: Props = $props();
|
|
14
14
|
</script>
|
|
15
15
|
|
|
16
16
|
<Link {href} external>
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
17
|
+
<HStack>
|
|
18
|
+
<Icon {icon} size="1.5em" />
|
|
19
|
+
<Text>{text}</Text>
|
|
20
|
+
</HStack>
|
|
21
21
|
</Link>
|