Skip to content

Commit 5b18eb0

Browse files
committed
extract controlbuttons into own component
1 parent ffb41f3 commit 5b18eb0

File tree

2 files changed

+124
-106
lines changed

2 files changed

+124
-106
lines changed

src/App.vue

Lines changed: 2 additions & 106 deletions
Original file line numberDiff line numberDiff line change
@@ -2,77 +2,17 @@
22
import NavBar from './components/NavBar.vue';
33
import FileUpload from './components/FileUpload.vue';
44
import FileItem from './components/FileItem.vue';
5+
import ControlButtons from './components/ControlButtons.vue';
56
import { useFileDataStore } from './stores/fileData';
67
import { storeToRefs } from 'pinia';
78
import type { FileObj } from './types/file';
8-
import { computed, ref, watch, watchEffect } from 'vue';
9-
import { compressFile } from './functions/imageCompression';
10-
import { compressToZip } from './functions/zipCompression';
119
import { useI18n } from './hooks/useI18n';
1210
1311
const { t } = useI18n();
1412
1513
const fileDataStore = useFileDataStore();
1614
const { files } = storeToRefs(fileDataStore);
1715
18-
const getUncompressedFiles = (fileObj: FileObj) => !fileObj.isCompressed;
19-
20-
const isCompressing = ref(false);
21-
const zipData = ref('');
22-
const isZipCompressing = ref(false);
23-
const uncompressedFiles = computed(() => files.value.filter(getUncompressedFiles));
24-
const anyUncompressed = computed(() => Boolean(uncompressedFiles.value.length));
25-
const toCompress = ref<FileObj[]>([]);
26-
27-
const availableThreads = Math.max(navigator.hardwareConcurrency - 2, 1);
28-
29-
async function editFileObj(fileObj: FileObj) {
30-
try {
31-
const compressedFile = await compressFile(fileObj.file);
32-
fileObj.file = compressedFile;
33-
fileObj.isCompressed = true;
34-
fileObj.isError = false;
35-
} catch {
36-
fileObj.isError = true;
37-
}
38-
}
39-
40-
// add files to toCompress array when a thread is available
41-
watchEffect(() => {
42-
if (isCompressing.value) {
43-
const amountToPush = availableThreads - toCompress.value.filter(getUncompressedFiles).length;
44-
const pool = uncompressedFiles.value.filter((item) => !toCompress.value.includes(item)).slice(0, amountToPush);
45-
if (pool.length) toCompress.value = [...toCompress.value, ...pool];
46-
}
47-
});
48-
49-
// compress new files that are added to the toCompress array
50-
watch(toCompress, (newVal, oldVal) => {
51-
const filesToCompress = newVal.filter((item) => !oldVal.includes(item));
52-
filesToCompress.forEach(editFileObj);
53-
});
54-
55-
// set isCompressing to false when compression finished
56-
watchEffect(() => {
57-
if (!uncompressedFiles.value.length && isCompressing.value) {
58-
isCompressing.value = false;
59-
toCompress.value = [];
60-
}
61-
});
62-
63-
// zip files when everything is compressed
64-
watch(isCompressing, async (newVal, oldVal) => {
65-
if (oldVal && !newVal) {
66-
URL.revokeObjectURL(zipData.value);
67-
isZipCompressing.value = true;
68-
zipData.value = await compressToZip();
69-
isZipCompressing.value = false;
70-
}
71-
});
72-
73-
// initiate compression
74-
const compressFiles = () => (isCompressing.value = true);
75-
7616
const removeItem = (file: FileObj) => (files.value = files.value.filter((item) => item !== file));
7717
</script>
7818

