Skip to content

Commit 86498c1

Browse files
committed
feat(upload): revamp file upload page UX
1 parent b92d979 commit 86498c1

3 files changed

Lines changed: 295 additions & 176 deletions

File tree

Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
<template>
2+
<div space-y-6>
3+
<div>
4+
<h3 mb-4>Upload Settings</h3>
5+
<div space-y-4>
6+
<div v-if="showFileNameType" space-y-2>
7+
<UiLabel :for="`filename-${id}`">File Name Type</UiLabel>
8+
<UiTabs
9+
:id="`filename-${id}`"
10+
:model-value="settings.fileNameType"
11+
variant="secondary"
12+
:items="[
13+
{
14+
label: 'Random',
15+
icon: 'solar:box-bold',
16+
},
17+
{
18+
label: 'UUID',
19+
icon: 'solar:key-bold',
20+
},
21+
{
22+
label: 'Original',
23+
icon: 'solar:document-bold',
24+
},
25+
]"
26+
rounded-xl="!"
27+
button-class="rounded-lg!"
28+
width-full
29+
@update:model-value="(val) => emit('update:fileNameType', val as never)"
30+
/>
31+
</div>
32+
33+
<div space-y-1>
34+
<UiInput
35+
:model-value="settings.password ?? ''"
36+
wfull
37+
label="Password"
38+
type="password"
39+
:disabled="disabled"
40+
@update:model-value="
41+
(val) => emit('update:password', (val as string) || null)
42+
"
43+
/>
44+
</div>
45+
46+
<div space-y-1>
47+
<UiInput
48+
:model-value="settings.maxViews || 0"
49+
wfull
50+
label="Max Views"
51+
caption="Set to 0 for unlimited views."
52+
type="number"
53+
:min="0"
54+
:disabled="disabled"
55+
@update:model-value="(val) => emit('update:maxViews', +val)"
56+
/>
57+
</div>
58+
</div>
59+
</div>
60+
61+
<div space-y-3>
62+
<h4 text-sm>Distribution</h4>
63+
<div space-y-4>
64+
<ExpirationPicker
65+
:model-value="settings.expiration"
66+
@update:model-value="(val) => emit('update:expiration', val)"
67+
>
68+
<UiInput
69+
:model-value="settings.expiration.label"
70+
label="Expiration"
71+
type="string"
72+
:disabled="disabled"
73+
readonly
74+
wfull
75+
cursor-pointer="!"
76+
/>
77+
</ExpirationPicker>
78+
79+
<FolderPicker
80+
:model-value="settings.folder"
81+
@update:model-value="(val) => emit('update:folder', val)"
82+
>
83+
<UiInput
84+
:model-value="settings.folder.label"
85+
label="Folder"
86+
type="string"
87+
:disabled="disabled"
88+
readonly
89+
wfull
90+
cursor-pointer="!"
91+
/>
92+
</FolderPicker>
93+
</div>
94+
</div>
95+
96+
<div v-if="showCompression" space-y-3>
97+
<h4 text-sm>Media Options</h4>
98+
<CompressionPicker
99+
:model-value="settings.compression"
100+
@update:model-value="(val) => emit('update:compression', val)"
101+
>
102+
<UiInput
103+
:model-value="settings.compression.label"
104+
label="Compression"
105+
type="string"
106+
:disabled="disabled"
107+
readonly
108+
wfull
109+
cursor-pointer="!"
110+
/>
111+
</CompressionPicker>
112+
</div>
113+
</div>
114+
</template>
115+
116+
<script setup lang="ts">
117+
type FileNameType = 'Random' | 'UUID' | 'Original';
118+
119+
interface ExpirationSetting {
120+
label: string;
121+
value: number | null;
122+
}
123+
124+
interface CompressionSetting {
125+
label: string;
126+
value: number;
127+
}
128+
129+
interface FolderSetting {
130+
label: string;
131+
value: string | null;
132+
}
133+
134+
interface Settings {
135+
fileNameType: FileNameType;
136+
maxViews: number;
137+
password: string | null;
138+
expiration: ExpirationSetting;
139+
compression: CompressionSetting;
140+
folder: FolderSetting;
141+
}
142+
143+
defineProps<{
144+
settings: Settings;
145+
disabled?: boolean;
146+
showFileNameType?: boolean;
147+
showCompression?: boolean;
148+
}>();
149+
150+
const emit = defineEmits<{
151+
'update:fileNameType': [FileNameType];
152+
'update:password': [string | null];
153+
'update:maxViews': [number];
154+
'update:expiration': [{ label: string; value: number | null }];
155+
'update:folder': [{ label: string; value: string | null }];
156+
'update:compression': [{ label: string; value: number }];
157+
}>();
158+
159+
const id = useId();
160+
</script>
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
<template>
2+
<div wfull rounded-xl bg-fs-overlay-2 p6 space-y-6 border="~ fs-overlay-4">
3+
<h3>
4+
<template v-if="activeTab === 'Media Upload'">Upload Media Files</template>
5+
<template v-else>Create Text File</template>
6+
</h3>
7+
8+
<DropZone
9+
v-if="activeTab === 'Media Upload'"
10+
v-model="uploadingFiles"
11+
:disabled="disabled"
12+
/>
13+
14+
<div v-else space-y-4>
15+
<div flex="~ items-center gap4 <sm:col" wfull>
16+
<UiInput
17+
v-model="textFileData.fileName"
18+
label="File Name"
19+
type="text"
20+
required
21+
wfull
22+
flex-1
23+
wrapper-class="wfull"
24+
:disabled="disabled"
25+
/>
26+
<FileTypePicker v-model="textFileData.fileType">
27+
<UiInput
28+
v-model="textFileData.fileType.label"
29+
label="File Type"
30+
type="string"
31+
:disabled="disabled"
32+
cursor-pointer="!"
33+
readonly
34+
required
35+
wfull
36+
wrapper-class="wfull sm:w72"
37+
/>
38+
</FileTypePicker>
39+
</div>
40+
41+
<UiTextArea
42+
v-model="textFileData.content"
43+
:disabled="disabled"
44+
label="Text"
45+
required
46+
wfull
47+
/>
48+
</div>
49+
50+
<div flex="~ gap2 items-center">
51+
<UiButton
52+
wfull
53+
gap2
54+
alignment="center"
55+
variant="accent"
56+
icon="solar:upload-minimalistic-linear"
57+
icon-size="20"
58+
:disabled="buttonDisabled"
59+
:loading="disabled"
60+
@click="emit('upload')"
61+
>
62+
<template v-if="activeTab === 'Media Upload'">Upload File(s)</template>
63+
<template v-else>Create Text</template>
64+
</UiButton>
65+
</div>
66+
</div>
67+
</template>
68+
69+
<script setup lang="ts">
70+
const props = defineProps<{
71+
activeTab: 'Media Upload' | 'Text Upload';
72+
disabled?: boolean;
73+
}>();
74+
75+
const emit = defineEmits<{
76+
upload: [];
77+
}>();
78+
79+
const uploadingFiles = useUploadingFiles();
80+
81+
const textFileData = reactive({
82+
fileName: '',
83+
fileType: TEXT_FILE_TYPES[0]!,
84+
content: '',
85+
});
86+
87+
const buttonDisabled = computed(() => {
88+
const baseDisabled = props.disabled;
89+
const emptyFiles = !uploadingFiles.value.length;
90+
const emptyText = !textFileData.content || !textFileData.fileName;
91+
92+
return baseDisabled || (emptyFiles && emptyText);
93+
});
94+
95+
defineExpose({
96+
textFileData,
97+
});
98+
</script>

0 commit comments

Comments
 (0)