svelora 3.0.0 → 3.0.2
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/Accordion/Accordion.svelte +66 -97
- package/dist/Alert/Alert.svelte +39 -64
- package/dist/Alert/Alert.svelte.d.ts +1 -1
- package/dist/Avatar/Avatar.svelte +35 -75
- package/dist/AvatarGroup/AvatarGroup.svelte +38 -55
- package/dist/Badge/Badge.svelte +28 -50
- package/dist/Banner/Banner.svelte +46 -41
- package/dist/Banner/Banner.svelte.d.ts +1 -1
- package/dist/Breadcrumb/Breadcrumb.svelte +32 -26
- package/dist/Button/Button.svelte +70 -138
- package/dist/Calendar/Calendar.svelte +94 -157
- package/dist/Calendar/Calendar.svelte.d.ts +1 -1
- package/dist/Card/Card.svelte +18 -31
- package/dist/Carousel/Carousel.svelte +118 -173
- package/dist/Checkbox/Checkbox.svelte +52 -97
- package/dist/CheckboxGroup/CheckboxGroup.svelte +62 -107
- package/dist/CheckboxGroup/CheckboxGroup.svelte.d.ts +1 -1
- package/dist/Chip/Chip.svelte +22 -34
- package/dist/CodeBlock/CodeBlock.svelte +42 -59
- package/dist/Collapsible/Collapsible.svelte +22 -38
- package/dist/Collapsible/Collapsible.svelte.d.ts +1 -1
- package/dist/Collapsible/CollapsibleTestWrapper.svelte +2 -5
- package/dist/Collapsible/CollapsibleTestWrapper.svelte.d.ts +1 -1
- package/dist/Command/Command.svelte +40 -77
- package/dist/Command/Command.svelte.d.ts +1 -1
- package/dist/Command/CommandTestWrapper.svelte +2 -10
- package/dist/Command/CommandTestWrapper.svelte.d.ts +1 -1
- package/dist/Container/Container.svelte +11 -14
- package/dist/ContextMenu/ContextMenu.svelte +51 -114
- package/dist/ContextMenu/ContextMenu.svelte.d.ts +1 -1
- package/dist/Drawer/Drawer.svelte +72 -110
- package/dist/Drawer/DrawerTriggerTestWrapper.svelte +1 -2
- package/dist/DropdownMenu/DropdownMenu.svelte +63 -124
- package/dist/DropdownMenu/DropdownMenu.svelte.d.ts +1 -1
- package/dist/DropdownMenu/DropdownMenuTriggerTestWrapper.svelte +2 -5
- package/dist/Editor/Editor.svelte +441 -576
- package/dist/Editor/Editor.svelte.d.ts +1 -1
- package/dist/Editor/EditorUrlPrompt.svelte +40 -53
- package/dist/Editor/SlashPopup.svelte +12 -24
- package/dist/Empty/Empty.svelte +32 -63
- package/dist/FieldGroup/FieldGroup.svelte +23 -38
- package/dist/FileUpload/FileUpload.svelte +242 -320
- package/dist/FileUpload/FileUpload.svelte.d.ts +1 -1
- package/dist/Fonts/Fonts.svelte +15 -37
- package/dist/Form/Form.svelte +112 -170
- package/dist/FormField/FormField.svelte +102 -135
- package/dist/Icon/Icon.svelte +7 -32
- package/dist/Input/Input.svelte +71 -141
- package/dist/Input/Input.svelte.d.ts +2 -2
- package/dist/Kbd/Kbd.svelte +18 -34
- package/dist/Link/Link.svelte +129 -196
- package/dist/LocaleButton/LocaleButton.svelte +165 -0
- package/dist/LocaleButton/LocaleButton.svelte.d.ts +5 -0
- package/dist/LocaleButton/index.d.ts +2 -0
- package/dist/LocaleButton/index.js +1 -0
- package/dist/LocaleButton/locale-button.types.d.ts +182 -0
- package/dist/LocaleButton/locale-button.types.js +1 -0
- package/dist/LocaleButton/locale-button.variants.d.ts +61 -0
- package/dist/LocaleButton/locale-button.variants.js +34 -0
- package/dist/Modal/Modal.svelte +52 -106
- package/dist/Modal/ModalTriggerTestWrapper.svelte +1 -2
- package/dist/Pagination/Pagination.svelte +48 -92
- package/dist/Pagination/pagination.variants.d.ts +1 -1
- package/dist/PinInput/PinInput.svelte +57 -111
- package/dist/PinInput/PinInput.svelte.d.ts +1 -1
- package/dist/Popover/Popover.svelte +28 -61
- package/dist/Popover/Popover.svelte.d.ts +1 -1
- package/dist/Progress/Progress.svelte +75 -94
- package/dist/RadioGroup/RadioGroup.svelte +54 -99
- package/dist/RadioGroup/RadioGroup.svelte.d.ts +1 -1
- package/dist/Select/Select.svelte +112 -269
- package/dist/Select/Select.svelte.d.ts +1 -1
- package/dist/SelectMenu/SelectMenu.svelte +211 -409
- package/dist/SelectMenu/SelectMenu.svelte.d.ts +1 -1
- package/dist/SelectMenu/SelectMenuFormFieldTestWrapper.svelte +3 -6
- package/dist/Separator/Separator.svelte +29 -44
- package/dist/Skeleton/Skeleton.svelte +11 -23
- package/dist/Slideover/Slideover.svelte +52 -106
- package/dist/Slideover/SlideoverTriggerTestWrapper.svelte +1 -2
- package/dist/Slider/Slider.svelte +48 -84
- package/dist/Slider/Slider.svelte.d.ts +1 -1
- package/dist/Stepper/Stepper.svelte +139 -132
- package/dist/Stepper/Stepper.svelte.d.ts +1 -1
- package/dist/Switch/Switch.svelte +62 -98
- package/dist/Table/Table.svelte +232 -283
- package/dist/Table/table.variants.d.ts +1 -1
- package/dist/Tabs/Tabs.svelte +96 -129
- package/dist/Tabs/Tabs.svelte.d.ts +1 -1
- package/dist/Textarea/Textarea.svelte +90 -173
- package/dist/Textarea/Textarea.svelte.d.ts +1 -1
- package/dist/ThemeModeButton/ThemeModeButton.svelte +16 -38
- package/dist/Timeline/Timeline.svelte +75 -54
- package/dist/Toast/Toaster.svelte +8 -25
- package/dist/Tooltip/Tooltip.svelte +34 -66
- package/dist/Tooltip/Tooltip.svelte.d.ts +1 -1
- package/dist/Tooltip/TooltipTestWrapper.svelte +2 -5
- package/dist/User/User.svelte +33 -49
- package/dist/docs/navigation.d.ts +1 -1
- package/dist/docs/navigation.js +8 -1
- package/dist/hooks/HookContextProbe.svelte +2 -4
- package/dist/hooks/HookContextProvider.svelte +8 -6
- package/dist/hooks/HookEmitProbe.svelte +8 -11
- package/dist/i18n.d.ts +2 -0
- package/dist/i18n.js +19 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/mcp/svelora-docs.data.json +4 -2
- package/dist/theme.css +1 -1
- package/package.json +16 -8
|
@@ -1,325 +1,247 @@
|
|
|
1
|
-
<script lang="ts" module>
|
|
2
|
-
import type { FileUploadProps, FileUploadRejection } from './file-upload.types.js'
|
|
3
|
-
|
|
4
|
-
export type Props = FileUploadProps
|
|
1
|
+
<script lang="ts" module>export {};
|
|
5
2
|
</script>
|
|
6
3
|
|
|
7
|
-
<script lang="ts">
|
|
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
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
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
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
export function clearAll() {
|
|
249
|
-
previewOpen = false
|
|
250
|
-
previewFile = null
|
|
251
|
-
for (const [, url] of urlCache) URL.revokeObjectURL(url)
|
|
252
|
-
urlCache.clear()
|
|
253
|
-
value = []
|
|
254
|
-
onValueChange?.(value)
|
|
255
|
-
emit.onChange()
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
function handleInputChange(e: Event) {
|
|
259
|
-
const input = e.target as HTMLInputElement
|
|
260
|
-
if (input.files?.length) {
|
|
261
|
-
addFiles(Array.from(input.files))
|
|
262
|
-
input.value = ''
|
|
263
|
-
}
|
|
264
|
-
}
|
|
265
|
-
|
|
266
|
-
function handleDrop(e: DragEvent) {
|
|
267
|
-
e.preventDefault()
|
|
268
|
-
dragCounter = 0
|
|
269
|
-
if (!dropzone || isDisabled) return
|
|
270
|
-
const files = e.dataTransfer?.files
|
|
271
|
-
if (files?.length) addFiles(Array.from(files))
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
function handleDragEnter(e: DragEvent) {
|
|
275
|
-
e.preventDefault()
|
|
276
|
-
if (dropzone && !isDisabled) dragCounter++
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
function handleDragOver(e: DragEvent) {
|
|
280
|
-
e.preventDefault()
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
function handleDragLeave() {
|
|
284
|
-
dragCounter = Math.max(0, dragCounter - 1)
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
function handleAreaClick() {
|
|
288
|
-
if (interactive && !isDisabled) open()
|
|
289
|
-
}
|
|
290
|
-
|
|
291
|
-
function handleKeydown(e: KeyboardEvent) {
|
|
292
|
-
if (!interactive) return
|
|
293
|
-
if (e.key === 'Enter' || e.key === ' ') {
|
|
294
|
-
e.preventDefault()
|
|
295
|
-
open()
|
|
296
|
-
}
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
function formatFileSize(bytes: number): string {
|
|
300
|
-
if (bytes === 0) return '0 B'
|
|
301
|
-
const k = 1024
|
|
302
|
-
const sizes = ['B', 'KB', 'MB', 'GB']
|
|
303
|
-
const i = Math.floor(Math.log(bytes) / Math.log(k))
|
|
304
|
-
return `${(bytes / k ** i).toFixed(1)} ${sizes[i]}`
|
|
305
|
-
}
|
|
306
|
-
|
|
307
|
-
function isImageFile(file: File): boolean {
|
|
308
|
-
return file.type.startsWith('image/')
|
|
309
|
-
}
|
|
310
|
-
|
|
311
|
-
function openPreview(file: File) {
|
|
312
|
-
previewFile = file
|
|
313
|
-
previewOpen = true
|
|
314
|
-
}
|
|
315
|
-
|
|
316
|
-
function getObjectUrl(file: File): string {
|
|
317
|
-
const key = fileKey(file)
|
|
318
|
-
if (!urlCache.has(key)) {
|
|
319
|
-
urlCache.set(key, URL.createObjectURL(file))
|
|
320
|
-
}
|
|
321
|
-
return urlCache.get(key)!
|
|
322
|
-
}
|
|
4
|
+
<script lang="ts">import { useId } from "bits-ui";
|
|
5
|
+
import { untrack } from "svelte";
|
|
6
|
+
import Button from "../Button/Button.svelte";
|
|
7
|
+
import { getComponentConfig, iconsDefaults } from "../config.js";
|
|
8
|
+
import { useFormField, useFormFieldEmit } from "../hooks/useFormField.svelte.js";
|
|
9
|
+
import Icon from "../Icon/Icon.svelte";
|
|
10
|
+
import Modal from "../Modal/Modal.svelte";
|
|
11
|
+
import { fileUploadDefaults, fileUploadVariants } from "./file-upload.variants.js";
|
|
12
|
+
const config = getComponentConfig("fileUpload", fileUploadDefaults);
|
|
13
|
+
const icons = getComponentConfig("icons", iconsDefaults);
|
|
14
|
+
let { ref = $bindable(null), value = $bindable([]), onValueChange, multiple = false, accept, maxSize, maxFiles, onReject, dropzone = true, interactive = true, label = "Drop files here or click to upload", description, icon = icons.upload, color = config.defaultVariants.color, size = config.defaultVariants.size, variant = config.defaultVariants.variant, layout = config.defaultVariants.layout, disabled = false, loading = false, loadingIcon = icons.loading, preview = true, highlight = false, required = false, fileIcon = icons.file, imagePreview = true, id, name, leadingSlot, labelSlot, descriptionSlot, actionsSlot, filesSlot, fileSlot, children, class: className, ui, ...restProps } = $props();
|
|
15
|
+
const autoId = useId();
|
|
16
|
+
let inputRef = $state(null);
|
|
17
|
+
let dragCounter = $state(0);
|
|
18
|
+
let previewOpen = $state(false);
|
|
19
|
+
let previewFile = $state(null);
|
|
20
|
+
const formFieldContext = useFormField();
|
|
21
|
+
const emit = useFormFieldEmit();
|
|
22
|
+
// Stable file identity key with separator to avoid collisions
|
|
23
|
+
const fileKey = (f) => `${f.name}:${f.size}:${f.lastModified}`;
|
|
24
|
+
// Plain Map intentionally — urlCache is an internal optimization cache, not reactive UI
|
|
25
|
+
// state. SvelteMap mutations would cascade re-renders every time a URL is cached/evicted.
|
|
26
|
+
// eslint-disable-next-line svelte/prefer-svelte-reactivity
|
|
27
|
+
const urlCache = new Map();
|
|
28
|
+
const hasError = $derived(formFieldContext?.error !== undefined && formFieldContext?.error !== false);
|
|
29
|
+
const resolvedHighlight = $derived(highlight || hasError);
|
|
30
|
+
const resolvedId = $derived(id ?? formFieldContext?.ariaId);
|
|
31
|
+
const resolvedName = $derived(name ?? formFieldContext?.name);
|
|
32
|
+
const ariaDescribedBy = $derived(!formFieldContext ? undefined : hasError ? `${formFieldContext.ariaId}-error` : `${formFieldContext.ariaId}-description ${formFieldContext.ariaId}-help`);
|
|
33
|
+
const isDisabled = $derived(disabled || loading);
|
|
34
|
+
const isDragging = $derived(dragCounter > 0);
|
|
35
|
+
const isFull = $derived(maxFiles !== undefined && value.length >= maxFiles);
|
|
36
|
+
const showFilesInside = $derived(layout === "grid" && !multiple && value.length > 0 && preview && variant === "area");
|
|
37
|
+
// Pass booleans directly so compound variants with `false` values match correctly
|
|
38
|
+
const slots = $derived(fileUploadVariants({
|
|
39
|
+
color: hasError ? "error" : color,
|
|
40
|
+
size,
|
|
41
|
+
variant,
|
|
42
|
+
layout,
|
|
43
|
+
dropzone,
|
|
44
|
+
interactive: interactive && !isDisabled,
|
|
45
|
+
highlight: resolvedHighlight,
|
|
46
|
+
multiple,
|
|
47
|
+
disabled: isDisabled
|
|
48
|
+
}));
|
|
49
|
+
const classes = $derived.by(() => {
|
|
50
|
+
const u = ui ?? {};
|
|
51
|
+
return {
|
|
52
|
+
root: slots.root({ class: [
|
|
53
|
+
config.slots.root,
|
|
54
|
+
className,
|
|
55
|
+
u.root
|
|
56
|
+
] }),
|
|
57
|
+
base: slots.base({ class: [config.slots.base, u.base] }),
|
|
58
|
+
wrapper: slots.wrapper({ class: [config.slots.wrapper, u.wrapper] }),
|
|
59
|
+
icon: slots.icon({ class: [config.slots.icon, u.icon] }),
|
|
60
|
+
label: slots.label({ class: [config.slots.label, u.label] }),
|
|
61
|
+
description: slots.description({ class: [config.slots.description, u.description] }),
|
|
62
|
+
actions: slots.actions({ class: [config.slots.actions, u.actions] }),
|
|
63
|
+
files: slots.files({ class: [config.slots.files, u.files] }),
|
|
64
|
+
file: slots.file({ class: [config.slots.file, u.file] }),
|
|
65
|
+
fileLeading: slots.fileLeading({ class: [config.slots.fileLeading, u.fileLeading] }),
|
|
66
|
+
fileWrapper: slots.fileWrapper({ class: [config.slots.fileWrapper, u.fileWrapper] }),
|
|
67
|
+
fileName: slots.fileName({ class: [config.slots.fileName, u.fileName] }),
|
|
68
|
+
fileSize: slots.fileSize({ class: [config.slots.fileSize, u.fileSize] }),
|
|
69
|
+
fileTrailing: slots.fileTrailing({ class: [config.slots.fileTrailing, u.fileTrailing] }),
|
|
70
|
+
previewContent: slots.previewContent({ class: [config.slots.previewContent, u.previewContent] }),
|
|
71
|
+
previewBody: slots.previewBody({ class: [config.slots.previewBody, u.previewBody] })
|
|
72
|
+
};
|
|
73
|
+
});
|
|
74
|
+
// Revoke blob URLs for files no longer in value (handles external bind:value resets)
|
|
75
|
+
$effect(() => {
|
|
76
|
+
const current = new Set(value.map(fileKey));
|
|
77
|
+
// Close preview if its file was removed externally — untrack to avoid subscribing to previewFile
|
|
78
|
+
const pf = untrack(() => previewFile);
|
|
79
|
+
if (pf && !current.has(fileKey(pf))) {
|
|
80
|
+
previewOpen = false;
|
|
81
|
+
previewFile = null;
|
|
82
|
+
}
|
|
83
|
+
for (const [key, url] of urlCache) {
|
|
84
|
+
if (!current.has(key)) {
|
|
85
|
+
URL.revokeObjectURL(url);
|
|
86
|
+
urlCache.delete(key);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
});
|
|
90
|
+
$effect(() => {
|
|
91
|
+
return () => {
|
|
92
|
+
for (const [, url] of urlCache) URL.revokeObjectURL(url);
|
|
93
|
+
urlCache.clear();
|
|
94
|
+
};
|
|
95
|
+
});
|
|
96
|
+
export function open() {
|
|
97
|
+
if (isDisabled) return;
|
|
98
|
+
inputRef?.click();
|
|
99
|
+
}
|
|
100
|
+
function isFileAccepted(file) {
|
|
101
|
+
if (!accept) return true;
|
|
102
|
+
return accept.split(",").map((s) => s.trim()).some((token) => {
|
|
103
|
+
if (!token || token === "*") return true;
|
|
104
|
+
if (token.startsWith(".")) return file.name.toLowerCase().endsWith(token.toLowerCase());
|
|
105
|
+
if (token.endsWith("/*")) return file.type.startsWith(token.slice(0, -1));
|
|
106
|
+
return file.type === token;
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
function validateIngress(file) {
|
|
110
|
+
if (accept && !isFileAccepted(file)) return "accept";
|
|
111
|
+
if (maxSize !== undefined && file.size > maxSize) return "maxSize";
|
|
112
|
+
return null;
|
|
113
|
+
}
|
|
114
|
+
function applyMaxFiles(candidates) {
|
|
115
|
+
if (maxFiles === undefined) return {
|
|
116
|
+
accepted: candidates,
|
|
117
|
+
rejected: []
|
|
118
|
+
};
|
|
119
|
+
const remaining = Math.max(0, maxFiles - value.length);
|
|
120
|
+
if (candidates.length <= remaining) return {
|
|
121
|
+
accepted: candidates,
|
|
122
|
+
rejected: []
|
|
123
|
+
};
|
|
124
|
+
return {
|
|
125
|
+
accepted: candidates.slice(0, remaining),
|
|
126
|
+
rejected: candidates.slice(remaining).map((file) => ({
|
|
127
|
+
file,
|
|
128
|
+
reason: "maxFiles"
|
|
129
|
+
}))
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
function addFiles(newFiles) {
|
|
133
|
+
if (isDisabled) return;
|
|
134
|
+
const rejected = [];
|
|
135
|
+
const passed = [];
|
|
136
|
+
for (const file of newFiles) {
|
|
137
|
+
const reason = validateIngress(file);
|
|
138
|
+
if (reason) rejected.push({
|
|
139
|
+
file,
|
|
140
|
+
reason
|
|
141
|
+
});
|
|
142
|
+
else passed.push(file);
|
|
143
|
+
}
|
|
144
|
+
let accepted;
|
|
145
|
+
if (!multiple) {
|
|
146
|
+
accepted = passed.slice(0, 1);
|
|
147
|
+
} else {
|
|
148
|
+
const existing = new Set(value.map(fileKey));
|
|
149
|
+
const deduped = passed.filter((f) => !existing.has(fileKey(f)));
|
|
150
|
+
const result = applyMaxFiles(deduped);
|
|
151
|
+
accepted = result.accepted;
|
|
152
|
+
rejected.push(...result.rejected);
|
|
153
|
+
}
|
|
154
|
+
if (accepted.length) {
|
|
155
|
+
value = multiple ? [...value, ...accepted] : accepted;
|
|
156
|
+
onValueChange?.(value);
|
|
157
|
+
emit.onChange();
|
|
158
|
+
}
|
|
159
|
+
if (rejected.length) onReject?.(rejected);
|
|
160
|
+
}
|
|
161
|
+
function removeFile(index) {
|
|
162
|
+
const removed = value[index];
|
|
163
|
+
const key = fileKey(removed);
|
|
164
|
+
if (previewFile && fileKey(previewFile) === key) {
|
|
165
|
+
previewOpen = false;
|
|
166
|
+
previewFile = null;
|
|
167
|
+
}
|
|
168
|
+
if (urlCache.has(key)) {
|
|
169
|
+
URL.revokeObjectURL(urlCache.get(key));
|
|
170
|
+
urlCache.delete(key);
|
|
171
|
+
}
|
|
172
|
+
value = value.filter((_, i) => i !== index);
|
|
173
|
+
onValueChange?.(value);
|
|
174
|
+
emit.onChange();
|
|
175
|
+
}
|
|
176
|
+
export function clearAll() {
|
|
177
|
+
previewOpen = false;
|
|
178
|
+
previewFile = null;
|
|
179
|
+
for (const [, url] of urlCache) URL.revokeObjectURL(url);
|
|
180
|
+
urlCache.clear();
|
|
181
|
+
value = [];
|
|
182
|
+
onValueChange?.(value);
|
|
183
|
+
emit.onChange();
|
|
184
|
+
}
|
|
185
|
+
function handleInputChange(e) {
|
|
186
|
+
const input = e.target;
|
|
187
|
+
if (input.files?.length) {
|
|
188
|
+
addFiles(Array.from(input.files));
|
|
189
|
+
input.value = "";
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
function handleDrop(e) {
|
|
193
|
+
e.preventDefault();
|
|
194
|
+
dragCounter = 0;
|
|
195
|
+
if (!dropzone || isDisabled) return;
|
|
196
|
+
const files = e.dataTransfer?.files;
|
|
197
|
+
if (files?.length) addFiles(Array.from(files));
|
|
198
|
+
}
|
|
199
|
+
function handleDragEnter(e) {
|
|
200
|
+
e.preventDefault();
|
|
201
|
+
if (dropzone && !isDisabled) dragCounter++;
|
|
202
|
+
}
|
|
203
|
+
function handleDragOver(e) {
|
|
204
|
+
e.preventDefault();
|
|
205
|
+
}
|
|
206
|
+
function handleDragLeave() {
|
|
207
|
+
dragCounter = Math.max(0, dragCounter - 1);
|
|
208
|
+
}
|
|
209
|
+
function handleAreaClick() {
|
|
210
|
+
if (interactive && !isDisabled) open();
|
|
211
|
+
}
|
|
212
|
+
function handleKeydown(e) {
|
|
213
|
+
if (!interactive) return;
|
|
214
|
+
if (e.key === "Enter" || e.key === " ") {
|
|
215
|
+
e.preventDefault();
|
|
216
|
+
open();
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
function formatFileSize(bytes) {
|
|
220
|
+
if (bytes === 0) return "0 B";
|
|
221
|
+
const k = 1024;
|
|
222
|
+
const sizes = [
|
|
223
|
+
"B",
|
|
224
|
+
"KB",
|
|
225
|
+
"MB",
|
|
226
|
+
"GB"
|
|
227
|
+
];
|
|
228
|
+
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
|
229
|
+
return `${(bytes / k ** i).toFixed(1)} ${sizes[i]}`;
|
|
230
|
+
}
|
|
231
|
+
function isImageFile(file) {
|
|
232
|
+
return file.type.startsWith("image/");
|
|
233
|
+
}
|
|
234
|
+
function openPreview(file) {
|
|
235
|
+
previewFile = file;
|
|
236
|
+
previewOpen = true;
|
|
237
|
+
}
|
|
238
|
+
function getObjectUrl(file) {
|
|
239
|
+
const key = fileKey(file);
|
|
240
|
+
if (!urlCache.has(key)) {
|
|
241
|
+
urlCache.set(key, URL.createObjectURL(file));
|
|
242
|
+
}
|
|
243
|
+
return urlCache.get(key);
|
|
244
|
+
}
|
|
323
245
|
</script>
|
|
324
246
|
|
|
325
247
|
<div {...restProps} bind:this={ref} class={classes.root} data-full={isFull ? '' : undefined}>
|
|
@@ -3,6 +3,6 @@ export type Props = FileUploadProps;
|
|
|
3
3
|
declare const FileUpload: import("svelte").Component<FileUploadProps, {
|
|
4
4
|
open: () => void;
|
|
5
5
|
clearAll: () => void;
|
|
6
|
-
}, "
|
|
6
|
+
}, "ref" | "value">;
|
|
7
7
|
type FileUpload = ReturnType<typeof FileUpload>;
|
|
8
8
|
export default FileUpload;
|
package/dist/Fonts/Fonts.svelte
CHANGED
|
@@ -1,40 +1,19 @@
|
|
|
1
|
-
<script lang="ts" module>
|
|
2
|
-
import type { FontsProps } from './fonts.types.js'
|
|
3
|
-
|
|
4
|
-
export type Props = FontsProps
|
|
1
|
+
<script lang="ts" module>export {};
|
|
5
2
|
</script>
|
|
6
3
|
|
|
7
|
-
<script lang="ts">
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
let {
|
|
21
|
-
fonts,
|
|
22
|
-
families,
|
|
23
|
-
display,
|
|
24
|
-
preconnect
|
|
25
|
-
}: Props = $props()
|
|
26
|
-
|
|
27
|
-
const resolvedFamilies = $derived(families ?? fonts ?? (config === false ? [] : config.families ?? []))
|
|
28
|
-
const resolvedDisplay = $derived(display ?? (config === false ? fontsDefaults.display : config.display))
|
|
29
|
-
const resolvedPreconnect = $derived(
|
|
30
|
-
preconnect ?? (config === false ? fontsDefaults.preconnect : config.preconnect)
|
|
31
|
-
)
|
|
32
|
-
|
|
33
|
-
const href = $derived(buildGoogleFontsUrl(resolvedFamilies, resolvedDisplay))
|
|
34
|
-
const localFontFaceCss = $derived(buildLocalFontFaceCss(resolvedFamilies, resolvedDisplay))
|
|
35
|
-
const variableCss = $derived(buildFontVariablesCss(resolvedFamilies))
|
|
36
|
-
const inlineCss = $derived([localFontFaceCss, variableCss].filter((value) => value.length > 0).join('\n\n'))
|
|
37
|
-
const hasFonts = $derived(href.length > 0 || inlineCss.length > 0)
|
|
4
|
+
<script lang="ts">import { getConfig } from "../config.js";
|
|
5
|
+
import { buildFontVariablesCss, buildGoogleFontsUrl, buildLocalFontFaceCss, fontsDefaults, resolveFontsOptions } from "./fonts.js";
|
|
6
|
+
const globalConfig = getConfig();
|
|
7
|
+
const config = resolveFontsOptions(globalConfig.fonts);
|
|
8
|
+
let { fonts, families, display, preconnect } = $props();
|
|
9
|
+
const resolvedFamilies = $derived(families ?? fonts ?? (config === false ? [] : config.families ?? []));
|
|
10
|
+
const resolvedDisplay = $derived(display ?? (config === false ? fontsDefaults.display : config.display));
|
|
11
|
+
const resolvedPreconnect = $derived(preconnect ?? (config === false ? fontsDefaults.preconnect : config.preconnect));
|
|
12
|
+
const href = $derived(buildGoogleFontsUrl(resolvedFamilies, resolvedDisplay));
|
|
13
|
+
const localFontFaceCss = $derived(buildLocalFontFaceCss(resolvedFamilies, resolvedDisplay));
|
|
14
|
+
const variableCss = $derived(buildFontVariablesCss(resolvedFamilies));
|
|
15
|
+
const inlineCss = $derived([localFontFaceCss, variableCss].filter((value) => value.length > 0).join("\n\n"));
|
|
16
|
+
const hasFonts = $derived(href.length > 0 || inlineCss.length > 0);
|
|
38
17
|
</script>
|
|
39
18
|
|
|
40
19
|
<svelte:head>
|
|
@@ -47,8 +26,7 @@
|
|
|
47
26
|
<link rel="stylesheet" href={href} />
|
|
48
27
|
{/if}
|
|
49
28
|
{#if inlineCss}
|
|
50
|
-
|
|
51
|
-
{@html `<style>${inlineCss}</style>`}
|
|
29
|
+
<svelte:element this={'style'}>{inlineCss}</svelte:element>
|
|
52
30
|
{/if}
|
|
53
31
|
{/if}
|
|
54
32
|
</svelte:head>
|