@functionalcms/svelte-components 5.0.0-beta2 → 5.0.0-beta3

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.
@@ -1,16 +1,16 @@
1
- <script lang="ts">
2
- import * as FlowbiteIcons from "flowbite-svelte-icons"
3
- import { IconSize } from "./icons.js";
4
-
5
- interface Props {
6
- icon?: string;
7
- size?: IconSize;
8
- class?: string;
9
- }
10
- let { icon, size = IconSize.Medium, class: className = '' } : Props = $props();
11
- </script>
12
-
13
- {#if icon != undefined}
14
- {@const Component = FlowbiteIcons[icon]}
15
- <Component class={`${className} ${size}`} />
16
- {/if}
1
+ <script lang="ts">
2
+ import * as FlowbiteIcons from "flowbite-svelte-icons"
3
+ import { IconSize } from "./icons.js";
4
+
5
+ interface Props {
6
+ icon?: string;
7
+ size?: IconSize;
8
+ class?: string;
9
+ }
10
+ let { icon, size = IconSize.Medium, class: className = '' } : Props = $props();
11
+ </script>
12
+
13
+ {#if icon != undefined}
14
+ {@const Component = FlowbiteIcons[icon]}
15
+ <Component class={`${className} ${size}`} />
16
+ {/if}
@@ -1,10 +1,10 @@
1
- <!-- <script lang="ts">
2
- import { marked } from 'marked';
3
-
4
- /** @type {{ data: import('./$types').PageData }} */
5
- let { source = $bindable() } = $props();
6
-
7
- const code = $derived(marked.parse(source));
8
- </script>
9
-
10
- {@html code} -->
1
+ <!-- <script lang="ts">
2
+ import { marked } from 'marked';
3
+
4
+ /** @type {{ data: import('./$types').PageData }} */
5
+ let { source = $bindable() } = $props();
6
+
7
+ const code = $derived(marked.parse(source));
8
+ </script>
9
+
10
+ {@html code} -->
@@ -1,12 +1,12 @@
1
- <div>
2
- <label for="human-detection">Please do not check it</label>
3
- <input id="human-detection" name="human-detection" />
4
- </div>
5
-
6
- <style>
7
- div,
8
- label,
9
- input {
10
- display: none !important;
11
- }
12
- </style>
1
+ <div>
2
+ <label for="human-detection">Please do not check it</label>
3
+ <input id="human-detection" name="human-detection" />
4
+ </div>
5
+
6
+ <style>
7
+ div,
8
+ label,
9
+ input {
10
+ display: none !important;
11
+ }
12
+ </style>
@@ -1,180 +1,180 @@
1
- <script lang="ts">
2
- import { displaySize, type FileDropZoneProps, type FileRejectedReason } from './dropzone.js';
3
-
4
- let {
5
- id,
6
- children,
7
- maxFiles,
8
- maxFileSize,
9
- fileCount,
10
- disabled = false,
11
- onUpload,
12
- onFileRejected,
13
- accept,
14
- class: css,
15
- ...rest
16
- }: FileDropZoneProps = $props();
17
-
18
-
19
- if (maxFiles !== undefined && fileCount === undefined) {
20
- console.warn(
21
- 'Make sure to provide FileDropZone with `fileCount` when using the `maxFiles` prompt'
22
- );
23
- }
24
-
25
- let uploading = $state(false);
26
-
27
- const drop = async (
28
- e: DragEvent & {
29
- currentTarget: EventTarget & HTMLLabelElement;
30
- }
31
- ) => {
32
- if (disabled || !canUploadFiles) return;
33
-
34
- e.preventDefault();
35
-
36
- const droppedFiles = Array.from(e.dataTransfer?.files ?? []);
37
-
38
- await upload(droppedFiles);
39
- };
40
-
41
- const change = async (
42
- e: Event & {
43
- currentTarget: EventTarget & HTMLInputElement;
44
- }
45
- ) => {
46
- if (disabled) return;
47
-
48
- const selectedFiles = e.currentTarget.files;
49
-
50
- if (!selectedFiles) return;
51
-
52
- await upload(Array.from(selectedFiles));
53
-
54
- // this if a file fails and we upload the same file again we still get feedback
55
- (e.target as HTMLInputElement).value = '';
56
- };
57
-
58
- const shouldAcceptFile = (file: File, fileNumber: number): FileRejectedReason | undefined => {
59
- if (maxFileSize !== undefined && file.size > maxFileSize) return 'Maximum file size exceeded';
60
-
61
- if (maxFiles !== undefined && fileNumber > maxFiles) return 'Maximum files uploaded';
62
-
63
- if (!accept) return undefined;
64
-
65
- const acceptedTypes = accept.split(',').map((a) => a.trim().toLowerCase());
66
- const fileType = file.type.toLowerCase();
67
- const fileName = file.name.toLowerCase();
68
-
69
- const isAcceptable = acceptedTypes.some((pattern) => {
70
- // check extension like .mp4
71
- if (fileType.startsWith('.')) {
72
- return fileName.endsWith(pattern);
73
- }
74
-
75
- // if pattern has wild card like video/*
76
- if (pattern.endsWith('/*')) {
77
- const baseType = pattern.slice(0, pattern.indexOf('/*'));
78
- return fileType.startsWith(baseType + '/');
79
- }
80
-
81
- // otherwise it must be a specific type like video/mp4
82
- return fileType === pattern;
83
- });
84
-
85
- if (!isAcceptable) return 'File type not allowed';
86
-
87
- return undefined;
88
- };
89
-
90
- const upload = async (uploadFiles: File[]) => {
91
- uploading = true;
92
-
93
- const validFiles: File[] = [];
94
-
95
- for (let i = 0; i < uploadFiles.length; i++) {
96
- const file = uploadFiles[i];
97
-
98
- const rejectedReason = shouldAcceptFile(file, (fileCount ?? 0) + i + 1);
99
-
100
- if (rejectedReason) {
101
- onFileRejected?.({ file, reason: rejectedReason });
102
- continue;
103
- }
104
-
105
- validFiles.push(file);
106
- }
107
-
108
- await onUpload(validFiles);
109
-
110
- uploading = false;
111
- };
112
-
113
- const canUploadFiles = $derived(
114
- !disabled &&
115
- !uploading &&
116
- !(maxFiles !== undefined && fileCount !== undefined && fileCount >= maxFiles)
117
- );
118
- </script>
119
-
120
- <label
121
- ondragover={(e) => e.preventDefault()}
122
- ondrop={drop}
123
- for={id}
124
- aria-disabled={!canUploadFiles}
125
- class=''
126
- >
127
- {#if children}
128
- {@render children()}
129
- {:else}
130
- <div class="flex flex-col place-items-center justify-center gap-2">
131
- <div
132
- class="flex size-14 place-items-center justify-center rounded-full border border-dashed border-border text-muted-foreground"
133
- >
134
- <svg
135
- xmlns="http://www.w3.org/2000/svg"
136
- width="24"
137
- height="24"
138
- viewBox="0 0 24 24"
139
- fill="none"
140
- stroke="currentColor"
141
- stroke-width="2"
142
- stroke-linecap="round"
143
- stroke-linejoin="round"
144
- class="lucide lucide-upload-icon lucide-upload"
145
- ><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4" /><polyline
146
- points="17 8 12 3 7 8"
147
- /><line x1="12" x2="12" y1="3" y2="15" /></svg
148
- >
149
- </div>
150
- <div class="flex flex-col gap-0.5 text-center">
151
- <span class="font-medium text-muted-foreground">
152
- Drag 'n' drop files here, or click to select files
153
- </span>
154
- {#if maxFiles || maxFileSize}
155
- <span class="text-sm text-muted-foreground/75">
156
- {#if maxFiles}
157
- <span>You can upload {maxFiles} files</span>
158
- {/if}
159
- {#if maxFiles && maxFileSize}
160
- <span>(up to {displaySize(maxFileSize)} each)</span>
161
- {/if}
162
- {#if maxFileSize && !maxFiles}
163
- <span>Maximum size {displaySize(maxFileSize)}</span>
164
- {/if}
165
- </span>
166
- {/if}
167
- </div>
168
- </div>
169
- {/if}
170
- <input
171
- {...rest}
172
- disabled={!canUploadFiles}
173
- {id}
174
- {accept}
175
- multiple={maxFiles === undefined || maxFiles - (fileCount ?? 0) > 1}
176
- type="file"
177
- onchange={change}
178
- class="hidden"
179
- />
180
- </label>
1
+ <script lang="ts">
2
+ import { displaySize, type FileDropZoneProps, type FileRejectedReason } from './dropzone.js';
3
+
4
+ let {
5
+ id,
6
+ children,
7
+ maxFiles,
8
+ maxFileSize,
9
+ fileCount,
10
+ disabled = false,
11
+ onUpload,
12
+ onFileRejected,
13
+ accept,
14
+ class: css,
15
+ ...rest
16
+ }: FileDropZoneProps = $props();
17
+
18
+
19
+ if (maxFiles !== undefined && fileCount === undefined) {
20
+ console.warn(
21
+ 'Make sure to provide FileDropZone with `fileCount` when using the `maxFiles` prompt'
22
+ );
23
+ }
24
+
25
+ let uploading = $state(false);
26
+
27
+ const drop = async (
28
+ e: DragEvent & {
29
+ currentTarget: EventTarget & HTMLLabelElement;
30
+ }
31
+ ) => {
32
+ if (disabled || !canUploadFiles) return;
33
+
34
+ e.preventDefault();
35
+
36
+ const droppedFiles = Array.from(e.dataTransfer?.files ?? []);
37
+
38
+ await upload(droppedFiles);
39
+ };
40
+
41
+ const change = async (
42
+ e: Event & {
43
+ currentTarget: EventTarget & HTMLInputElement;
44
+ }
45
+ ) => {
46
+ if (disabled) return;
47
+
48
+ const selectedFiles = e.currentTarget.files;
49
+
50
+ if (!selectedFiles) return;
51
+
52
+ await upload(Array.from(selectedFiles));
53
+
54
+ // this if a file fails and we upload the same file again we still get feedback
55
+ (e.target as HTMLInputElement).value = '';
56
+ };
57
+
58
+ const shouldAcceptFile = (file: File, fileNumber: number): FileRejectedReason | undefined => {
59
+ if (maxFileSize !== undefined && file.size > maxFileSize) return 'Maximum file size exceeded';
60
+
61
+ if (maxFiles !== undefined && fileNumber > maxFiles) return 'Maximum files uploaded';
62
+
63
+ if (!accept) return undefined;
64
+
65
+ const acceptedTypes = accept.split(',').map((a) => a.trim().toLowerCase());
66
+ const fileType = file.type.toLowerCase();
67
+ const fileName = file.name.toLowerCase();
68
+
69
+ const isAcceptable = acceptedTypes.some((pattern) => {
70
+ // check extension like .mp4
71
+ if (fileType.startsWith('.')) {
72
+ return fileName.endsWith(pattern);
73
+ }
74
+
75
+ // if pattern has wild card like video/*
76
+ if (pattern.endsWith('/*')) {
77
+ const baseType = pattern.slice(0, pattern.indexOf('/*'));
78
+ return fileType.startsWith(baseType + '/');
79
+ }
80
+
81
+ // otherwise it must be a specific type like video/mp4
82
+ return fileType === pattern;
83
+ });
84
+
85
+ if (!isAcceptable) return 'File type not allowed';
86
+
87
+ return undefined;
88
+ };
89
+
90
+ const upload = async (uploadFiles: File[]) => {
91
+ uploading = true;
92
+
93
+ const validFiles: File[] = [];
94
+
95
+ for (let i = 0; i < uploadFiles.length; i++) {
96
+ const file = uploadFiles[i];
97
+
98
+ const rejectedReason = shouldAcceptFile(file, (fileCount ?? 0) + i + 1);
99
+
100
+ if (rejectedReason) {
101
+ onFileRejected?.({ file, reason: rejectedReason });
102
+ continue;
103
+ }
104
+
105
+ validFiles.push(file);
106
+ }
107
+
108
+ await onUpload(validFiles);
109
+
110
+ uploading = false;
111
+ };
112
+
113
+ const canUploadFiles = $derived(
114
+ !disabled &&
115
+ !uploading &&
116
+ !(maxFiles !== undefined && fileCount !== undefined && fileCount >= maxFiles)
117
+ );
118
+ </script>
119
+
120
+ <label
121
+ ondragover={(e) => e.preventDefault()}
122
+ ondrop={drop}
123
+ for={id}
124
+ aria-disabled={!canUploadFiles}
125
+ class=''
126
+ >
127
+ {#if children}
128
+ {@render children()}
129
+ {:else}
130
+ <div class="flex flex-col place-items-center justify-center gap-2">
131
+ <div
132
+ class="flex size-14 place-items-center justify-center rounded-full border border-dashed border-border text-muted-foreground"
133
+ >
134
+ <svg
135
+ xmlns="http://www.w3.org/2000/svg"
136
+ width="24"
137
+ height="24"
138
+ viewBox="0 0 24 24"
139
+ fill="none"
140
+ stroke="currentColor"
141
+ stroke-width="2"
142
+ stroke-linecap="round"
143
+ stroke-linejoin="round"
144
+ class="lucide lucide-upload-icon lucide-upload"
145
+ ><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4" /><polyline
146
+ points="17 8 12 3 7 8"
147
+ /><line x1="12" x2="12" y1="3" y2="15" /></svg
148
+ >
149
+ </div>
150
+ <div class="flex flex-col gap-0.5 text-center">
151
+ <span class="font-medium text-muted-foreground">
152
+ Drag 'n' drop files here, or click to select files
153
+ </span>
154
+ {#if maxFiles || maxFileSize}
155
+ <span class="text-sm text-muted-foreground/75">
156
+ {#if maxFiles}
157
+ <span>You can upload {maxFiles} files</span>
158
+ {/if}
159
+ {#if maxFiles && maxFileSize}
160
+ <span>(up to {displaySize(maxFileSize)} each)</span>
161
+ {/if}
162
+ {#if maxFileSize && !maxFiles}
163
+ <span>Maximum size {displaySize(maxFileSize)}</span>
164
+ {/if}
165
+ </span>
166
+ {/if}
167
+ </div>
168
+ </div>
169
+ {/if}
170
+ <input
171
+ {...rest}
172
+ disabled={!canUploadFiles}
173
+ {id}
174
+ {accept}
175
+ multiple={maxFiles === undefined || maxFiles - (fileCount ?? 0) > 1}
176
+ type="file"
177
+ onchange={change}
178
+ class="hidden"
179
+ />
180
+ </label>