Skip to content

Commit 8cc8f88

Browse files
committed
fix: file input validation
1 parent b8cf42d commit 8cc8f88

File tree

9 files changed

+57
-24
lines changed

9 files changed

+57
-24
lines changed

packages/components-vue/src/components/form/Simple.stories.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,12 @@ const inputs: FormInput[] = [
5252
name: "nullable",
5353
title: "Is this field optional in the offer?",
5454
}),
55+
new FormInput({
56+
name: "logo",
57+
type: eFormType.FILE,
58+
title: "Business logo",
59+
max: 3,
60+
}),
5561
];
5662

5763
const meta = {

packages/components-vue/src/components/form/Simple.vue

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,11 +36,11 @@
3636
</LoaderContentFetch>
3737
<slot v-else>
3838
<!-- No inputs given -->
39-
<BoxMessage :theme="theme" class="--width">
39+
<BaseBox class="--width" :theme="theme" button dashed transparent>
4040
<div class="flx --flxRow --flx-center">
4141
<span>{{ emptyMessage || t("nothing_to_show") }}</span>
4242
</div>
43-
</BoxMessage>
43+
</BaseBox>
4444
</slot>
4545
</BaseErrorBoundary>
4646
</template>
@@ -54,7 +54,7 @@
5454
import { type FormInput as FormInputClass, useI18n } from "@open-xamu-co/ui-common-helpers";
5555
5656
import BaseErrorBoundary from "../base/ErrorBoundary.vue";
57-
import BoxMessage from "../box/Message.vue";
57+
import BaseBox from "../base/Box.vue";
5858
import FormInput from "./Input.vue";
5959
import LoaderContentFetch from "../loader/ContentFetch.vue";
6060

packages/components-vue/src/components/form/Stages.vue

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,8 @@
157157
* submit fn
158158
*/
159159
submitFn?: (values: FormInputClass[], event?: Event) => Promise<boolean | iInvalidInput[]>;
160+
/** Perform additional actions if submit succeds */
161+
successFn?: () => void;
160162
/**
161163
* Omit requiring filling up the form
162164
*/
@@ -213,7 +215,11 @@
213215
if (Array.isArray(successOrInvalid)) invalid.value = successOrInvalid;
214216
else {
215217
emit("submited", successOrInvalid);
216-
resetStages(props.stages); // reset form
218+
219+
if (successOrInvalid) {
220+
resetStages(props.stages); // reset form
221+
props.successFn?.();
222+
}
217223
}
218224
});
219225

packages/components-vue/src/components/input/File.vue

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<template>
22
<BaseBox
3-
class="flx --flxColumn --flx-start-stretch --gap-10"
3+
class="flx --flxColumn --flx-start-stretch --gap-10 --width"
44
button
55
v-bind="{ ...props, theme: fileInputTheme.themeValues }"
66
>
@@ -32,7 +32,7 @@
3232
<span class="--txtWrap-nowrap">
3333
{{
3434
t("file_one_of_amount", {
35-
count: modelValue.length,
35+
count: thumbnails.length,
3636
amount: maxAmount,
3737
})
3838
}}
@@ -54,7 +54,7 @@
5454
>
5555
<template v-if="!isLoading">
5656
<BaseBox
57-
v-if="modelValue.length < maxAmount"
57+
v-if="thumbnails.length < maxAmount"
5858
:for="id"
5959
class="flx --flxColumn --flx-center --minHeight-90"
6060
el="label"
@@ -245,7 +245,7 @@
245245
isLoading.value = true;
246246
247247
// copy the files
248-
const savedFiles = [...props.modelValue];
248+
const savedFiles = [...props.modelValue].filter((v) => v instanceof File);
249249
const savedThumbs = [...thumbnails.value];
250250
251251
try {
@@ -269,7 +269,14 @@
269269
const isImage = await fileMatchesMimeTypes(files[i], standardImageMimeTypes);
270270
271271
// 50MB max file size
272-
if (isImage) {
272+
if (!isImage) {
273+
// not image
274+
Swal.fire({
275+
title: t("swal.file_wrong_format_image"),
276+
text: t("swal.file_wrong_format_image_text"),
277+
icon: "warning",
278+
});
279+
} else {
273280
// is image file
274281
if (files[i].size < maxFileSize.value) {
275282
const fileName = `${props.filePrefix ?? "image"}_${i}`;
@@ -284,13 +291,6 @@
284291
icon: "warning",
285292
});
286293
}
287-
} else {
288-
// not image
289-
Swal.fire({
290-
title: t("swal.file_wrong_format_image"),
291-
text: t("swal.file_wrong_format_image_text"),
292-
icon: "warning",
293-
});
294294
}
295295
}
296296

