Skip to content

Commit b9122be

Browse files
committed
[Feat]: #2099 add clearValueAt + one unified state
1 parent ff471ee commit b9122be

File tree

3 files changed

+64
-28
lines changed

3 files changed

+64
-28
lines changed

client/packages/lowcoder/src/comps/comps/fileComp/draggerUpload.tsx

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { default as AntdUpload } from "antd/es/upload";
22
import { default as Button } from "antd/es/button";
33
import { UploadFile, UploadChangeParam, UploadFileStatus, RcFile } from "antd/es/upload/interface";
4-
import { useState, useEffect } from "react";
4+
import { useState, useMemo } from "react";
55
import styled, { css } from "styled-components";
66
import { trans } from "i18n";
77
import _ from "lodash";
@@ -162,25 +162,27 @@ interface DraggerUploadProps {
162162

163163
export const DraggerUpload = (props: DraggerUploadProps) => {
164164
const { dispatch, files, style, autoHeight, animationStyle } = props;
165-
const [fileList, setFileList] = useState<UploadFile[]>(
166-
files.map((f) => ({ ...f, status: "done" })) as UploadFile[]
167-
);
165+
// Track only files currently being uploaded (not yet in props.files)
166+
const [uploadingFiles, setUploadingFiles] = useState<UploadFile[]>([]);
168167
const [showModal, setShowModal] = useState(false);
169168
const isMobile = checkIsMobile(window.innerWidth);
170169

171-
useEffect(() => {
172-
if (files.length === 0 && fileList.length !== 0) {
173-
setFileList([]);
174-
}
175-
}, [files]);
170+
// Derive fileList from props.files (source of truth) + currently uploading files
171+
const fileList = useMemo<UploadFile[]>(() => [
172+
...(files.map((f) => ({ ...f, status: "done" as const })) as UploadFile[]),
173+
...uploadingFiles,
174+
], [files, uploadingFiles]);
176175

177176
const handleOnChange = (param: UploadChangeParam) => {
178-
const uploadingFiles = param.fileList.filter((f) => f.status === "uploading");
179-
if (uploadingFiles.length !== 0) {
180-
setFileList(param.fileList);
177+
const currentlyUploading = param.fileList.filter((f) => f.status === "uploading");
178+
if (currentlyUploading.length !== 0) {
179+
setUploadingFiles(currentlyUploading);
181180
return;
182181
}
183182

183+
// Clear uploading state when all uploads complete
184+
setUploadingFiles([]);
185+
184186
let maxFiles = props.maxFiles;
185187
if (props.uploadType === "single") {
186188
maxFiles = 1;
@@ -240,8 +242,6 @@ export const DraggerUpload = (props: DraggerUploadProps) => {
240242
props.onEvent("parse");
241243
});
242244
}
243-
244-
setFileList(uploadedFiles.slice(-maxFiles));
245245
};
246246

247247
return (

client/packages/lowcoder/src/comps/comps/fileComp/fileComp.tsx

Lines changed: 49 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ import {
2424
RecordConstructorToView,
2525
} from "lowcoder-core";
2626
import { UploadRequestOption } from "rc-upload/lib/interface";
27-
import { Suspense, useCallback, useEffect, useRef, useState } from "react";
27+
import { Suspense, useCallback, useMemo, useRef, useState } from "react";
2828
import styled, { css } from "styled-components";
2929
import { JSONObject, JSONValue } from "../../../util/jsonTypes";
3030
import { BoolControl, BoolPureControl } from "../../controls/boolControl";
@@ -265,29 +265,32 @@ const Upload = (
265265
},
266266
) => {
267267
const { dispatch, files, style } = props;
268-
const [fileList, setFileList] = useState<UploadFile[]>(
269-
files.map((f) => ({ ...f, status: "done" })) as UploadFile[]
270-
);
268+
// Track only files currently being uploaded (not yet in props.files)
269+
const [uploadingFiles, setUploadingFiles] = useState<UploadFile[]>([]);
271270
const [showModal, setShowModal] = useState(false);
272271
const isMobile = checkIsMobile(window.innerWidth);
273272

274-
useEffect(() => {
275-
if (files.length === 0 && fileList.length !== 0) {
276-
setFileList([]);
277-
}
278-
}, [files]);
273+
// Derive fileList from props.files (source of truth) + currently uploading files
274+
const fileList = useMemo<UploadFile[]>(() => [
275+
...(files.map((f) => ({ ...f, status: "done" as const })) as UploadFile[]),
276+
...uploadingFiles,
277+
], [files, uploadingFiles]);
278+
279279
// chrome86 bug: button children should not contain only empty span
280280
const hasChildren = hasIcon(props.prefixIcon) || !!props.text || hasIcon(props.suffixIcon);
281281

282282
const handleOnChange = (param: UploadChangeParam) => {
283-
const uploadingFiles = param.fileList.filter((f) => f.status === "uploading");
283+
const currentlyUploading = param.fileList.filter((f) => f.status === "uploading");
284284
// the onChange callback will be executed when the state of the antd upload file changes.
285285
// so make a trick logic: the file list with loading will not be processed
286-
if (uploadingFiles.length !== 0) {
287-
setFileList(param.fileList);
286+
if (currentlyUploading.length !== 0) {
287+
setUploadingFiles(currentlyUploading);
288288
return;
289289
}
290290

291+
// Clear uploading state when all uploads complete
292+
setUploadingFiles([]);
293+
291294
let maxFiles = props.maxFiles;
292295
if (props.uploadType === "single") {
293296
maxFiles = 1;
@@ -348,8 +351,6 @@ const Upload = (
348351
props.onEvent("parse");
349352
});
350353
}
351-
352-
setFileList(uploadedFiles.slice(-maxFiles));
353354
};
354355

355356
return (
@@ -552,6 +553,40 @@ const FileWithMethods = withMethodExposing(FileImplComp, [
552553
})
553554
),
554555
},
556+
{
557+
method: {
558+
name: "clearValueAt",
559+
description: trans("file.clearValueAtDesc"),
560+
params: [{ name: "index", type: "number" }],
561+
},
562+
execute: (comp, params) => {
563+
const index = params[0] as number;
564+
const value = comp.children.value.getView();
565+
const files = comp.children.files.getView();
566+
const parsedValue = comp.children.parsedValue.getView();
567+
568+
if (index < 0 || index >= files.length) {
569+
return;
570+
}
571+
572+
comp.dispatch(
573+
multiChangeAction({
574+
value: changeValueAction(
575+
[...value.slice(0, index), ...value.slice(index + 1)],
576+
false
577+
),
578+
files: changeValueAction(
579+
[...files.slice(0, index), ...files.slice(index + 1)],
580+
false
581+
),
582+
parsedValue: changeValueAction(
583+
[...parsedValue.slice(0, index), ...parsedValue.slice(index + 1)],
584+
false
585+
),
586+
})
587+
);
588+
},
589+
},
555590
]);
556591

557592
export const FileComp = withExposingConfigs(FileWithMethods, [

client/packages/lowcoder/src/i18n/locales/en.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1934,6 +1934,7 @@ export const en = {
19341934
"filesValueDesc": "The Contents of the Currently Uploaded File Are Base64 Encoded",
19351935
"filesDesc": "List of the Current Uploaded Files. For Details, Refer to",
19361936
"clearValueDesc": "Clear All Files",
1937+
"clearValueAtDesc": "Clear File at Index",
19371938
"parseFiles": "Parse Files",
19381939
"parsedValueTooltip1": "If parseFiles Is True, Upload Files Will Parse to Object, Array, or String. Parsed Data Can Be Accessed via the parsedValue Array.",
19391940
"parsedValueTooltip2": "Supports Excel, JSON, CSV, and Text Files. Other Formats Will Return Null.",

0 commit comments

Comments
 (0)