@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.
- package/dist/components/Icon.svelte +16 -16
- package/dist/components/content/Markdown.svelte +10 -10
- package/dist/components/form/AntiBot.svelte +12 -12
- package/dist/components/form/Dropzone.svelte +180 -180
- package/dist/components/form/SmartForm.svelte +251 -251
- package/dist/components/integrations/EasyTools.svelte +30 -30
- package/dist/components/layouts/DefaultLayout.svelte +110 -110
- package/dist/components/layouts/FlexBox.svelte +73 -73
- package/dist/components/layouts/MenuLayout.svelte +60 -60
- package/dist/components/layouts/Meta.svelte +28 -28
- package/dist/components/layouts/StyleBox.svelte +17 -17
- package/dist/components/layouts/Tracker.svelte +18 -18
- package/dist/components/layouts/Well.svelte +20 -20
- package/dist/components/presentation/ImageCompare.svelte +180 -180
- package/dist/components/presentation/Logo.svelte +37 -37
- package/package.json +75 -75
|
@@ -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>
|