@douyinfe/semi-foundation 2.101.1 → 2.103.0
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/button/button.scss +1 -0
- package/dragMove/foundation.ts +36 -3
- package/lib/cjs/button/button.css +1 -0
- package/lib/cjs/button/button.scss +1 -0
- package/lib/cjs/dragMove/foundation.d.ts +5 -0
- package/lib/cjs/dragMove/foundation.js +27 -3
- package/lib/cjs/progress/progress.css +48 -0
- package/lib/cjs/progress/progress.scss +60 -1
- package/lib/cjs/tree/foundation.js +4 -1
- package/lib/cjs/upload/foundation.d.ts +27 -0
- package/lib/cjs/upload/foundation.js +75 -33
- package/lib/es/button/button.css +1 -0
- package/lib/es/button/button.scss +1 -0
- package/lib/es/dragMove/foundation.d.ts +5 -0
- package/lib/es/dragMove/foundation.js +27 -3
- package/lib/es/progress/progress.css +48 -0
- package/lib/es/progress/progress.scss +60 -1
- package/lib/es/tree/foundation.js +4 -1
- package/lib/es/upload/foundation.d.ts +27 -0
- package/lib/es/upload/foundation.js +75 -33
- package/package.json +4 -4
- package/progress/progress.scss +60 -1
- package/tree/foundation.ts +4 -1
- package/upload/foundation.ts +81 -27
package/tree/foundation.ts
CHANGED
|
@@ -711,7 +711,10 @@ export default class TreeFoundation extends BaseFoundation<TreeAdapter, BasicTre
|
|
|
711
711
|
keyMaps,
|
|
712
712
|
isSearching && showFilteredOnly && filteredShownKeys
|
|
713
713
|
);
|
|
714
|
-
|
|
714
|
+
let motionKeys = this._isAnimated() ? getMotionKeys(eventKey, expandedKeys, keyEntities) : [];
|
|
715
|
+
if (isSearching && showFilteredOnly) {
|
|
716
|
+
motionKeys = motionKeys.filter(key => filteredShownKeys.has(key));
|
|
717
|
+
}
|
|
715
718
|
const newState = {
|
|
716
719
|
[expandedStateKey]: expandedKeys,
|
|
717
720
|
flattenNodes,
|
package/upload/foundation.ts
CHANGED
|
@@ -110,6 +110,45 @@ class UploadFoundation<P = Record<string, any>, S = Record<string, any>> extends
|
|
|
110
110
|
* When paste event is successfully handled, we ignore the subsequent keydown event.
|
|
111
111
|
*/
|
|
112
112
|
_pasteHandled: boolean = false;
|
|
113
|
+
/**
|
|
114
|
+
* Working copy of fileList used during a synchronous startUpload batch.
|
|
115
|
+
* Do NOT rely on React state fileList here: setState is async (batched in React 18),
|
|
116
|
+
* so reading getState('fileList') between iterations of a sync loop returns a stale
|
|
117
|
+
* snapshot and earlier removals get overwritten by later ones (see #3335).
|
|
118
|
+
* Seeded at the start of startUpload and cleared when the sync loop finishes (see
|
|
119
|
+
* try/finally in startUpload); async (promise) results fall back to _latestFileList
|
|
120
|
+
* then React state.
|
|
121
|
+
*/
|
|
122
|
+
_batchFileList: Array<BaseFileItem> | null = null;
|
|
123
|
+
/**
|
|
124
|
+
* The most recent fileList produced by handleBeforeUploadResultInObject.
|
|
125
|
+
* Unlike React state (which is flushed asynchronously under React 18 automatic
|
|
126
|
+
* batching), this is updated synchronously, so a later async beforeUpload result
|
|
127
|
+
* composes with earlier sync removals of the same batch instead of reading a stale
|
|
128
|
+
* snapshot (mixed sync + async beforeUpload results, see #3335).
|
|
129
|
+
*/
|
|
130
|
+
_latestFileList: Array<BaseFileItem> | null = null;
|
|
131
|
+
/**
|
|
132
|
+
* Unify all fileList commits: keep _latestFileList in sync synchronously so async
|
|
133
|
+
* beforeUpload results compose with recent mutations even before React flushes
|
|
134
|
+
* setState (React 18 automatic batching). (#3335)
|
|
135
|
+
*/
|
|
136
|
+
updateFileListInternal(newFileList: Array<BaseFileItem>, callback?: () => void): void {
|
|
137
|
+
if (this._batchFileList) {
|
|
138
|
+
this._batchFileList = newFileList;
|
|
139
|
+
}
|
|
140
|
+
this._latestFileList = newFileList;
|
|
141
|
+
this._adapter.updateFileList(newFileList, callback);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/** Keep the synchronous snapshot aligned with an externally controlled fileList. */
|
|
145
|
+
syncLatestFileList(fileList: Array<BaseFileItem>): void {
|
|
146
|
+
this._latestFileList = fileList.slice();
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
getLatestFileList(): Array<BaseFileItem> {
|
|
150
|
+
return this._batchFileList || this._latestFileList || this.getState('fileList');
|
|
151
|
+
}
|
|
113
152
|
constructor(adapter: UploadAdapter<P, S>) {
|
|
114
153
|
super({ ...adapter });
|
|
115
154
|
}
|
|
@@ -117,6 +156,7 @@ class UploadFoundation<P = Record<string, any>, S = Record<string, any>> extends
|
|
|
117
156
|
init(): void {
|
|
118
157
|
// make sure state reset, otherwise may cause upload abort in React StrictMode, like https://github.com/DouyinFE/semi-design/pull/843
|
|
119
158
|
this.destroyState = false;
|
|
159
|
+
this.syncLatestFileList(this.getState('fileList'));
|
|
120
160
|
// In controlled mode, parent may keep and pass back fileList with blob url.
|
|
121
161
|
// Sync them into internal map so later remove/clear can revoke correctly.
|
|
122
162
|
this._syncLocalUrlsFromFileList();
|
|
@@ -160,6 +200,8 @@ class UploadFoundation<P = Record<string, any>, S = Record<string, any>> extends
|
|
|
160
200
|
if (!disabled) {
|
|
161
201
|
this.unbindPastingHandler();
|
|
162
202
|
}
|
|
203
|
+
this._batchFileList = null;
|
|
204
|
+
this._latestFileList = null;
|
|
163
205
|
this.destroyState = true;
|
|
164
206
|
}
|
|
165
207
|
|
|
@@ -363,7 +405,7 @@ class UploadFoundation<P = Record<string, any>, S = Record<string, any>> extends
|
|
|
363
405
|
}
|
|
364
406
|
newFileList.splice(replaceIdx, 1, newFileItem);
|
|
365
407
|
this._adapter.notifyChange({ currentFile: newFileItem, fileList: newFileList });
|
|
366
|
-
this.
|
|
408
|
+
this.updateFileListInternal(newFileList, () => {
|
|
367
409
|
this._adapter.resetReplaceInput();
|
|
368
410
|
if (!newFileItem._sizeInvalid) {
|
|
369
411
|
this.upload(newFileItem);
|
|
@@ -405,7 +447,7 @@ class UploadFoundation<P = Record<string, any>, S = Record<string, any>> extends
|
|
|
405
447
|
const { uploadTrigger } = this.getProps();
|
|
406
448
|
const currentFiles = files.map(item => this.buildFileItem(item, uploadTrigger));
|
|
407
449
|
this._adapter.notifyChange({ fileList: currentFiles, currentFile: currentFiles[0] });
|
|
408
|
-
this.
|
|
450
|
+
this.updateFileListInternal(currentFiles, () => {
|
|
409
451
|
if (uploadTrigger === TRIGGER_AUTO) {
|
|
410
452
|
this.startUpload(currentFiles);
|
|
411
453
|
}
|
|
@@ -425,7 +467,7 @@ class UploadFoundation<P = Record<string, any>, S = Record<string, any>> extends
|
|
|
425
467
|
this._adapter.notifyChange({ fileList, currentFile: file });
|
|
426
468
|
}
|
|
427
469
|
});
|
|
428
|
-
this.
|
|
470
|
+
this.updateFileListInternal(fileList, () => {
|
|
429
471
|
if (uploadTrigger === TRIGGER_AUTO) {
|
|
430
472
|
this.startUpload(currentFiles);
|
|
431
473
|
}
|
|
@@ -506,7 +548,7 @@ class UploadFoundation<P = Record<string, any>, S = Record<string, any>> extends
|
|
|
506
548
|
|
|
507
549
|
this._adapter.notifyFileSelect(currentFileList);
|
|
508
550
|
this._adapter.notifyChange({ fileList: newFileList, currentFile: null });
|
|
509
|
-
this.
|
|
551
|
+
this.updateFileListInternal(newFileList, () => {
|
|
510
552
|
if (uploadTrigger === TRIGGER_AUTO) {
|
|
511
553
|
this.startUpload(fileItemList);
|
|
512
554
|
}
|
|
@@ -516,16 +558,25 @@ class UploadFoundation<P = Record<string, any>, S = Record<string, any>> extends
|
|
|
516
558
|
/* istanbul ignore next */
|
|
517
559
|
manualUpload(): void {
|
|
518
560
|
// find the list of files that have not been uploaded
|
|
519
|
-
const waitToUploadFileList = this.
|
|
561
|
+
const waitToUploadFileList = this.getLatestFileList().filter((item: BaseFileItem) => item.status === FILE_STATUS_WAIT_UPLOAD);
|
|
520
562
|
this.startUpload(waitToUploadFileList);
|
|
521
563
|
}
|
|
522
564
|
|
|
523
565
|
startUpload(fileList: Array<BaseFileItem>): void {
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
566
|
+
// Seed a working copy so multiple synchronous beforeUpload results
|
|
567
|
+
// (e.g. several files returning autoRemove) compose correctly instead of each
|
|
568
|
+
// reading a stale React state snapshot and overwriting earlier removals. (#3335)
|
|
569
|
+
// try/finally guarantees the working copy is cleared even if beforeUpload throws.
|
|
570
|
+
this._batchFileList = this.getLatestFileList().slice();
|
|
571
|
+
try {
|
|
572
|
+
fileList.forEach(file => {
|
|
573
|
+
if (!file._sizeInvalid) {
|
|
574
|
+
this.upload(file);
|
|
575
|
+
}
|
|
576
|
+
});
|
|
577
|
+
} finally {
|
|
578
|
+
this._batchFileList = null;
|
|
579
|
+
}
|
|
529
580
|
}
|
|
530
581
|
|
|
531
582
|
upload(file: BaseFileItem): void {
|
|
@@ -535,7 +586,7 @@ class UploadFoundation<P = Record<string, any>, S = Record<string, any>> extends
|
|
|
535
586
|
return;
|
|
536
587
|
}
|
|
537
588
|
if (typeof beforeUpload === 'function') {
|
|
538
|
-
const
|
|
589
|
+
const fileList = this.getLatestFileList();
|
|
539
590
|
const buResult = this._adapter.notifyBeforeUpload({ file, fileList });
|
|
540
591
|
switch (true) {
|
|
541
592
|
// sync validate - boolean
|
|
@@ -584,7 +635,11 @@ class UploadFoundation<P = Record<string, any>, S = Record<string, any>> extends
|
|
|
584
635
|
// handle beforeUpload result when it's an object
|
|
585
636
|
handleBeforeUploadResultInObject(buResult: Partial<BeforeUploadObjectResult>, file: BaseFileItem): void {
|
|
586
637
|
const { shouldUpload, status, autoRemove, validateMessage, fileInstance } = buResult;
|
|
587
|
-
|
|
638
|
+
// During a synchronous startUpload batch, read from the working copy; for async
|
|
639
|
+
// (promise) results read from the most recent synchronous snapshot, falling back
|
|
640
|
+
// to React state. This keeps mixed sync + async beforeUpload results composing
|
|
641
|
+
// correctly under React 18 automatic batching. (#3335)
|
|
642
|
+
let newFileList: Array<BaseFileItem> = this.getLatestFileList().slice();
|
|
588
643
|
if (autoRemove) {
|
|
589
644
|
this._releaseFileUrl(file.uid);
|
|
590
645
|
newFileList = newFileList.filter(item => item.uid !== file.uid);
|
|
@@ -607,7 +662,7 @@ class UploadFoundation<P = Record<string, any>, S = Record<string, any>> extends
|
|
|
607
662
|
newFileList[index].shouldUpload = shouldUpload;
|
|
608
663
|
}
|
|
609
664
|
|
|
610
|
-
this.
|
|
665
|
+
this.updateFileListInternal(newFileList);
|
|
611
666
|
this._adapter.notifyChange({ fileList: newFileList, currentFile: file });
|
|
612
667
|
|
|
613
668
|
if (shouldUpload) {
|
|
@@ -703,8 +758,7 @@ class UploadFoundation<P = Record<string, any>, S = Record<string, any>> extends
|
|
|
703
758
|
}
|
|
704
759
|
|
|
705
760
|
handleProgress({ e, fileInstance }: { e?: ProgressEvent; fileInstance: File }): void {
|
|
706
|
-
const
|
|
707
|
-
const newFileList = fileList.slice();
|
|
761
|
+
const newFileList = this.getLatestFileList().slice();
|
|
708
762
|
let percent = 0;
|
|
709
763
|
if (e.total > 0) {
|
|
710
764
|
percent = Number(((e.loaded / e.total) * 100 * numbers.PROGRESS_COEFFICIENT).toFixed(0)) || 0;
|
|
@@ -714,15 +768,15 @@ class UploadFoundation<P = Record<string, any>, S = Record<string, any>> extends
|
|
|
714
768
|
return;
|
|
715
769
|
}
|
|
716
770
|
newFileList[index].percent = percent;
|
|
717
|
-
newFileList[index].status = FILE_STATUS_UPLOADING;
|
|
771
|
+
newFileList[index].status = FILE_STATUS_UPLOADING as FileItemStatus;
|
|
718
772
|
|
|
719
773
|
this._adapter.notifyProgress(percent, fileInstance, newFileList);
|
|
720
|
-
this.
|
|
774
|
+
this.updateFileListInternal(newFileList);
|
|
721
775
|
this._adapter.notifyChange({ fileList: newFileList, currentFile: newFileList[index] });
|
|
722
776
|
}
|
|
723
777
|
|
|
724
778
|
handleOnLoad({ e, xhr, fileInstance }: { e?: ProgressEvent; xhr: XMLHttpRequest; fileInstance: File }): void {
|
|
725
|
-
const
|
|
779
|
+
const fileList = this.getLatestFileList();
|
|
726
780
|
const index = this._getFileIndex(fileInstance, fileList);
|
|
727
781
|
if (index < 0) {
|
|
728
782
|
return;
|
|
@@ -735,7 +789,7 @@ class UploadFoundation<P = Record<string, any>, S = Record<string, any>> extends
|
|
|
735
789
|
}
|
|
736
790
|
|
|
737
791
|
handleSuccess({ e, fileInstance, isCustomRequest = false, xhr, response }: { e?: ProgressEvent; fileInstance: CustomFile; isCustomRequest?: boolean; xhr?: XMLHttpRequest; response?: any }): void {
|
|
738
|
-
const
|
|
792
|
+
const fileList = this.getLatestFileList();
|
|
739
793
|
let body: any = null;
|
|
740
794
|
const index = this._getFileIndex(fileInstance, fileList);
|
|
741
795
|
if (index < 0) {
|
|
@@ -750,7 +804,7 @@ class UploadFoundation<P = Record<string, any>, S = Record<string, any>> extends
|
|
|
750
804
|
const newFileList = fileList.slice();
|
|
751
805
|
const { afterUpload } = this.getProps();
|
|
752
806
|
|
|
753
|
-
newFileList[index].status = FILE_STATUS_SUCCESS;
|
|
807
|
+
newFileList[index].status = FILE_STATUS_SUCCESS as FileItemStatus;
|
|
754
808
|
newFileList[index].percent = 100;
|
|
755
809
|
this._adapter.notifyProgress(100, fileInstance, newFileList);
|
|
756
810
|
newFileList[index].response = body;
|
|
@@ -763,7 +817,7 @@ class UploadFoundation<P = Record<string, any>, S = Record<string, any>> extends
|
|
|
763
817
|
file: newFileList[index],
|
|
764
818
|
fileList: newFileList,
|
|
765
819
|
}) || {};
|
|
766
|
-
status ? (newFileList[index].status = status) : null;
|
|
820
|
+
status ? (newFileList[index].status = status as FileItemStatus) : null;
|
|
767
821
|
validateMessage ? (newFileList[index].validateMessage = validateMessage) : null;
|
|
768
822
|
name ? (newFileList[index].name = name) : null;
|
|
769
823
|
if (url) {
|
|
@@ -778,7 +832,7 @@ class UploadFoundation<P = Record<string, any>, S = Record<string, any>> extends
|
|
|
778
832
|
}
|
|
779
833
|
this._adapter.notifySuccess(body, fileInstance, newFileList);
|
|
780
834
|
this._adapter.notifyChange({ fileList: newFileList, currentFile: newFileList[index] });
|
|
781
|
-
this.
|
|
835
|
+
this.updateFileListInternal(newFileList);
|
|
782
836
|
}
|
|
783
837
|
|
|
784
838
|
_getFileIndex(file: CustomFile | BaseFileItem, fileList: Array<BaseFileItem>): number {
|
|
@@ -808,13 +862,13 @@ class UploadFoundation<P = Record<string, any>, S = Record<string, any>> extends
|
|
|
808
862
|
this._releaseFileUrl(file.uid);
|
|
809
863
|
|
|
810
864
|
this._adapter.notifyRemove(file.fileInstance, newFileList, file);
|
|
811
|
-
this.
|
|
865
|
+
this.updateFileListInternal(newFileList);
|
|
812
866
|
this._adapter.notifyChange({ fileList: newFileList, currentFile: file });
|
|
813
867
|
});
|
|
814
868
|
}
|
|
815
869
|
|
|
816
870
|
handleError({ e, xhr, fileInstance }: { e?: ProgressEvent;xhr: XMLHttpRequest;fileInstance: CustomFile }): void {
|
|
817
|
-
const
|
|
871
|
+
const fileList = this.getLatestFileList();
|
|
818
872
|
const index = this._getFileIndex(fileInstance, fileList);
|
|
819
873
|
if (index < 0) {
|
|
820
874
|
return;
|
|
@@ -823,12 +877,12 @@ class UploadFoundation<P = Record<string, any>, S = Record<string, any>> extends
|
|
|
823
877
|
const newFileList = fileList.slice();
|
|
824
878
|
const error = this.getError({ action, xhr, fileName: fileInstance.name });
|
|
825
879
|
|
|
826
|
-
newFileList[index].status = FILE_STATUS_UPLOAD_FAIL;
|
|
880
|
+
newFileList[index].status = FILE_STATUS_UPLOAD_FAIL as FileItemStatus;
|
|
827
881
|
newFileList[index].response = error;
|
|
828
882
|
newFileList[index].event = e;
|
|
829
883
|
|
|
830
884
|
this._adapter.notifyError(error, fileInstance, newFileList, xhr);
|
|
831
|
-
this.
|
|
885
|
+
this.updateFileListInternal(newFileList);
|
|
832
886
|
this._adapter.notifyChange({ currentFile: newFileList[index], fileList: newFileList });
|
|
833
887
|
}
|
|
834
888
|
|
|
@@ -844,7 +898,7 @@ class UploadFoundation<P = Record<string, any>, S = Record<string, any>> extends
|
|
|
844
898
|
return;
|
|
845
899
|
}
|
|
846
900
|
this._releaseAllFileUrls();
|
|
847
|
-
this.
|
|
901
|
+
this.updateFileListInternal([]);
|
|
848
902
|
this._adapter.notifyClear();
|
|
849
903
|
this._adapter.notifyChange({ fileList: [] } as any);
|
|
850
904
|
}).catch(error => {
|