packages/components-vue/src/components/loader/ContentFetch.vue

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -154,12 +154,6 @@
154154
loading.value = false;
155155
}
156156
157-
// lifecycle
158-
if (!props.preventAutoload) refresh();
159-
160-
// Allow the parent to manually force an update
161-
emit("refresh", refresh);
162-
163157
function validatePromiseLike(newPromise: any, oldPromise: any) {
164158
/**
165159
* The same promise would trigger the watcher
@@ -174,7 +168,13 @@
174168
if (!loading.value && !!newPromise) refresh();
175169
}
176170
177-
// lifecycle, refetch on url or promise change
171+
// lifecycle
172+
if (!props.preventAutoload) refresh();
173+
174+
// Allow the parent to manually force an update
175+
emit("refresh", refresh);
176+
177+
// refetch on url or promise change
178178
watch(
179179
() => props.url,
180180
(newUrl, oldUrl) => {

packages/components-vue/src/components/pagination/Content.vue

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
:promise="patchedPromise"
55
:payload="[{ ...pagination, ...defaults }]"
66
v-bind="{ ...$attrs, preventAutoload, theme, noContentMessage, label, isContent }"
7+
@refresh="$emit('refresh', $event)"
78
>
89
<slot
910
v-bind="{
@@ -92,6 +93,8 @@
9293
9394
const props = defineProps<iPCProps<T, C> | iPCWithTransformProps<T, C, R>>();
9495
96+
defineEmits(["refresh"]);
97+
9598
const xamuOptions = inject<iPluginOptions>("xamu");
9699
const router = getCurrentInstance()?.appContext.config.globalProperties.$router;
97100

packages/components-vue/src/components/table/Simple.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -529,7 +529,7 @@
529529
const options = (props.properties || []).map(toOption);
530530
const property = toOption(options.find((p) => p.value === key) || key);
531531
const aliasKey = _.snakeCase(key);
532-
const canSort = typeof value === "string" || typeof value === "number";
532+
const canSort = ["string", "number", "boolean"].includes(typeof value);
533533
534534
return {
535535
...property,

packages/styles/src/layouts/_scroll.scss

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,20 @@
4343
&.#{utils.$prefix-default}--always {
4444
max-width: min(100%, 100vw);
4545
overflow: auto hidden;
46+
box-sizing: content-box;
47+
48+
@media (max-width: utils.$querie-tablet) {
49+
&:has(:only-child) {
50+
width: calc(min(100%, 100vw) + 2rem);
51+
max-width: none;
52+
margin: 0 -1rem;
53+
54+
> :only-child {
55+
border-right: 1rem solid transparent;
56+
border-left: 1rem solid transparent;
57+
}
58+
}
59+
}
4660

4761
> * {
4862
min-width: 100%;

packages/styles/src/layouts/_table.scss

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,10 @@
5959
padding: 0.4rem 1rem;
6060
transition: color 0.1s ease-out;
6161
font-weight: utils.weight();
62+
63+
@media (max-width: utils.$querie-tablet) {
64+
padding: 0.4rem 0.6667rem;
65+
}
6266
}
6367

6468
td {

0 commit comments

Comments
 (0)