@@ -97,32 +37,7 @@ const removeItem = (file: FileObj) => (files.value = files.value.filter((item) =
9737
<FileUpload />
9838

9939
<h2 class="subheading">{{ t('translation.filelist') }}</h2>
100-
<div class="buttons">
101-
<button
102-
:aria-busy="isCompressing"
103-
:class="{ 'is-success': files.length && !anyUncompressed }"
104-
:disabled="!files.length || !anyUncompressed || isCompressing"
105-
@click="compressFiles"
106-
>
107-
{{ files.length && !anyUncompressed ? t('translation.allcompressed') : t('translation.compress') }}
108-
</button>
109-
<a
110-
:aria-busy="isZipCompressing"
111-
:disabled="!zipData || undefined"
112-
:href="zipData || undefined"
113-
role="button"
114-
download
115-
>
116-
{{ t('translation.downloadzip') }}
117-
</a>
118-
<button
119-
:disabled="!files.length"
120-
class="secondary"
121-
@click="files = []"
122-
>
123-
{{ t('translation.clearlist') }}
124-
</button>
125-
</div>
40+
<ControlButtons />
12641
<div class="file-list">
12742
<FileItem
12843
v-for="file in files"
@@ -168,23 +83,4 @@ const removeItem = (file: FileObj) => (files.value = files.value.filter((item) =
16883
gap: 0.25rem;
16984
width: fit-content;
17085
}
171-
172-
.is-success {
173-
background-color: mediumseagreen;
174-
border: 1px solid darkseagreen;
175-
}
176-
177-
.buttons {
178-
display: flex;
179-
gap: 0.5rem;
180-
flex-wrap: wrap;
181-
margin-block-end: 1rem;
182-
183-
& > * {
184-
flex-grow: 1;
185-
width: fit-content;
186-
height: fit-content;
187-
margin: 0;
188-
}
189-
}
19086
</style>

src/components/ControlButtons.vue

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
<script setup lang="ts">
2+
import { compressFile } from '@/functions/imageCompression';
3+
import { compressToZip } from '@/functions/zipCompression';
4+
import { useI18n } from '@/hooks/useI18n';
5+
import { useFileDataStore } from '@/stores/fileData';
6+
import type { FileObj } from '@/types/file';
7+
import { storeToRefs } from 'pinia';
8+
import { ref, computed, watchEffect, watch } from 'vue';
9+
10+
const { t } = useI18n();
11+
12+
const fileDataStore = useFileDataStore();
13+
const { files } = storeToRefs(fileDataStore);
14+
15+
const getUncompressedFiles = (fileObj: FileObj) => !fileObj.isCompressed;
16+
17+
const isCompressing = ref(false);
18+
const zipData = ref('');
19+
const isZipCompressing = ref(false);
20+
const uncompressedFiles = computed(() => files.value.filter(getUncompressedFiles));
21+
const anyUncompressed = computed(() => Boolean(uncompressedFiles.value.length));
22+
const toCompress = ref<FileObj[]>([]);
23+
24+
const availableThreads = Math.max(navigator.hardwareConcurrency - 2, 1);
25+
26+
async function editFileObj(fileObj: FileObj) {
27+
try {
28+
const compressedFile = await compressFile(fileObj.file);
29+
fileObj.file = compressedFile;
30+
fileObj.isCompressed = true;
31+
fileObj.isError = false;
32+
} catch {
33+
fileObj.isError = true;
34+
}
35+
}
36+
37+
// add files to toCompress array when a thread is available
38+
watchEffect(() => {
39+
if (isCompressing.value) {
40+
const amountToPush = availableThreads - toCompress.value.filter(getUncompressedFiles).length;
41+
const pool = uncompressedFiles.value.filter((item) => !toCompress.value.includes(item)).slice(0, amountToPush);
42+
if (pool.length) toCompress.value = [...toCompress.value, ...pool];
43+
}
44+
});
45+
46+
// compress new files that are added to the toCompress array
47+
watch(toCompress, (newVal, oldVal) => {
48+
const filesToCompress = newVal.filter((item) => !oldVal.includes(item));
49+
filesToCompress.forEach(editFileObj);
50+
});
51+
52+
// set isCompressing to false when compression finished
53+
watchEffect(() => {
54+
if (!uncompressedFiles.value.length && isCompressing.value) {
55+
isCompressing.value = false;
56+
toCompress.value = [];
57+
}
58+
});
59+
60+
// zip files when everything is compressed
61+
watch(isCompressing, async (newVal, oldVal) => {
62+
if (oldVal && !newVal) {
63+
URL.revokeObjectURL(zipData.value);
64+
isZipCompressing.value = true;
65+
zipData.value = await compressToZip();
66+
isZipCompressing.value = false;
67+
}
68+
});
69+
70+
// initiate compression
71+
const compressFiles = () => (isCompressing.value = true);
72+
</script>
73+
74+
<template>
75+
<div class="buttons">
76+
<button
77+
:aria-busy="isCompressing"
78+
:class="{ 'is-success': files.length && !anyUncompressed }"
79+
:disabled="!files.length || !anyUncompressed || isCompressing"
80+
@click="compressFiles"
81+
>
82+
{{ files.length && !anyUncompressed ? t('translation.allcompressed') : t('translation.compress') }}
83+
</button>
84+
<a
85+
:aria-busy="isZipCompressing"
86+
:disabled="!zipData || undefined"
87+
:href="zipData || undefined"
88+
role="button"
89+
download
90+
>
91+
{{ t('translation.downloadzip') }}
92+
</a>
93+
<button
94+
:disabled="!files.length"
95+
class="secondary"
96+
@click="files = []"
97+
>
98+
{{ t('translation.clearlist') }}
99+
</button>
100+
</div>
101+
</template>
102+
103+
<style scoped lang="scss">
104+
.is-success {
105+
background-color: mediumseagreen;
106+
border: 1px solid darkseagreen;
107+
}
108+
109+
.buttons {
110+
display: flex;
111+
gap: 0.5rem;
112+
flex-wrap: wrap;
113+
margin-block-end: 1rem;
114+
115+
& > * {
116+
flex-grow: 1;
117+
width: fit-content;
118+
height: fit-content;
119+
margin: 0;
120+
}
121+
}
122+
</style>

0 commit comments

Comments
 (0)