@sierra-95/svelte-scaffold 1.1.5 → 1.2.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/dist/Core/components/Menus/MenuItem/menuItem.svelte +3 -2
- package/dist/Core/components/Menus/MenuItem/menuItem.svelte.d.ts +1 -0
- package/dist/Core/components/Menus/Tabs/main.css +0 -1
- package/dist/Core/components/Menus/Tabs/main.svelte +2 -1
- package/dist/Core/components/Menus/Tabs/main.svelte.d.ts +1 -0
- package/dist/Core/components/others/Previews/Audio/audio.svelte +5 -43
- package/dist/Core/components/others/Previews/Document/documents.svelte +1 -1
- package/dist/Core/components/others/Previews/Image/image.svelte +2 -2
- package/dist/Core/components/others/Previews/Video/video.svelte +4 -6
- package/dist/Core/components/others/Previews/Video/video.svelte.d.ts +0 -2
- package/dist/Modules/Editor/Nodes/Images/images.svelte +0 -4
- package/dist/Modules/FilePicker/{ActionsMenu → Actions}/Actions.svelte +10 -12
- package/dist/Modules/FilePicker/Actions/Actions.svelte.d.ts +3 -0
- package/dist/Modules/FilePicker/{ActionsMenu/delete_media.svelte → Actions/delete.svelte} +8 -14
- package/dist/Modules/FilePicker/Actions/delete.svelte.d.ts +5 -0
- package/dist/Modules/FilePicker/{ActionsMenu/download_media.svelte → Actions/download.svelte} +13 -13
- package/dist/Modules/FilePicker/Actions/download.svelte.d.ts +5 -0
- package/dist/Modules/FilePicker/{ActionsMenu/file_properties.svelte → Actions/properties.svelte} +3 -3
- package/dist/Modules/FilePicker/{ActionsMenu/file_properties.svelte.d.ts → Actions/properties.svelte.d.ts} +3 -3
- package/dist/Modules/FilePicker/Cloud/cloudStore.svelte +42 -104
- package/dist/Modules/FilePicker/Cloud/cloudStore.svelte.d.ts +2 -4
- package/dist/Modules/FilePicker/Cloud/content.svelte +118 -0
- package/dist/Modules/FilePicker/Cloud/content.svelte.d.ts +3 -0
- package/dist/Modules/FilePicker/Cloud/storage.svelte +40 -0
- package/dist/Modules/FilePicker/Cloud/storage.svelte.d.ts +15 -0
- package/dist/Modules/FilePicker/Upload/upload.svelte +55 -0
- package/dist/Modules/FilePicker/{Cloud/previews.svelte.d.ts → Upload/upload.svelte.d.ts} +4 -6
- package/dist/Modules/FilePicker/main.svelte +11 -62
- package/dist/stores/modules/editor.d.ts +0 -2
- package/dist/stores/modules/editor.js +0 -2
- package/dist/stores/modules/fileInput.d.ts +3 -1
- package/dist/stores/modules/fileInput.js +4 -2
- package/package.json +1 -1
- package/dist/Modules/FilePicker/ActionsMenu/Actions.svelte.d.ts +0 -5
- package/dist/Modules/FilePicker/ActionsMenu/delete_media.svelte.d.ts +0 -6
- package/dist/Modules/FilePicker/ActionsMenu/download_media.svelte.d.ts +0 -6
- package/dist/Modules/FilePicker/ActionsMenu/media_storage.svelte +0 -54
- package/dist/Modules/FilePicker/ActionsMenu/media_storage.svelte.d.ts +0 -36
- package/dist/Modules/FilePicker/Cloud/previews.svelte +0 -39
|
@@ -5,7 +5,8 @@
|
|
|
5
5
|
disabled = false,
|
|
6
6
|
iconSize = '20px',
|
|
7
7
|
iconGap = '20px',
|
|
8
|
-
iconBg
|
|
8
|
+
iconBg = 'var(--primary-bg)',
|
|
9
|
+
rounded = '0px',
|
|
9
10
|
children = null,
|
|
10
11
|
...rest
|
|
11
12
|
} = $props();
|
|
@@ -30,7 +31,7 @@
|
|
|
30
31
|
<div
|
|
31
32
|
id="sierra-menu-item"
|
|
32
33
|
role="none"
|
|
33
|
-
style="background-color: {active ? 'var(--menu-item-active)' : ''}"
|
|
34
|
+
style="background-color: {active ? 'var(--menu-item-active)' : ''}; border-radius: {rounded};"
|
|
34
35
|
class="{disabled? 'disabled':''}"
|
|
35
36
|
{...rest}
|
|
36
37
|
onclick={(e: MouseEvent) => {
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
iconSize = '15px',
|
|
11
11
|
boxShadow = false,
|
|
12
12
|
actions,
|
|
13
|
+
padding = '1rem'
|
|
13
14
|
} = $props();
|
|
14
15
|
|
|
15
16
|
onMount(()=>{
|
|
@@ -38,7 +39,7 @@
|
|
|
38
39
|
{@render actions?.()}
|
|
39
40
|
</div>
|
|
40
41
|
|
|
41
|
-
<div class="tab-content">
|
|
42
|
+
<div style="padding: {padding};" class="tab-content">
|
|
42
43
|
{#if snippets?.[active]}
|
|
43
44
|
{@render snippets[active]()}
|
|
44
45
|
{/if}
|
|
@@ -1,56 +1,17 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
-
import {ButtonTimes, fileInputStore, ButtonSelect, removeFileForMedia, toggleSelectForMedia
|
|
2
|
+
import {ButtonTimes, fileInputStore, ButtonSelect, removeFileForMedia, toggleSelectForMedia} from '../../../../../index.js'
|
|
3
3
|
export let media;
|
|
4
4
|
export let buttonTimes = false;
|
|
5
5
|
export let urlsOnly = true;
|
|
6
|
-
|
|
7
|
-
let audioPlaying: Record<string, boolean> = {};
|
|
8
|
-
let audioRefs: Record<string, HTMLAudioElement> = {};
|
|
9
|
-
function togglePlay(id: string) {
|
|
10
|
-
if(!urlsOnly){
|
|
11
|
-
addToast({
|
|
12
|
-
status: 'warning',
|
|
13
|
-
message: 'Audio preview is disabled for local files.'
|
|
14
|
-
});
|
|
15
|
-
return;
|
|
16
|
-
}
|
|
17
|
-
const audio = audioRefs[id];
|
|
18
|
-
if (!audio) return;
|
|
19
|
-
|
|
20
|
-
if (audio.paused) {
|
|
21
|
-
// Pause any other playing audio
|
|
22
|
-
for (const key in audioRefs) {
|
|
23
|
-
if (key !== id && !audioRefs[key].paused) {
|
|
24
|
-
audioRefs[key].pause();
|
|
25
|
-
audioPlaying[key] = false;
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
audio.play();
|
|
29
|
-
audioPlaying[id] = true;
|
|
30
|
-
} else {
|
|
31
|
-
audio.pause();
|
|
32
|
-
audioPlaying[id] = false;
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
function getPreviewUrl(file: File) {
|
|
36
|
-
return URL.createObjectURL(file);
|
|
37
|
-
}
|
|
38
|
-
function uuid() {
|
|
39
|
-
return crypto.randomUUID();
|
|
40
|
-
}
|
|
41
6
|
</script>
|
|
42
7
|
|
|
43
8
|
{#if media.Audio.length === 0}
|
|
44
9
|
<p>No audio files available.</p>
|
|
45
10
|
{:else}
|
|
46
11
|
<div style="display: flex; flex-wrap:wrap; gap: 1rem">
|
|
47
|
-
{#each media.Audio as item
|
|
48
|
-
<div
|
|
49
|
-
<
|
|
50
|
-
<i class="fa {audioPlaying[item.id] ? 'fa-pause' : 'fa-play'}" aria-hidden="true"></i>
|
|
51
|
-
</button>
|
|
52
|
-
<audio hidden bind:this={audioRefs[item.id]} src={urlsOnly ? item.url : getPreviewUrl(item)} controls></audio>
|
|
53
|
-
<h3 style=" margin-left:0.2rem" class="sierra-text-ellipsis">{item.original_name || item.name}</h3>
|
|
12
|
+
{#each media.Audio as item}
|
|
13
|
+
<div title={item.original_name || item.name} style="position: relative; cursor: pointer;" on:click={() => toggleSelectForMedia(item, urlsOnly)} role="none">
|
|
14
|
+
<img style="height: 70px;" src="https://files.michaelmachohi.com/svelte-scaffold/icons/music.icon.png" alt={item.original_name || item.name}/>
|
|
54
15
|
{#if buttonTimes}
|
|
55
16
|
<ButtonTimes vertical="bottom" onclick={(e: Event) => removeFileForMedia(
|
|
56
17
|
$fileInputStore.selectedFiles.indexOf(item), e
|
|
@@ -59,6 +20,7 @@
|
|
|
59
20
|
{#if $fileInputStore.submissions.some(sub => sub.url === item.url || sub.id === item.id)}
|
|
60
21
|
<ButtonSelect />
|
|
61
22
|
{/if}
|
|
23
|
+
<h3 style="width: 100px;" class="sierra-text-ellipsis">{item.original_name || item.name}</h3>
|
|
62
24
|
</div>
|
|
63
25
|
{/each}
|
|
64
26
|
</div>
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
<div on:click={() => toggleSelectForMedia(item, urlsOnly)} role="none" class="sierra-translate" style="position: relative; width: 70px; cursor: pointer;">
|
|
24
24
|
<img src={getIconUrl(item.type || item.mime_type)} alt="Document Icon" style="height: 70px;"/>
|
|
25
25
|
{#if buttonTimes}
|
|
26
|
-
<ButtonTimes
|
|
26
|
+
<ButtonTimes onclick={(e: Event) => removeFileForMedia(
|
|
27
27
|
$fileInputStore.selectedFiles.indexOf(item), e
|
|
28
28
|
)}/>
|
|
29
29
|
{/if}
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
<style>
|
|
27
27
|
#sierra-preview-image img{
|
|
28
28
|
width: 100%;
|
|
29
|
-
height:
|
|
29
|
+
height: 70px;
|
|
30
30
|
object-fit: cover;
|
|
31
31
|
}
|
|
32
32
|
</style>
|
|
@@ -46,7 +46,7 @@
|
|
|
46
46
|
{:else}
|
|
47
47
|
<Generic/>
|
|
48
48
|
{/if}
|
|
49
|
-
<h3
|
|
49
|
+
<h3 class="sierra-text-ellipsis">{item.original_name || item.name}</h3>
|
|
50
50
|
{#if buttonTimes}
|
|
51
51
|
<ButtonTimes vertical="bottom" onclick={(e: Event) => removeFileForMedia(
|
|
52
52
|
$fileInputStore.selectedFiles.indexOf(item), e
|
|
@@ -10,19 +10,17 @@
|
|
|
10
10
|
{:else}
|
|
11
11
|
<div style="display: flex; flex-wrap: wrap; gap: 1rem;">
|
|
12
12
|
{#each media.Videos as item}
|
|
13
|
-
<div on:click={() => toggleSelectForMedia(item, urlsOnly)} role="none" class="sierra-translate" style="position:relative;
|
|
14
|
-
<
|
|
15
|
-
<track kind="captions" />
|
|
16
|
-
</video>
|
|
17
|
-
<h3 style=" margin: 0.5rem" class="sierra-text-ellipsis">{item.original_name || item.name}</h3>
|
|
13
|
+
<div on:click={() => toggleSelectForMedia(item, urlsOnly)} role="none" class="sierra-translate" style="position:relative;">
|
|
14
|
+
<img style="height: 70px;" src="https://files.michaelmachohi.com/svelte-scaffold/icons/videos.icon.png" alt={item.original_name || item.name}/>
|
|
18
15
|
{#if buttonTimes}
|
|
19
|
-
<ButtonTimes
|
|
16
|
+
<ButtonTimes onclick={(e: Event) => removeFileForMedia(
|
|
20
17
|
$fileInputStore.selectedFiles.indexOf(item), e
|
|
21
18
|
)}/>
|
|
22
19
|
{/if}
|
|
23
20
|
{#if $fileInputStore.submissions.some(sub => sub.url === item.url || sub.id === item.id)}
|
|
24
21
|
<ButtonSelect />
|
|
25
22
|
{/if}
|
|
23
|
+
<h3 class="sierra-text-wrap">{item.original_name || item.name}</h3>
|
|
26
24
|
</div>
|
|
27
25
|
{/each}
|
|
28
26
|
</div>
|
|
@@ -9,11 +9,9 @@
|
|
|
9
9
|
let openDropdown = false;
|
|
10
10
|
function handleInsertImage(){
|
|
11
11
|
if(
|
|
12
|
-
!$editorStore.r2_key ||
|
|
13
12
|
!$editorStore.serverGetUrl ||
|
|
14
13
|
!$editorStore.serverUploadUrl ||
|
|
15
14
|
!$editorStore.serverDeleteUrl ||
|
|
16
|
-
!$editorStore.serverStorageUrl ||
|
|
17
15
|
!$editorStore.serverDownloadUrl
|
|
18
16
|
){
|
|
19
17
|
addToast({
|
|
@@ -27,11 +25,9 @@
|
|
|
27
25
|
...store,
|
|
28
26
|
uploadType: ['image'],
|
|
29
27
|
disabledMenuItem: ['Music', 'Videos', 'Documents', 'Others'],
|
|
30
|
-
r2_key: $editorStore.r2_key,
|
|
31
28
|
serverGetUrl: $editorStore.serverGetUrl,
|
|
32
29
|
serverUploadUrl: $editorStore.serverUploadUrl,
|
|
33
30
|
serverDeleteUrl: $editorStore.serverDeleteUrl,
|
|
34
|
-
serverStorageUrl: $editorStore.serverStorageUrl,
|
|
35
31
|
serverDownloadUrl: $editorStore.serverDownloadUrl,
|
|
36
32
|
uploadModalOpen: true
|
|
37
33
|
}));
|
|
@@ -1,12 +1,8 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
-
import {fileInputStore,resetFileInputStore, DropdownContainer, MenuItem, buttonRipple} from "../../../index.js";
|
|
3
|
-
import FileProperties from "./
|
|
4
|
-
import DownloadMedia from "./
|
|
5
|
-
import DeleteMedia from "./
|
|
6
|
-
|
|
7
|
-
let {
|
|
8
|
-
processing = $bindable(),
|
|
9
|
-
} = $props();
|
|
2
|
+
import {fileInputStore,resetFileInputStore, DropdownContainer, MenuItem, buttonRipple, Button} from "../../../index.js";
|
|
3
|
+
import FileProperties from "./properties.svelte";
|
|
4
|
+
import DownloadMedia from "./download.svelte";
|
|
5
|
+
import DeleteMedia from "./delete.svelte";
|
|
10
6
|
|
|
11
7
|
function handleSelect() {
|
|
12
8
|
fileInputStore.update(store => ({
|
|
@@ -30,17 +26,19 @@
|
|
|
30
26
|
|
|
31
27
|
<FileProperties bind:showProperties />
|
|
32
28
|
<div style="display: flex; gap: 1rem; align-items: center; margin-right: 1rem">
|
|
33
|
-
<button hidden={$fileInputStore.manage || disableActions} title="Select" onclick={handleSelect} style="color: var(--primary-bg);" aria-label="Select submissions"><i class="fa fa-check-circle-o"></i></button>
|
|
34
29
|
{#snippet TriggerMenu()}
|
|
35
30
|
<button hidden={$fileInputStore.activeTab !== 'Cloud'} disabled={$fileInputStore.submissions.length === 0} use:buttonRipple class="w-10" aria-label="Ellipsis" onclick={() => (openActionsMenu = !openActionsMenu)}>
|
|
36
31
|
<i class="fa fa-bars"></i>
|
|
37
32
|
</button>
|
|
38
33
|
{/snippet}
|
|
39
34
|
<DropdownContainer bind:open={openActionsMenu} dropdownTrigger={TriggerMenu}>
|
|
40
|
-
<MenuItem onclick={() => { showProperties = true; openActionsMenu = false; }}>Properties</MenuItem>
|
|
41
|
-
<DownloadMedia bind:
|
|
42
|
-
<DeleteMedia bind:
|
|
35
|
+
<MenuItem icon="fa fa-info-circle" iconGap="10px" iconSize="15px" onclick={() => { showProperties = true; openActionsMenu = false; }}>Properties</MenuItem>
|
|
36
|
+
<DownloadMedia bind:openActionsMenu/>
|
|
37
|
+
<DeleteMedia bind:openActionsMenu />
|
|
43
38
|
</DropdownContainer>
|
|
44
39
|
<button title="Cancel" onclick={resetFileInputStore} aria-label="Cancel"><i class="fa fa-xmark"></i></button>
|
|
45
40
|
</div>
|
|
41
|
+
<div hidden={$fileInputStore.manage || disableActions} style="position: absolute; bottom: 1rem; right: 1rem; z-index: 5;">
|
|
42
|
+
<Button endIcon="fa-check-circle-o" onclick={handleSelect}>Insert</Button>
|
|
43
|
+
</div>
|
|
46
44
|
|
|
@@ -3,7 +3,6 @@
|
|
|
3
3
|
import {modalStore, fileInputStore, MenuItem, addToast} from "../../../index.js";
|
|
4
4
|
|
|
5
5
|
let {
|
|
6
|
-
processing = $bindable(),
|
|
7
6
|
openActionsMenu = $bindable(),
|
|
8
7
|
} = $props();
|
|
9
8
|
|
|
@@ -28,13 +27,13 @@
|
|
|
28
27
|
if (!$fileInputStore.serverDeleteUrl) return;
|
|
29
28
|
|
|
30
29
|
try {
|
|
31
|
-
processing = true;
|
|
30
|
+
$fileInputStore.processing = true;
|
|
32
31
|
const response = await fetch($fileInputStore.serverDeleteUrl, {
|
|
33
32
|
method: 'DELETE',
|
|
34
33
|
headers: {
|
|
35
34
|
'Content-Type': 'application/json'
|
|
36
35
|
},
|
|
37
|
-
body: JSON.stringify(
|
|
36
|
+
body: JSON.stringify(idsToDelete)
|
|
38
37
|
});
|
|
39
38
|
|
|
40
39
|
const data = await response.json();
|
|
@@ -47,20 +46,15 @@
|
|
|
47
46
|
});
|
|
48
47
|
return;
|
|
49
48
|
}
|
|
50
|
-
|
|
51
|
-
data.forEach((item: { id: string; code: number }) => {
|
|
52
|
-
if (item.code === 404) {
|
|
53
|
-
addToast({
|
|
54
|
-
status: 'error',
|
|
55
|
-
message: `Failed to delete file with ID: ${item.id}`
|
|
56
|
-
});
|
|
57
|
-
}
|
|
58
|
-
});
|
|
59
49
|
fileInputStore.update(store => ({
|
|
60
50
|
...store,
|
|
61
51
|
submissions: [],
|
|
62
52
|
requestReload: true
|
|
63
53
|
}));
|
|
54
|
+
addToast({
|
|
55
|
+
status: 'success',
|
|
56
|
+
message: 'Files deleted successfully.'
|
|
57
|
+
});
|
|
64
58
|
|
|
65
59
|
} catch (error) {
|
|
66
60
|
addToast({
|
|
@@ -68,9 +62,9 @@
|
|
|
68
62
|
message: `Error deleting files: ${error}`
|
|
69
63
|
});
|
|
70
64
|
} finally {
|
|
71
|
-
processing = false;
|
|
65
|
+
$fileInputStore.processing = false;
|
|
72
66
|
}
|
|
73
67
|
}
|
|
74
68
|
</script>
|
|
75
69
|
|
|
76
|
-
<MenuItem iconGap="10px" onclick={handleDelete} icon="fa-trash-o" iconBg="var(--error-bg)" iconSize="
|
|
70
|
+
<MenuItem iconGap="10px" onclick={handleDelete} icon="fa-trash-o" iconBg="var(--error-bg)" iconSize="15px">Delete</MenuItem>
|
package/dist/Modules/FilePicker/{ActionsMenu/download_media.svelte → Actions/download.svelte}
RENAMED
|
@@ -1,16 +1,15 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import { get } from "svelte/store";
|
|
3
|
-
import {MenuItem, fileInputStore,
|
|
3
|
+
import {MenuItem, fileInputStore, addToast} from "../../../index.js";
|
|
4
4
|
|
|
5
5
|
let {
|
|
6
|
-
processing = $bindable(),
|
|
7
6
|
openActionsMenu = $bindable(),
|
|
8
7
|
} = $props();
|
|
9
8
|
|
|
10
9
|
async function handleDownload() {
|
|
11
10
|
openActionsMenu = false;
|
|
12
11
|
const store = get(fileInputStore);
|
|
13
|
-
if (!store.submissions?.length
|
|
12
|
+
if (!store.submissions?.length) {
|
|
14
13
|
addToast({
|
|
15
14
|
status: 'error',
|
|
16
15
|
message: 'An error occurred while processing your request.'
|
|
@@ -18,13 +17,13 @@
|
|
|
18
17
|
return;
|
|
19
18
|
};
|
|
20
19
|
try {
|
|
21
|
-
processing = true;
|
|
20
|
+
$fileInputStore.processing = true;
|
|
22
21
|
for (const item of store.submissions) {
|
|
23
22
|
if (!item.id) continue;
|
|
24
23
|
await download(item.id);
|
|
25
24
|
}
|
|
26
25
|
} finally {
|
|
27
|
-
processing = false;
|
|
26
|
+
$fileInputStore.processing = false;
|
|
28
27
|
fileInputStore.update(store => {
|
|
29
28
|
store.submissions = [];
|
|
30
29
|
return store;
|
|
@@ -34,13 +33,9 @@
|
|
|
34
33
|
|
|
35
34
|
async function download(id: string) {
|
|
36
35
|
try {
|
|
37
|
-
const
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
const res = await fetch($fileInputStore.serverDownloadUrl, {
|
|
42
|
-
method: 'POST',
|
|
43
|
-
body: formData
|
|
36
|
+
const downloadURL = $fileInputStore.serverDownloadUrl + `?id=${id}`;
|
|
37
|
+
const res = await fetch(downloadURL, {
|
|
38
|
+
method: 'GET',
|
|
44
39
|
});
|
|
45
40
|
|
|
46
41
|
if (!res.ok) {
|
|
@@ -80,4 +75,9 @@
|
|
|
80
75
|
}
|
|
81
76
|
</script>
|
|
82
77
|
|
|
83
|
-
<MenuItem
|
|
78
|
+
<MenuItem
|
|
79
|
+
icon="fa fa-download"
|
|
80
|
+
iconGap="10px"
|
|
81
|
+
iconSize="15px"
|
|
82
|
+
onclick={handleDownload}>Download
|
|
83
|
+
</MenuItem>
|
package/dist/Modules/FilePicker/{ActionsMenu/file_properties.svelte → Actions/properties.svelte}
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<script>
|
|
2
2
|
import {Wrapper, Backdrop, fileInputStore} from "../../../index.js";
|
|
3
|
-
import Storage from "
|
|
3
|
+
import Storage from "../Cloud/storage.svelte";
|
|
4
4
|
let {
|
|
5
5
|
showProperties = $bindable(),
|
|
6
6
|
} = $props();
|
|
@@ -16,8 +16,8 @@
|
|
|
16
16
|
</style>
|
|
17
17
|
<Backdrop bind:open={showProperties}>
|
|
18
18
|
<Wrapper>
|
|
19
|
-
<div style="display: flex; justify-content: space-between">
|
|
20
|
-
<Storage padding="0rem"
|
|
19
|
+
<div style="display: flex; justify-content: space-between; gap: 1rem">
|
|
20
|
+
<Storage padding="0rem" forceRender={true}/>
|
|
21
21
|
<button style="align-self: start;" title="Cancel" onclick={() => showProperties = false} aria-label="Cancel"><i class="fa fa-times"></i></button>
|
|
22
22
|
</div>
|
|
23
23
|
<div style="display: flex; flex-direction: column; gap: 1rem; margin-top: 1rem; max-height: 70vh; overflow-y: auto;">
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
export default
|
|
2
|
-
type
|
|
1
|
+
export default Properties;
|
|
2
|
+
type Properties = {
|
|
3
3
|
$on?(type: string, callback: (e: any) => void): () => void;
|
|
4
4
|
$set?(props: Partial<$$ComponentProps>): void;
|
|
5
5
|
};
|
|
6
|
-
declare const
|
|
6
|
+
declare const Properties: import("svelte").Component<{
|
|
7
7
|
showProperties?: any;
|
|
8
8
|
}, {}, "showProperties">;
|
|
9
9
|
type $$ComponentProps = {
|
|
@@ -1,12 +1,10 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
-
import {
|
|
3
|
-
import
|
|
4
|
-
import
|
|
5
|
-
import Storage from '../ActionsMenu/media_storage.svelte';
|
|
2
|
+
import { fileInputStore, MenuItem, isDesktop } from '../../../index.js';
|
|
3
|
+
import Content from './content.svelte';
|
|
4
|
+
import Storage from './storage.svelte';
|
|
6
5
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
{ name: 'Documents', icon: 'fa fa-file-o' },
|
|
6
|
+
const menuItem: { name: 'Documents'| 'Music' | 'Pictures' | 'Videos'| 'Others'; icon: string }[] = [
|
|
7
|
+
{ name: 'Documents', icon: 'fa fa-file-alt' },
|
|
10
8
|
{ name: 'Music', icon: 'fa fa-music' },
|
|
11
9
|
{ name: 'Pictures', icon: 'fa fa-file-image-o' },
|
|
12
10
|
{ name: 'Videos', icon: 'fa fa-film' },
|
|
@@ -14,120 +12,60 @@
|
|
|
14
12
|
];
|
|
15
13
|
|
|
16
14
|
$: if ($fileInputStore.disabledMenuItem?.includes($fileInputStore.activeMenu)) {
|
|
17
|
-
const firstAvailable =
|
|
15
|
+
const firstAvailable = menuItem.find(item => !$fileInputStore.disabledMenuItem?.includes(item.name));
|
|
18
16
|
if (firstAvailable) $fileInputStore.activeMenu = firstAvailable.name;
|
|
19
17
|
}
|
|
20
|
-
|
|
21
|
-
type MediaItem = {
|
|
22
|
-
id: string;
|
|
23
|
-
};
|
|
24
|
-
|
|
25
|
-
type MediaResponse = {
|
|
26
|
-
Audio: MediaItem[];
|
|
27
|
-
Documents: MediaItem[];
|
|
28
|
-
Images: MediaItem[];
|
|
29
|
-
Videos: MediaItem[];
|
|
30
|
-
Others: MediaItem[];
|
|
31
|
-
};
|
|
32
|
-
|
|
33
|
-
let media: MediaResponse | null = null;
|
|
34
|
-
let childFunctionRef : Storage;
|
|
35
|
-
|
|
36
|
-
async function loadMedia(){
|
|
37
|
-
if (!$User.userId || !$fileInputStore.serverGetUrl) return;
|
|
38
|
-
try {
|
|
39
|
-
processing = true;
|
|
40
|
-
const params = new URLSearchParams({
|
|
41
|
-
userId: $User.userId
|
|
42
|
-
});
|
|
43
|
-
|
|
44
|
-
const res = await fetch(`${$fileInputStore.serverGetUrl}?${params.toString()}`);
|
|
45
|
-
|
|
46
|
-
const data = await res.json();
|
|
47
|
-
|
|
48
|
-
if (!res.ok) {
|
|
49
|
-
//console.log('Error fetching media:', data);
|
|
50
|
-
addToast({
|
|
51
|
-
status: 'error',
|
|
52
|
-
message: data || "Failed to fetch media."
|
|
53
|
-
});
|
|
54
|
-
return;
|
|
55
|
-
}
|
|
56
|
-
media = data;
|
|
57
|
-
} catch (e) {
|
|
58
|
-
console.error("catch error:", e);
|
|
59
|
-
addToast({
|
|
60
|
-
status: 'error',
|
|
61
|
-
message: "An error occurred while fetching media."
|
|
62
|
-
});
|
|
63
|
-
} finally {
|
|
64
|
-
processing = false;
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
onMount(async () => {
|
|
69
|
-
await loadMedia();
|
|
70
|
-
});
|
|
71
|
-
|
|
72
|
-
$: if ($fileInputStore.requestReload && childFunctionRef){
|
|
73
|
-
fileInputStore.update(store => ({
|
|
74
|
-
...store,
|
|
75
|
-
requestReload: false
|
|
76
|
-
}));
|
|
77
|
-
loadMedia();
|
|
78
|
-
childFunctionRef.loadMediaStorage();
|
|
79
|
-
};
|
|
80
|
-
|
|
81
18
|
</script>
|
|
82
|
-
|
|
83
19
|
<style>
|
|
84
|
-
#sierra-cloud-store
|
|
85
|
-
|
|
20
|
+
#sierra-cloud-store{
|
|
21
|
+
position: relative;
|
|
22
|
+
display: flex;
|
|
23
|
+
height: 400px;
|
|
86
24
|
}
|
|
87
|
-
#sierra-cloud-store
|
|
88
|
-
|
|
89
|
-
background-color: var(--menu-item-active);
|
|
90
|
-
}
|
|
91
|
-
#sierra-cloud-store i{
|
|
92
|
-
color: var(--icon-theme);
|
|
25
|
+
#sierra-cloud-store nav{
|
|
26
|
+
padding-top: 1rem;
|
|
93
27
|
}
|
|
94
|
-
#sierra-cloud-store
|
|
95
|
-
|
|
28
|
+
#sierra-cloud-store main{
|
|
29
|
+
overflow-y: auto;
|
|
30
|
+
padding: 1rem;
|
|
96
31
|
}
|
|
97
|
-
@media (max-width:
|
|
98
|
-
#sierra-cloud-store
|
|
32
|
+
@media (max-width: 1024px) {
|
|
33
|
+
#sierra-cloud-store{
|
|
99
34
|
flex-direction: column-reverse;
|
|
100
35
|
}
|
|
101
36
|
#sierra-cloud-store nav{
|
|
102
|
-
|
|
103
|
-
}
|
|
104
|
-
#sierra-cloud-store nav button {
|
|
37
|
+
display: flex;
|
|
105
38
|
justify-content: center;
|
|
39
|
+
padding: 0.5rem 0rem;
|
|
40
|
+
}
|
|
41
|
+
#sierra-cloud-store main{
|
|
42
|
+
flex: 1;
|
|
106
43
|
}
|
|
107
44
|
}
|
|
108
45
|
</style>
|
|
109
|
-
<main id="sierra-cloud-store"
|
|
110
|
-
<nav
|
|
111
|
-
{#each
|
|
112
|
-
<
|
|
113
|
-
|
|
114
|
-
on:click={() =>
|
|
46
|
+
<main id="sierra-cloud-store">
|
|
47
|
+
<nav>
|
|
48
|
+
{#each menuItem as item}
|
|
49
|
+
<MenuItem
|
|
50
|
+
onclick={() =>
|
|
115
51
|
fileInputStore.update(store =>({
|
|
116
52
|
...store,
|
|
117
|
-
activeMenu:
|
|
53
|
+
activeMenu: item.name
|
|
118
54
|
}))
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
{
|
|
128
|
-
</
|
|
55
|
+
}
|
|
56
|
+
rounded={!$isDesktop ? '8px' : '0px'}
|
|
57
|
+
title={item.name}
|
|
58
|
+
hidden={$fileInputStore.disabledMenuItem?.includes(item.name)}
|
|
59
|
+
active={$fileInputStore.activeMenu === item.name}
|
|
60
|
+
icon={item.icon}
|
|
61
|
+
iconSize='15px'
|
|
62
|
+
iconBg={ $fileInputStore.activeMenu === item.name ? 'var(--primary-bg)' : 'var(--text-secondary)'}
|
|
63
|
+
iconGap="10px">{!$isDesktop ? '' : item.name}
|
|
64
|
+
</MenuItem>
|
|
129
65
|
{/each}
|
|
130
|
-
<Storage
|
|
66
|
+
<Storage/>
|
|
131
67
|
</nav>
|
|
132
|
-
<
|
|
68
|
+
<main>
|
|
69
|
+
<Content/>
|
|
70
|
+
</main>
|
|
133
71
|
</main>
|
|
@@ -2,7 +2,7 @@ interface $$__sveltets_2_IsomorphicComponent<Props extends Record<string, any> =
|
|
|
2
2
|
new (options: import('svelte').ComponentConstructorOptions<Props>): import('svelte').SvelteComponent<Props, Events, Slots> & {
|
|
3
3
|
$$bindings?: Bindings;
|
|
4
4
|
} & Exports;
|
|
5
|
-
(internal: unknown, props:
|
|
5
|
+
(internal: unknown, props: {
|
|
6
6
|
$$events?: Events;
|
|
7
7
|
$$slots?: Slots;
|
|
8
8
|
}): Exports & {
|
|
@@ -11,9 +11,7 @@ interface $$__sveltets_2_IsomorphicComponent<Props extends Record<string, any> =
|
|
|
11
11
|
};
|
|
12
12
|
z_$$bindings?: Bindings;
|
|
13
13
|
}
|
|
14
|
-
declare const CloudStore: $$__sveltets_2_IsomorphicComponent<{
|
|
15
|
-
processing: boolean;
|
|
16
|
-
}, {
|
|
14
|
+
declare const CloudStore: $$__sveltets_2_IsomorphicComponent<Record<string, never>, {
|
|
17
15
|
[evt: string]: CustomEvent<any>;
|
|
18
16
|
}, {}, {}, string>;
|
|
19
17
|
type CloudStore = InstanceType<typeof CloudStore>;
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { browser } from "$app/environment";
|
|
3
|
+
import { fileInputStore, addToast, PreviewImage, PreviewVideo, PreviewAudio, PreviewDocument, PreviewGenericFile } from "../../../index.js";
|
|
4
|
+
import type {FileInputStoreMediaItem} from '../../../index.js';
|
|
5
|
+
|
|
6
|
+
let itemCount = $state(0);
|
|
7
|
+
let totalSizeMB = $state(0);
|
|
8
|
+
let currentCategory = $state('');
|
|
9
|
+
let lastSelectedCategory = $state('');
|
|
10
|
+
const categoryMap = {
|
|
11
|
+
Documents: "document",
|
|
12
|
+
Music: "audio",
|
|
13
|
+
Pictures: "image",
|
|
14
|
+
Videos: "video",
|
|
15
|
+
Others: "other"
|
|
16
|
+
};
|
|
17
|
+
const media = $state<{
|
|
18
|
+
Images: any[];
|
|
19
|
+
Videos: any[];
|
|
20
|
+
Audio: any[];
|
|
21
|
+
Documents: any[];
|
|
22
|
+
Others: any[];
|
|
23
|
+
}>({
|
|
24
|
+
Images: [],
|
|
25
|
+
Videos: [],
|
|
26
|
+
Audio: [],
|
|
27
|
+
Documents: [],
|
|
28
|
+
Others: []
|
|
29
|
+
});
|
|
30
|
+
async function loadMedia(category: string) {
|
|
31
|
+
if (!$fileInputStore.serverGetUrl) return;
|
|
32
|
+
try {
|
|
33
|
+
$fileInputStore.processing = true;
|
|
34
|
+
const res = await fetch(`${$fileInputStore.serverGetUrl}?category=${category}`);
|
|
35
|
+
const data = await res.json();
|
|
36
|
+
|
|
37
|
+
if (!res.ok) {
|
|
38
|
+
addToast({
|
|
39
|
+
status: 'error',
|
|
40
|
+
message: data || "Failed to fetch media."
|
|
41
|
+
});
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
media.Images = [];
|
|
45
|
+
media.Videos = [];
|
|
46
|
+
media.Audio = [];
|
|
47
|
+
media.Documents = [];
|
|
48
|
+
media.Others = [];
|
|
49
|
+
//console.log('Fetched media:', data);
|
|
50
|
+
for (const item of data) {
|
|
51
|
+
switch (item.category) {
|
|
52
|
+
case "image":
|
|
53
|
+
media.Images.push(item);
|
|
54
|
+
break;
|
|
55
|
+
case "video":
|
|
56
|
+
media.Videos.push(item);
|
|
57
|
+
break;
|
|
58
|
+
case "audio":
|
|
59
|
+
media.Audio.push(item);
|
|
60
|
+
break;
|
|
61
|
+
case "document":
|
|
62
|
+
media.Documents.push(item);
|
|
63
|
+
break;
|
|
64
|
+
default:
|
|
65
|
+
media.Others.push(item);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
} catch (e) {
|
|
69
|
+
console.error("catch error:", e);
|
|
70
|
+
addToast({
|
|
71
|
+
status: 'error',
|
|
72
|
+
message: "An error occurred while fetching media."
|
|
73
|
+
});
|
|
74
|
+
} finally {
|
|
75
|
+
$fileInputStore.processing = false;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
$effect(()=> {
|
|
80
|
+
if( !browser) return;
|
|
81
|
+
if($fileInputStore.activeMenu){
|
|
82
|
+
currentCategory = categoryMap[$fileInputStore.activeMenu];
|
|
83
|
+
if (lastSelectedCategory !== currentCategory) {
|
|
84
|
+
loadMedia(currentCategory);
|
|
85
|
+
lastSelectedCategory = currentCategory;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
if($fileInputStore.submissions){
|
|
89
|
+
const submissions = $fileInputStore.submissions || [];
|
|
90
|
+
itemCount = submissions.length;
|
|
91
|
+
const totalBytes = submissions.reduce(
|
|
92
|
+
(sum: number, item: FileInputStoreMediaItem) => sum + (item.size_bytes || 0),
|
|
93
|
+
0
|
|
94
|
+
);
|
|
95
|
+
totalSizeMB = +(totalBytes / (1024 * 1024)).toFixed(2);
|
|
96
|
+
}
|
|
97
|
+
if($fileInputStore.requestReload){
|
|
98
|
+
$fileInputStore.requestReload = false;
|
|
99
|
+
loadMedia(currentCategory);
|
|
100
|
+
}
|
|
101
|
+
})
|
|
102
|
+
</script>
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
{#if $fileInputStore.activeMenu === 'Music'}
|
|
106
|
+
<PreviewAudio {media} />
|
|
107
|
+
{:else if $fileInputStore.activeMenu === 'Documents'}
|
|
108
|
+
<PreviewDocument {media} />
|
|
109
|
+
{:else if $fileInputStore.activeMenu === 'Pictures'}
|
|
110
|
+
<PreviewImage {media} />
|
|
111
|
+
{:else if $fileInputStore.activeMenu === 'Videos'}
|
|
112
|
+
<PreviewVideo {media} />
|
|
113
|
+
{:else if $fileInputStore.activeMenu === 'Others'}
|
|
114
|
+
<PreviewGenericFile {media} />
|
|
115
|
+
{/if}
|
|
116
|
+
<div hidden={itemCount === 0} style="position: absolute; top: 0; right: 0;padding: 2px 5px; box-shadow: var(--box-shadow); background: var(--background); z-index: 1;">
|
|
117
|
+
{itemCount} {itemCount === 1 ? 'item' : 'items'} selected ({totalSizeMB} MB)
|
|
118
|
+
</div>
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
<script>
|
|
2
|
+
import { onMount } from 'svelte';
|
|
3
|
+
import { fileInputStore,CustomProgress, isDesktop, addToast} from '../../../index.js';
|
|
4
|
+
|
|
5
|
+
let {
|
|
6
|
+
padding = "0.5rem",
|
|
7
|
+
width = "100%",
|
|
8
|
+
forceRender = $bindable(false),
|
|
9
|
+
} = $props()
|
|
10
|
+
|
|
11
|
+
let progress = $state(0);
|
|
12
|
+
let progressColor = $state("var(--primary-bg)");
|
|
13
|
+
|
|
14
|
+
onMount(()=>{
|
|
15
|
+
if(!$fileInputStore.usedBytes || !$fileInputStore.maxBytes){
|
|
16
|
+
addToast({
|
|
17
|
+
status: 'warning',
|
|
18
|
+
message: 'Unable to calculate storage usage. Please configure your cloud storage settings.',
|
|
19
|
+
persistent: true,
|
|
20
|
+
});
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
progress = ($fileInputStore.usedBytes / $fileInputStore.maxBytes) * 100;
|
|
24
|
+
})
|
|
25
|
+
$effect(()=>{
|
|
26
|
+
if(progress){
|
|
27
|
+
progressColor = progress < 60
|
|
28
|
+
? 'var(--primary-bg)'
|
|
29
|
+
: progress < 90
|
|
30
|
+
? 'var(--warning-bg)'
|
|
31
|
+
: 'var(--error-bg)';
|
|
32
|
+
}
|
|
33
|
+
})
|
|
34
|
+
</script>
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
<div hidden={!$isDesktop && !forceRender} style="padding: 0.5rem {padding}; width: {width};">
|
|
38
|
+
<CustomProgress value={progress} color={progressColor}/>
|
|
39
|
+
<h3 style="font-size: 0.8rem;">{progress.toFixed(2)}% of 100 MB</h3>
|
|
40
|
+
</div>
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export default Storage;
|
|
2
|
+
type Storage = {
|
|
3
|
+
$on?(type: string, callback: (e: any) => void): () => void;
|
|
4
|
+
$set?(props: Partial<$$ComponentProps>): void;
|
|
5
|
+
};
|
|
6
|
+
declare const Storage: import("svelte").Component<{
|
|
7
|
+
padding?: string;
|
|
8
|
+
width?: string;
|
|
9
|
+
forceRender?: boolean;
|
|
10
|
+
}, {}, "forceRender">;
|
|
11
|
+
type $$ComponentProps = {
|
|
12
|
+
padding?: string;
|
|
13
|
+
width?: string;
|
|
14
|
+
forceRender?: boolean;
|
|
15
|
+
};
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import {fileInputStore,FileInput, addToast} from '../../../index.js'
|
|
3
|
+
|
|
4
|
+
async function handleUpload() {
|
|
5
|
+
if( $fileInputStore.selectedFiles.length === 0 || !$fileInputStore.serverUploadUrl) return;
|
|
6
|
+
|
|
7
|
+
try {
|
|
8
|
+
$fileInputStore.processing = true;
|
|
9
|
+
const formData = new FormData();
|
|
10
|
+
|
|
11
|
+
$fileInputStore.selectedFiles.forEach(file => formData.append('files', file));
|
|
12
|
+
|
|
13
|
+
const res = await fetch($fileInputStore.serverUploadUrl, {
|
|
14
|
+
method: 'POST',
|
|
15
|
+
body: formData
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
const data = await res.json();
|
|
19
|
+
|
|
20
|
+
if (!res.ok) {
|
|
21
|
+
addToast({
|
|
22
|
+
status: 'error',
|
|
23
|
+
message: data || 'Upload failed',
|
|
24
|
+
persistent: true
|
|
25
|
+
});
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
fileInputStore.update(store => {
|
|
30
|
+
store.selectedFiles = [];
|
|
31
|
+
store.activeTab = 'Cloud';
|
|
32
|
+
return store;
|
|
33
|
+
});
|
|
34
|
+
addToast({
|
|
35
|
+
status: 'success',
|
|
36
|
+
message: 'Files uploaded successfully!'
|
|
37
|
+
});
|
|
38
|
+
} catch (err) {
|
|
39
|
+
addToast({
|
|
40
|
+
status: 'error',
|
|
41
|
+
message: `Upload failed: ${err}`,
|
|
42
|
+
persistent: true
|
|
43
|
+
});
|
|
44
|
+
} finally {
|
|
45
|
+
$fileInputStore.processing = false;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
</script>
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
<FileInput
|
|
52
|
+
minHeight="400px"
|
|
53
|
+
bind:processing={$fileInputStore.processing}
|
|
54
|
+
onclick={handleUpload}
|
|
55
|
+
/>
|
|
@@ -2,7 +2,7 @@ interface $$__sveltets_2_IsomorphicComponent<Props extends Record<string, any> =
|
|
|
2
2
|
new (options: import('svelte').ComponentConstructorOptions<Props>): import('svelte').SvelteComponent<Props, Events, Slots> & {
|
|
3
3
|
$$bindings?: Bindings;
|
|
4
4
|
} & Exports;
|
|
5
|
-
(internal: unknown, props:
|
|
5
|
+
(internal: unknown, props: {
|
|
6
6
|
$$events?: Events;
|
|
7
7
|
$$slots?: Slots;
|
|
8
8
|
}): Exports & {
|
|
@@ -11,10 +11,8 @@ interface $$__sveltets_2_IsomorphicComponent<Props extends Record<string, any> =
|
|
|
11
11
|
};
|
|
12
12
|
z_$$bindings?: Bindings;
|
|
13
13
|
}
|
|
14
|
-
declare const
|
|
15
|
-
media: any;
|
|
16
|
-
}, {
|
|
14
|
+
declare const Upload: $$__sveltets_2_IsomorphicComponent<Record<string, never>, {
|
|
17
15
|
[evt: string]: CustomEvent<any>;
|
|
18
16
|
}, {}, {}, string>;
|
|
19
|
-
type
|
|
20
|
-
export default
|
|
17
|
+
type Upload = InstanceType<typeof Upload>;
|
|
18
|
+
export default Upload;
|
|
@@ -1,83 +1,31 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
-
import {Backdrop,Wrapper,fileInputStore, LinearProgress,
|
|
2
|
+
import {Backdrop,Wrapper,fileInputStore, LinearProgress, Tabs, resetFileInputStore, addToast} from '../../index.js'
|
|
3
|
+
|
|
3
4
|
import CloudStore from './Cloud/cloudStore.svelte';
|
|
4
|
-
import Actions from './
|
|
5
|
-
|
|
6
|
-
let processing: boolean = false;
|
|
7
|
-
|
|
8
|
-
async function handleUpload() {
|
|
9
|
-
if(!$User.userId || !$fileInputStore.r2_key || $fileInputStore.selectedFiles.length === 0 || !$fileInputStore.serverUploadUrl) return;
|
|
10
|
-
|
|
11
|
-
try {
|
|
12
|
-
processing = true;
|
|
13
|
-
const formData = new FormData();
|
|
14
|
-
|
|
15
|
-
$fileInputStore.selectedFiles.forEach(file => formData.append('files', file));
|
|
16
|
-
formData.append('r2_key', $fileInputStore.r2_key);
|
|
17
|
-
formData.append('userid', $User.userId);
|
|
18
|
-
|
|
19
|
-
const res = await fetch($fileInputStore.serverUploadUrl, {
|
|
20
|
-
method: 'POST',
|
|
21
|
-
body: formData
|
|
22
|
-
});
|
|
23
|
-
|
|
24
|
-
const data = await res.json();
|
|
25
|
-
|
|
26
|
-
if (!res.ok) {
|
|
27
|
-
addToast({
|
|
28
|
-
status: 'error',
|
|
29
|
-
message: data || 'Upload failed',
|
|
30
|
-
persistent: true
|
|
31
|
-
});
|
|
32
|
-
return;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
data.forEach((item: { original_name: string; code: number; }) => {
|
|
36
|
-
if (item.code === 500) {
|
|
37
|
-
addToast({
|
|
38
|
-
status: 'error',
|
|
39
|
-
message: `Failed to upload file: ${item.original_name}`
|
|
40
|
-
});
|
|
41
|
-
}
|
|
42
|
-
});
|
|
43
|
-
|
|
44
|
-
fileInputStore.update(store => {
|
|
45
|
-
store.selectedFiles = [];
|
|
46
|
-
store.activeTab = 'Cloud';
|
|
47
|
-
store.requestReload = true;
|
|
48
|
-
return store;
|
|
49
|
-
});
|
|
50
|
-
} catch (err) {
|
|
51
|
-
addToast({
|
|
52
|
-
status: 'error',
|
|
53
|
-
message: `Upload failed: ${err}`,
|
|
54
|
-
persistent: true
|
|
55
|
-
});
|
|
56
|
-
} finally {
|
|
57
|
-
processing = false;
|
|
58
|
-
}
|
|
59
|
-
}
|
|
5
|
+
import Actions from './Actions/Actions.svelte';
|
|
6
|
+
import UploadMedia from './Upload/upload.svelte';
|
|
60
7
|
|
|
61
8
|
function onEscape(){
|
|
62
9
|
resetFileInputStore();
|
|
63
10
|
}
|
|
11
|
+
|
|
64
12
|
</script>
|
|
65
13
|
|
|
66
14
|
|
|
67
15
|
{#snippet Cloud()}
|
|
68
|
-
<CloudStore
|
|
16
|
+
<CloudStore />
|
|
69
17
|
{/snippet}
|
|
70
18
|
|
|
71
19
|
{#snippet Upload()}
|
|
72
|
-
<
|
|
20
|
+
<UploadMedia />
|
|
73
21
|
{/snippet}
|
|
74
22
|
|
|
75
23
|
{#snippet actions()}
|
|
76
|
-
<Actions
|
|
24
|
+
<Actions/>
|
|
77
25
|
{/snippet}
|
|
78
26
|
|
|
79
27
|
<Backdrop onClose={onEscape} bind:open={$fileInputStore.uploadModalOpen} zIndex={15}>
|
|
80
|
-
<Wrapper maxWidth="700px"
|
|
28
|
+
<Wrapper maxWidth="700px" padding="0px">
|
|
81
29
|
<Tabs
|
|
82
30
|
bind:active={$fileInputStore.activeTab}
|
|
83
31
|
tabs={[
|
|
@@ -86,7 +34,8 @@
|
|
|
86
34
|
]}
|
|
87
35
|
snippets={{ Cloud, Upload }}
|
|
88
36
|
actions={actions}
|
|
37
|
+
padding="0px"
|
|
89
38
|
/>
|
|
90
|
-
{#if processing}<LinearProgress />{/if}
|
|
39
|
+
{#if $fileInputStore.processing}<LinearProgress />{/if}
|
|
91
40
|
</Wrapper>
|
|
92
41
|
</Backdrop>
|
|
@@ -4,11 +4,9 @@ export type EditorState = {
|
|
|
4
4
|
content: any;
|
|
5
5
|
export: boolean;
|
|
6
6
|
onExport: any;
|
|
7
|
-
r2_key: string;
|
|
8
7
|
serverGetUrl: string;
|
|
9
8
|
serverUploadUrl: string;
|
|
10
9
|
serverDeleteUrl: string;
|
|
11
|
-
serverStorageUrl: string;
|
|
12
10
|
serverDownloadUrl: string;
|
|
13
11
|
enabledFeatures: EditorFeature[];
|
|
14
12
|
insertImageMode: ImageInsertMode[];
|
|
@@ -3,11 +3,9 @@ const defaultEditorState = {
|
|
|
3
3
|
content: {},
|
|
4
4
|
export: false,
|
|
5
5
|
onExport: () => { },
|
|
6
|
-
r2_key: '',
|
|
7
6
|
serverGetUrl: '',
|
|
8
7
|
serverUploadUrl: '',
|
|
9
8
|
serverDeleteUrl: '',
|
|
10
|
-
serverStorageUrl: '',
|
|
11
9
|
serverDownloadUrl: '',
|
|
12
10
|
enabledFeatures: [],
|
|
13
11
|
insertImageMode: ['filepicker', 'url']
|
|
@@ -23,9 +23,11 @@ export type FileInputState = {
|
|
|
23
23
|
serverGetUrl: string;
|
|
24
24
|
serverUploadUrl: string;
|
|
25
25
|
serverDeleteUrl: string;
|
|
26
|
-
serverStorageUrl: string;
|
|
27
26
|
serverDownloadUrl: string;
|
|
28
27
|
requestReload: boolean;
|
|
28
|
+
processing?: boolean;
|
|
29
|
+
usedBytes: number;
|
|
30
|
+
maxBytes: number;
|
|
29
31
|
};
|
|
30
32
|
export declare const fileInputStore: import("svelte/store").Writable<FileInputState>;
|
|
31
33
|
export declare function resetFileInputStore(): void;
|
|
@@ -15,9 +15,11 @@ const defaultFileInputState = {
|
|
|
15
15
|
serverGetUrl: '',
|
|
16
16
|
serverUploadUrl: '',
|
|
17
17
|
serverDeleteUrl: '',
|
|
18
|
-
serverStorageUrl: '',
|
|
19
18
|
serverDownloadUrl: '',
|
|
20
|
-
requestReload: false
|
|
19
|
+
requestReload: false,
|
|
20
|
+
processing: false,
|
|
21
|
+
usedBytes: 0,
|
|
22
|
+
maxBytes: 0
|
|
21
23
|
};
|
|
22
24
|
export const fileInputStore = writable({
|
|
23
25
|
...defaultFileInputState
|
package/package.json
CHANGED
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
<script>
|
|
2
|
-
import { onMount } from 'svelte';
|
|
3
|
-
import { fileInputStore, User, CustomProgress, addToast} from '../../../index.js';
|
|
4
|
-
|
|
5
|
-
export let padding = "0.5rem";
|
|
6
|
-
export let hidden = false;
|
|
7
|
-
export let width = "100%";
|
|
8
|
-
|
|
9
|
-
$: progress = 0;
|
|
10
|
-
$: progressColor = progress < 60
|
|
11
|
-
? 'var(--primary-bg)'
|
|
12
|
-
: progress < 90
|
|
13
|
-
? 'var(--warning-bg)'
|
|
14
|
-
: 'var(--error-bg)';
|
|
15
|
-
|
|
16
|
-
export async function loadMediaStorage(){
|
|
17
|
-
if (!$User.userId || !$fileInputStore.serverStorageUrl) return;
|
|
18
|
-
try {
|
|
19
|
-
const params = new URLSearchParams({
|
|
20
|
-
userId: $User.userId
|
|
21
|
-
});
|
|
22
|
-
|
|
23
|
-
const res = await fetch(`${$fileInputStore.serverStorageUrl}?${params.toString()}`);
|
|
24
|
-
|
|
25
|
-
const data = await res.json();
|
|
26
|
-
|
|
27
|
-
if (!res.ok) {
|
|
28
|
-
addToast({
|
|
29
|
-
status: 'error',
|
|
30
|
-
message: data || "Failed to fetch storage usage.",
|
|
31
|
-
persistent: true
|
|
32
|
-
});
|
|
33
|
-
return;
|
|
34
|
-
}
|
|
35
|
-
const { usedBytes, maxBytes } = data.storage;
|
|
36
|
-
progress = (usedBytes / maxBytes) * 100;
|
|
37
|
-
} catch (e) {
|
|
38
|
-
console.error("catch error:", e);
|
|
39
|
-
addToast({
|
|
40
|
-
status: 'error',
|
|
41
|
-
message: "An error occurred while fetching storage usage.",
|
|
42
|
-
persistent: true
|
|
43
|
-
});
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
onMount(async () => {
|
|
48
|
-
await loadMediaStorage();
|
|
49
|
-
});
|
|
50
|
-
</script>
|
|
51
|
-
<div hidden={hidden} style="padding: 0.5rem {padding}; width: {width};">
|
|
52
|
-
<CustomProgress value={progress} color={progressColor}/>
|
|
53
|
-
<h3 style="font-size: 0.8rem;">{progress.toFixed(2)}% of 100 MB</h3>
|
|
54
|
-
</div>
|
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
export default MediaStorage;
|
|
2
|
-
type MediaStorage = SvelteComponent<{
|
|
3
|
-
width?: string | undefined;
|
|
4
|
-
padding?: string | undefined;
|
|
5
|
-
hidden?: boolean | undefined;
|
|
6
|
-
loadMediaStorage?: (() => Promise<void>) | undefined;
|
|
7
|
-
}, {
|
|
8
|
-
[evt: string]: CustomEvent<any>;
|
|
9
|
-
}, {}> & {
|
|
10
|
-
$$bindings?: string | undefined;
|
|
11
|
-
} & {
|
|
12
|
-
loadMediaStorage: () => Promise<void>;
|
|
13
|
-
};
|
|
14
|
-
declare const MediaStorage: $$__sveltets_2_IsomorphicComponent<{
|
|
15
|
-
width?: string | undefined;
|
|
16
|
-
padding?: string | undefined;
|
|
17
|
-
hidden?: boolean | undefined;
|
|
18
|
-
loadMediaStorage?: (() => Promise<void>) | undefined;
|
|
19
|
-
}, {
|
|
20
|
-
[evt: string]: CustomEvent<any>;
|
|
21
|
-
}, {}, {
|
|
22
|
-
loadMediaStorage: () => Promise<void>;
|
|
23
|
-
}, string>;
|
|
24
|
-
interface $$__sveltets_2_IsomorphicComponent<Props extends Record<string, any> = any, Events extends Record<string, any> = any, Slots extends Record<string, any> = any, Exports = {}, Bindings = string> {
|
|
25
|
-
new (options: import("svelte").ComponentConstructorOptions<Props>): import("svelte").SvelteComponent<Props, Events, Slots> & {
|
|
26
|
-
$$bindings?: Bindings;
|
|
27
|
-
} & Exports;
|
|
28
|
-
(internal: unknown, props: Props & {
|
|
29
|
-
$$events?: Events;
|
|
30
|
-
$$slots?: Slots;
|
|
31
|
-
}): Exports & {
|
|
32
|
-
$set?: any;
|
|
33
|
-
$on?: any;
|
|
34
|
-
};
|
|
35
|
-
z_$$bindings?: Bindings;
|
|
36
|
-
}
|
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
<script lang="ts">
|
|
2
|
-
import {PreviewAudio,PreviewDocument,PreviewImage,PreviewVideo, fileInputStore, PreviewGenericFile} from '../../../index.js';
|
|
3
|
-
import type {FileInputStoreMediaItem} from '../../../index.js';
|
|
4
|
-
|
|
5
|
-
export let media;
|
|
6
|
-
|
|
7
|
-
$: itemCount = 0;
|
|
8
|
-
$: totalSizeMB = 0;
|
|
9
|
-
$: {
|
|
10
|
-
const submissions = $fileInputStore.submissions || [];
|
|
11
|
-
itemCount = submissions.length;
|
|
12
|
-
const totalBytes = submissions.reduce(
|
|
13
|
-
(sum: number, item: FileInputStoreMediaItem) => sum + (item.size_bytes || 0),
|
|
14
|
-
0
|
|
15
|
-
);
|
|
16
|
-
totalSizeMB = +(totalBytes / (1024 * 1024)).toFixed(2);
|
|
17
|
-
}
|
|
18
|
-
</script>
|
|
19
|
-
|
|
20
|
-
<div id="file-picker-previews" style="flex-grow: 1; overflow-y: auto; padding-top: 1rem; max-height: 400px;">
|
|
21
|
-
{#if media}
|
|
22
|
-
{#if $fileInputStore.activeMenu === 'Music'}
|
|
23
|
-
<PreviewAudio {media} />
|
|
24
|
-
{:else if $fileInputStore.activeMenu === 'Documents'}
|
|
25
|
-
<PreviewDocument {media} />
|
|
26
|
-
{:else if $fileInputStore.activeMenu === 'Pictures'}
|
|
27
|
-
<PreviewImage {media} />
|
|
28
|
-
{:else if $fileInputStore.activeMenu === 'Videos'}
|
|
29
|
-
<PreviewVideo {media} />
|
|
30
|
-
{:else if $fileInputStore.activeMenu === 'Others'}
|
|
31
|
-
<PreviewGenericFile {media} />
|
|
32
|
-
{/if}
|
|
33
|
-
{:else}
|
|
34
|
-
<p>No media available.</p>
|
|
35
|
-
{/if}
|
|
36
|
-
<div hidden={itemCount === 0} style="position: absolute; bottom: 0; right: 0;padding: 2px 5px; box-shadow: var(--box-shadow); background: var(--background); z-index: 1;">
|
|
37
|
-
{itemCount} {itemCount === 1 ? 'item' : 'items'} selected ({totalSizeMB} MB)
|
|
38
|
-
</div>
|
|
39
|
-
</div>
|