@douyinfe/semi-foundation 2.102.0 → 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/lib/cjs/progress/progress.css +48 -0
- package/lib/cjs/progress/progress.scss +60 -1
- package/lib/cjs/upload/foundation.d.ts +27 -0
- package/lib/cjs/upload/foundation.js +75 -33
- package/lib/es/progress/progress.css +48 -0
- package/lib/es/progress/progress.scss +60 -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/upload/foundation.ts +81 -27
|
@@ -20,6 +20,7 @@
|
|
|
20
20
|
.semi-progress-horizontal .semi-progress-track {
|
|
21
21
|
height: 100%;
|
|
22
22
|
width: 100%;
|
|
23
|
+
overflow: hidden;
|
|
23
24
|
}
|
|
24
25
|
.semi-progress-horizontal .semi-progress-track-inner {
|
|
25
26
|
height: 100%;
|
|
@@ -28,6 +29,11 @@
|
|
|
28
29
|
transition: width 0.3s;
|
|
29
30
|
transition-timing-function: cubic-bezier(0.62, 0.05, 0.36, 0.95);
|
|
30
31
|
}
|
|
32
|
+
.semi-progress-horizontal .semi-progress-track-inner-indeterminate {
|
|
33
|
+
width: 35%;
|
|
34
|
+
transition: none;
|
|
35
|
+
animation: semi-progress-indeterminate-slide 1.6s ease-in-out infinite;
|
|
36
|
+
}
|
|
31
37
|
.semi-progress-horizontal .semi-progress-line-text {
|
|
32
38
|
min-width: 45px;
|
|
33
39
|
font-weight: 600;
|
|
@@ -48,6 +54,7 @@
|
|
|
48
54
|
.semi-progress-vertical .semi-progress-track {
|
|
49
55
|
height: 100%;
|
|
50
56
|
width: 100%;
|
|
57
|
+
overflow: hidden;
|
|
51
58
|
}
|
|
52
59
|
.semi-progress-vertical .semi-progress-track-inner {
|
|
53
60
|
background-color: var(--semi-color-success);
|
|
@@ -56,6 +63,12 @@
|
|
|
56
63
|
transition: height 0.3s;
|
|
57
64
|
transition-timing-function: cubic-bezier(0.62, 0.05, 0.36, 0.95);
|
|
58
65
|
}
|
|
66
|
+
.semi-progress-vertical .semi-progress-track-inner-indeterminate {
|
|
67
|
+
height: 35%;
|
|
68
|
+
width: 100%;
|
|
69
|
+
transition: none;
|
|
70
|
+
animation: semi-progress-indeterminate-slide-vertical 1.6s ease-in-out infinite;
|
|
71
|
+
}
|
|
59
72
|
.semi-progress-vertical .semi-progress-line-text {
|
|
60
73
|
font-weight: 600;
|
|
61
74
|
margin-top: 8px;
|
|
@@ -77,6 +90,11 @@
|
|
|
77
90
|
transform-origin: 50% 50%;
|
|
78
91
|
stroke: var(--semi-color-success);
|
|
79
92
|
}
|
|
93
|
+
.semi-progress-circle-ring-inner-indeterminate {
|
|
94
|
+
transform: rotate(0deg);
|
|
95
|
+
transform-origin: 50% 50%;
|
|
96
|
+
animation: semi-progress-indeterminate-rotate 1.2s linear infinite;
|
|
97
|
+
}
|
|
80
98
|
.semi-progress-circle-text {
|
|
81
99
|
position: absolute;
|
|
82
100
|
top: 50%;
|
|
@@ -88,6 +106,36 @@
|
|
|
88
106
|
color: var(--semi-color-mode-minor-text);
|
|
89
107
|
}
|
|
90
108
|
|
|
109
|
+
@keyframes semi-progress-indeterminate-slide {
|
|
110
|
+
0% {
|
|
111
|
+
transform: translateX(-120%);
|
|
112
|
+
}
|
|
113
|
+
55% {
|
|
114
|
+
transform: translateX(300%);
|
|
115
|
+
}
|
|
116
|
+
100% {
|
|
117
|
+
transform: translateX(300%);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
@keyframes semi-progress-indeterminate-slide-vertical {
|
|
121
|
+
0% {
|
|
122
|
+
transform: translateY(-120%);
|
|
123
|
+
}
|
|
124
|
+
55% {
|
|
125
|
+
transform: translateY(300%);
|
|
126
|
+
}
|
|
127
|
+
100% {
|
|
128
|
+
transform: translateY(300%);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
@keyframes semi-progress-indeterminate-rotate {
|
|
132
|
+
from {
|
|
133
|
+
transform: rotate(0deg);
|
|
134
|
+
}
|
|
135
|
+
to {
|
|
136
|
+
transform: rotate(360deg);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
91
139
|
.semi-rtl .semi-progress,
|
|
92
140
|
.semi-portal-rtl .semi-progress {
|
|
93
141
|
direction: rtl;
|
|
@@ -23,6 +23,7 @@ $module: #{$prefix}-progress;
|
|
|
23
23
|
.#{$module}-track {
|
|
24
24
|
height: 100%;
|
|
25
25
|
width: 100%;
|
|
26
|
+
overflow: hidden;
|
|
26
27
|
}
|
|
27
28
|
|
|
28
29
|
.#{$module}-track-inner {
|
|
@@ -33,6 +34,12 @@ $module: #{$prefix}-progress;
|
|
|
33
34
|
transition-timing-function: $motion-progress-transition_timing_function;
|
|
34
35
|
}
|
|
35
36
|
|
|
37
|
+
.#{$module}-track-inner-indeterminate {
|
|
38
|
+
width: 35%;
|
|
39
|
+
transition: none;
|
|
40
|
+
animation: semi-progress-indeterminate-slide 1.6s ease-in-out infinite;
|
|
41
|
+
}
|
|
42
|
+
|
|
36
43
|
.#{$module}-line-text {
|
|
37
44
|
min-width: $width-progress_line_text;
|
|
38
45
|
font-weight: $font-progress_line_text-fontWeight;
|
|
@@ -57,6 +64,7 @@ $module: #{$prefix}-progress;
|
|
|
57
64
|
.#{$module}-track {
|
|
58
65
|
height: 100%;
|
|
59
66
|
width: 100%;
|
|
67
|
+
overflow: hidden;
|
|
60
68
|
}
|
|
61
69
|
|
|
62
70
|
.#{$module}-track-inner {
|
|
@@ -67,6 +75,13 @@ $module: #{$prefix}-progress;
|
|
|
67
75
|
transition-timing-function: $motion-progress-transition_timing_function;
|
|
68
76
|
}
|
|
69
77
|
|
|
78
|
+
.#{$module}-track-inner-indeterminate {
|
|
79
|
+
height: 35%;
|
|
80
|
+
width: 100%;
|
|
81
|
+
transition: none;
|
|
82
|
+
animation: semi-progress-indeterminate-slide-vertical 1.6s ease-in-out infinite;
|
|
83
|
+
}
|
|
84
|
+
|
|
70
85
|
.#{$module}-line-text {
|
|
71
86
|
font-weight: $font-progress_line_text-fontWeight;
|
|
72
87
|
margin-top: $spacing-progress_vertical_line_text-marginTop;
|
|
@@ -95,6 +110,12 @@ $module: #{$prefix}-progress;
|
|
|
95
110
|
stroke: $color-progress_track_inner-bg;
|
|
96
111
|
}
|
|
97
112
|
|
|
113
|
+
&-ring-inner-indeterminate {
|
|
114
|
+
transform: rotate(0deg);
|
|
115
|
+
transform-origin: 50% 50%;
|
|
116
|
+
animation: semi-progress-indeterminate-rotate 1.2s linear infinite;
|
|
117
|
+
}
|
|
118
|
+
|
|
98
119
|
&-text {
|
|
99
120
|
position: absolute;
|
|
100
121
|
top: 50%;
|
|
@@ -108,4 +129,42 @@ $module: #{$prefix}-progress;
|
|
|
108
129
|
}
|
|
109
130
|
}
|
|
110
131
|
|
|
111
|
-
@
|
|
132
|
+
@keyframes semi-progress-indeterminate-slide {
|
|
133
|
+
0% {
|
|
134
|
+
transform: translateX(-120%);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
55% {
|
|
138
|
+
transform: translateX(300%);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
100% {
|
|
142
|
+
transform: translateX(300%);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
@keyframes semi-progress-indeterminate-slide-vertical {
|
|
147
|
+
0% {
|
|
148
|
+
transform: translateY(-120%);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
55% {
|
|
152
|
+
transform: translateY(300%);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
100% {
|
|
156
|
+
transform: translateY(300%);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
@keyframes semi-progress-indeterminate-rotate {
|
|
161
|
+
from {
|
|
162
|
+
transform: rotate(0deg);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
to {
|
|
166
|
+
transform: rotate(360deg);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
@import './rtl.scss';
|
|
@@ -94,6 +94,33 @@ declare class UploadFoundation<P = Record<string, any>, S = Record<string, any>>
|
|
|
94
94
|
* When paste event is successfully handled, we ignore the subsequent keydown event.
|
|
95
95
|
*/
|
|
96
96
|
_pasteHandled: boolean;
|
|
97
|
+
/**
|
|
98
|
+
* Working copy of fileList used during a synchronous startUpload batch.
|
|
99
|
+
* Do NOT rely on React state fileList here: setState is async (batched in React 18),
|
|
100
|
+
* so reading getState('fileList') between iterations of a sync loop returns a stale
|
|
101
|
+
* snapshot and earlier removals get overwritten by later ones (see #3335).
|
|
102
|
+
* Seeded at the start of startUpload and cleared when the sync loop finishes (see
|
|
103
|
+
* try/finally in startUpload); async (promise) results fall back to _latestFileList
|
|
104
|
+
* then React state.
|
|
105
|
+
*/
|
|
106
|
+
_batchFileList: Array<BaseFileItem> | null;
|
|
107
|
+
/**
|
|
108
|
+
* The most recent fileList produced by handleBeforeUploadResultInObject.
|
|
109
|
+
* Unlike React state (which is flushed asynchronously under React 18 automatic
|
|
110
|
+
* batching), this is updated synchronously, so a later async beforeUpload result
|
|
111
|
+
* composes with earlier sync removals of the same batch instead of reading a stale
|
|
112
|
+
* snapshot (mixed sync + async beforeUpload results, see #3335).
|
|
113
|
+
*/
|
|
114
|
+
_latestFileList: Array<BaseFileItem> | null;
|
|
115
|
+
/**
|
|
116
|
+
* Unify all fileList commits: keep _latestFileList in sync synchronously so async
|
|
117
|
+
* beforeUpload results compose with recent mutations even before React flushes
|
|
118
|
+
* setState (React 18 automatic batching). (#3335)
|
|
119
|
+
*/
|
|
120
|
+
updateFileListInternal(newFileList: Array<BaseFileItem>, callback?: () => void): void;
|
|
121
|
+
/** Keep the synchronous snapshot aligned with an externally controlled fileList. */
|
|
122
|
+
syncLatestFileList(fileList: Array<BaseFileItem>): void;
|
|
123
|
+
getLatestFileList(): Array<BaseFileItem>;
|
|
97
124
|
constructor(adapter: UploadAdapter<P, S>);
|
|
98
125
|
init(): void;
|
|
99
126
|
/**
|
|
@@ -48,6 +48,25 @@ const {
|
|
|
48
48
|
TRIGGER_AUTO
|
|
49
49
|
} = _constants.strings;
|
|
50
50
|
class UploadFoundation extends _foundation.default {
|
|
51
|
+
/**
|
|
52
|
+
* Unify all fileList commits: keep _latestFileList in sync synchronously so async
|
|
53
|
+
* beforeUpload results compose with recent mutations even before React flushes
|
|
54
|
+
* setState (React 18 automatic batching). (#3335)
|
|
55
|
+
*/
|
|
56
|
+
updateFileListInternal(newFileList, callback) {
|
|
57
|
+
if (this._batchFileList) {
|
|
58
|
+
this._batchFileList = newFileList;
|
|
59
|
+
}
|
|
60
|
+
this._latestFileList = newFileList;
|
|
61
|
+
this._adapter.updateFileList(newFileList, callback);
|
|
62
|
+
}
|
|
63
|
+
/** Keep the synchronous snapshot aligned with an externally controlled fileList. */
|
|
64
|
+
syncLatestFileList(fileList) {
|
|
65
|
+
this._latestFileList = fileList.slice();
|
|
66
|
+
}
|
|
67
|
+
getLatestFileList() {
|
|
68
|
+
return this._batchFileList || this._latestFileList || this.getState('fileList');
|
|
69
|
+
}
|
|
51
70
|
constructor(adapter) {
|
|
52
71
|
super(Object.assign({}, adapter));
|
|
53
72
|
this.destroyState = false;
|
|
@@ -62,10 +81,29 @@ class UploadFoundation extends _foundation.default {
|
|
|
62
81
|
* When paste event is successfully handled, we ignore the subsequent keydown event.
|
|
63
82
|
*/
|
|
64
83
|
this._pasteHandled = false;
|
|
84
|
+
/**
|
|
85
|
+
* Working copy of fileList used during a synchronous startUpload batch.
|
|
86
|
+
* Do NOT rely on React state fileList here: setState is async (batched in React 18),
|
|
87
|
+
* so reading getState('fileList') between iterations of a sync loop returns a stale
|
|
88
|
+
* snapshot and earlier removals get overwritten by later ones (see #3335).
|
|
89
|
+
* Seeded at the start of startUpload and cleared when the sync loop finishes (see
|
|
90
|
+
* try/finally in startUpload); async (promise) results fall back to _latestFileList
|
|
91
|
+
* then React state.
|
|
92
|
+
*/
|
|
93
|
+
this._batchFileList = null;
|
|
94
|
+
/**
|
|
95
|
+
* The most recent fileList produced by handleBeforeUploadResultInObject.
|
|
96
|
+
* Unlike React state (which is flushed asynchronously under React 18 automatic
|
|
97
|
+
* batching), this is updated synchronously, so a later async beforeUpload result
|
|
98
|
+
* composes with earlier sync removals of the same batch instead of reading a stale
|
|
99
|
+
* snapshot (mixed sync + async beforeUpload results, see #3335).
|
|
100
|
+
*/
|
|
101
|
+
this._latestFileList = null;
|
|
65
102
|
}
|
|
66
103
|
init() {
|
|
67
104
|
// make sure state reset, otherwise may cause upload abort in React StrictMode, like https://github.com/DouyinFE/semi-design/pull/843
|
|
68
105
|
this.destroyState = false;
|
|
106
|
+
this.syncLatestFileList(this.getState('fileList'));
|
|
69
107
|
// In controlled mode, parent may keep and pass back fileList with blob url.
|
|
70
108
|
// Sync them into internal map so later remove/clear can revoke correctly.
|
|
71
109
|
this._syncLocalUrlsFromFileList();
|
|
@@ -115,6 +153,8 @@ class UploadFoundation extends _foundation.default {
|
|
|
115
153
|
if (!disabled) {
|
|
116
154
|
this.unbindPastingHandler();
|
|
117
155
|
}
|
|
156
|
+
this._batchFileList = null;
|
|
157
|
+
this._latestFileList = null;
|
|
118
158
|
this.destroyState = true;
|
|
119
159
|
}
|
|
120
160
|
/**
|
|
@@ -331,7 +371,7 @@ class UploadFoundation extends _foundation.default {
|
|
|
331
371
|
currentFile: newFileItem,
|
|
332
372
|
fileList: newFileList
|
|
333
373
|
});
|
|
334
|
-
this.
|
|
374
|
+
this.updateFileListInternal(newFileList, () => {
|
|
335
375
|
this._adapter.resetReplaceInput();
|
|
336
376
|
if (!newFileItem._sizeInvalid) {
|
|
337
377
|
this.upload(newFileItem);
|
|
@@ -375,7 +415,7 @@ class UploadFoundation extends _foundation.default {
|
|
|
375
415
|
fileList: currentFiles,
|
|
376
416
|
currentFile: currentFiles[0]
|
|
377
417
|
});
|
|
378
|
-
this.
|
|
418
|
+
this.updateFileListInternal(currentFiles, () => {
|
|
379
419
|
if (uploadTrigger === TRIGGER_AUTO) {
|
|
380
420
|
this.startUpload(currentFiles);
|
|
381
421
|
}
|
|
@@ -399,7 +439,7 @@ class UploadFoundation extends _foundation.default {
|
|
|
399
439
|
});
|
|
400
440
|
}
|
|
401
441
|
});
|
|
402
|
-
this.
|
|
442
|
+
this.updateFileListInternal(fileList, () => {
|
|
403
443
|
if (uploadTrigger === TRIGGER_AUTO) {
|
|
404
444
|
this.startUpload(currentFiles);
|
|
405
445
|
}
|
|
@@ -483,7 +523,7 @@ class UploadFoundation extends _foundation.default {
|
|
|
483
523
|
fileList: newFileList,
|
|
484
524
|
currentFile: null
|
|
485
525
|
});
|
|
486
|
-
this.
|
|
526
|
+
this.updateFileListInternal(newFileList, () => {
|
|
487
527
|
if (uploadTrigger === TRIGGER_AUTO) {
|
|
488
528
|
this.startUpload(fileItemList);
|
|
489
529
|
}
|
|
@@ -492,15 +532,24 @@ class UploadFoundation extends _foundation.default {
|
|
|
492
532
|
/* istanbul ignore next */
|
|
493
533
|
manualUpload() {
|
|
494
534
|
// find the list of files that have not been uploaded
|
|
495
|
-
const waitToUploadFileList = this.
|
|
535
|
+
const waitToUploadFileList = this.getLatestFileList().filter(item => item.status === FILE_STATUS_WAIT_UPLOAD);
|
|
496
536
|
this.startUpload(waitToUploadFileList);
|
|
497
537
|
}
|
|
498
538
|
startUpload(fileList) {
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
539
|
+
// Seed a working copy so multiple synchronous beforeUpload results
|
|
540
|
+
// (e.g. several files returning autoRemove) compose correctly instead of each
|
|
541
|
+
// reading a stale React state snapshot and overwriting earlier removals. (#3335)
|
|
542
|
+
// try/finally guarantees the working copy is cleared even if beforeUpload throws.
|
|
543
|
+
this._batchFileList = this.getLatestFileList().slice();
|
|
544
|
+
try {
|
|
545
|
+
fileList.forEach(file => {
|
|
546
|
+
if (!file._sizeInvalid) {
|
|
547
|
+
this.upload(file);
|
|
548
|
+
}
|
|
549
|
+
});
|
|
550
|
+
} finally {
|
|
551
|
+
this._batchFileList = null;
|
|
552
|
+
}
|
|
504
553
|
}
|
|
505
554
|
upload(file) {
|
|
506
555
|
const {
|
|
@@ -511,9 +560,7 @@ class UploadFoundation extends _foundation.default {
|
|
|
511
560
|
return;
|
|
512
561
|
}
|
|
513
562
|
if (typeof beforeUpload === 'function') {
|
|
514
|
-
const
|
|
515
|
-
fileList
|
|
516
|
-
} = this.getStates();
|
|
563
|
+
const fileList = this.getLatestFileList();
|
|
517
564
|
const buResult = this._adapter.notifyBeforeUpload({
|
|
518
565
|
file,
|
|
519
566
|
fileList
|
|
@@ -578,7 +625,11 @@ class UploadFoundation extends _foundation.default {
|
|
|
578
625
|
validateMessage,
|
|
579
626
|
fileInstance
|
|
580
627
|
} = buResult;
|
|
581
|
-
|
|
628
|
+
// During a synchronous startUpload batch, read from the working copy; for async
|
|
629
|
+
// (promise) results read from the most recent synchronous snapshot, falling back
|
|
630
|
+
// to React state. This keeps mixed sync + async beforeUpload results composing
|
|
631
|
+
// correctly under React 18 automatic batching. (#3335)
|
|
632
|
+
let newFileList = this.getLatestFileList().slice();
|
|
582
633
|
if (autoRemove) {
|
|
583
634
|
this._releaseFileUrl(file.uid);
|
|
584
635
|
newFileList = newFileList.filter(item => item.uid !== file.uid);
|
|
@@ -600,7 +651,7 @@ class UploadFoundation extends _foundation.default {
|
|
|
600
651
|
}
|
|
601
652
|
newFileList[index].shouldUpload = shouldUpload;
|
|
602
653
|
}
|
|
603
|
-
this.
|
|
654
|
+
this.updateFileListInternal(newFileList);
|
|
604
655
|
this._adapter.notifyChange({
|
|
605
656
|
fileList: newFileList,
|
|
606
657
|
currentFile: file
|
|
@@ -714,10 +765,7 @@ class UploadFoundation extends _foundation.default {
|
|
|
714
765
|
e,
|
|
715
766
|
fileInstance
|
|
716
767
|
} = _ref2;
|
|
717
|
-
const
|
|
718
|
-
fileList
|
|
719
|
-
} = this.getStates();
|
|
720
|
-
const newFileList = fileList.slice();
|
|
768
|
+
const newFileList = this.getLatestFileList().slice();
|
|
721
769
|
let percent = 0;
|
|
722
770
|
if (e.total > 0) {
|
|
723
771
|
percent = Number((e.loaded / e.total * 100 * _constants.numbers.PROGRESS_COEFFICIENT).toFixed(0)) || 0;
|
|
@@ -729,7 +777,7 @@ class UploadFoundation extends _foundation.default {
|
|
|
729
777
|
newFileList[index].percent = percent;
|
|
730
778
|
newFileList[index].status = FILE_STATUS_UPLOADING;
|
|
731
779
|
this._adapter.notifyProgress(percent, fileInstance, newFileList);
|
|
732
|
-
this.
|
|
780
|
+
this.updateFileListInternal(newFileList);
|
|
733
781
|
this._adapter.notifyChange({
|
|
734
782
|
fileList: newFileList,
|
|
735
783
|
currentFile: newFileList[index]
|
|
@@ -741,9 +789,7 @@ class UploadFoundation extends _foundation.default {
|
|
|
741
789
|
xhr,
|
|
742
790
|
fileInstance
|
|
743
791
|
} = _ref3;
|
|
744
|
-
const
|
|
745
|
-
fileList
|
|
746
|
-
} = this.getStates();
|
|
792
|
+
const fileList = this.getLatestFileList();
|
|
747
793
|
const index = this._getFileIndex(fileInstance, fileList);
|
|
748
794
|
if (index < 0) {
|
|
749
795
|
return;
|
|
@@ -771,9 +817,7 @@ class UploadFoundation extends _foundation.default {
|
|
|
771
817
|
xhr,
|
|
772
818
|
response
|
|
773
819
|
} = _ref4;
|
|
774
|
-
const
|
|
775
|
-
fileList
|
|
776
|
-
} = this.getStates();
|
|
820
|
+
const fileList = this.getLatestFileList();
|
|
777
821
|
let body = null;
|
|
778
822
|
const index = this._getFileIndex(fileInstance, fileList);
|
|
779
823
|
if (index < 0) {
|
|
@@ -824,7 +868,7 @@ class UploadFoundation extends _foundation.default {
|
|
|
824
868
|
fileList: newFileList,
|
|
825
869
|
currentFile: newFileList[index]
|
|
826
870
|
});
|
|
827
|
-
this.
|
|
871
|
+
this.updateFileListInternal(newFileList);
|
|
828
872
|
}
|
|
829
873
|
_getFileIndex(file, fileList) {
|
|
830
874
|
return fileList.findIndex(item => item.uid === file.uid);
|
|
@@ -852,7 +896,7 @@ class UploadFoundation extends _foundation.default {
|
|
|
852
896
|
newFileList.splice(index, 1);
|
|
853
897
|
this._releaseFileUrl(file.uid);
|
|
854
898
|
this._adapter.notifyRemove(file.fileInstance, newFileList, file);
|
|
855
|
-
this.
|
|
899
|
+
this.updateFileListInternal(newFileList);
|
|
856
900
|
this._adapter.notifyChange({
|
|
857
901
|
fileList: newFileList,
|
|
858
902
|
currentFile: file
|
|
@@ -865,9 +909,7 @@ class UploadFoundation extends _foundation.default {
|
|
|
865
909
|
xhr,
|
|
866
910
|
fileInstance
|
|
867
911
|
} = _ref5;
|
|
868
|
-
const
|
|
869
|
-
fileList
|
|
870
|
-
} = this.getStates();
|
|
912
|
+
const fileList = this.getLatestFileList();
|
|
871
913
|
const index = this._getFileIndex(fileInstance, fileList);
|
|
872
914
|
if (index < 0) {
|
|
873
915
|
return;
|
|
@@ -885,7 +927,7 @@ class UploadFoundation extends _foundation.default {
|
|
|
885
927
|
newFileList[index].response = error;
|
|
886
928
|
newFileList[index].event = e;
|
|
887
929
|
this._adapter.notifyError(error, fileInstance, newFileList, xhr);
|
|
888
|
-
this.
|
|
930
|
+
this.updateFileListInternal(newFileList);
|
|
889
931
|
this._adapter.notifyChange({
|
|
890
932
|
currentFile: newFileList[index],
|
|
891
933
|
fileList: newFileList
|
|
@@ -906,7 +948,7 @@ class UploadFoundation extends _foundation.default {
|
|
|
906
948
|
return;
|
|
907
949
|
}
|
|
908
950
|
this._releaseAllFileUrls();
|
|
909
|
-
this.
|
|
951
|
+
this.updateFileListInternal([]);
|
|
910
952
|
this._adapter.notifyClear();
|
|
911
953
|
this._adapter.notifyChange({
|
|
912
954
|
fileList: []
|
|
@@ -20,6 +20,7 @@
|
|
|
20
20
|
.semi-progress-horizontal .semi-progress-track {
|
|
21
21
|
height: 100%;
|
|
22
22
|
width: 100%;
|
|
23
|
+
overflow: hidden;
|
|
23
24
|
}
|
|
24
25
|
.semi-progress-horizontal .semi-progress-track-inner {
|
|
25
26
|
height: 100%;
|
|
@@ -28,6 +29,11 @@
|
|
|
28
29
|
transition: width 0.3s;
|
|
29
30
|
transition-timing-function: cubic-bezier(0.62, 0.05, 0.36, 0.95);
|
|
30
31
|
}
|
|
32
|
+
.semi-progress-horizontal .semi-progress-track-inner-indeterminate {
|
|
33
|
+
width: 35%;
|
|
34
|
+
transition: none;
|
|
35
|
+
animation: semi-progress-indeterminate-slide 1.6s ease-in-out infinite;
|
|
36
|
+
}
|
|
31
37
|
.semi-progress-horizontal .semi-progress-line-text {
|
|
32
38
|
min-width: 45px;
|
|
33
39
|
font-weight: 600;
|
|
@@ -48,6 +54,7 @@
|
|
|
48
54
|
.semi-progress-vertical .semi-progress-track {
|
|
49
55
|
height: 100%;
|
|
50
56
|
width: 100%;
|
|
57
|
+
overflow: hidden;
|
|
51
58
|
}
|
|
52
59
|
.semi-progress-vertical .semi-progress-track-inner {
|
|
53
60
|
background-color: var(--semi-color-success);
|
|
@@ -56,6 +63,12 @@
|
|
|
56
63
|
transition: height 0.3s;
|
|
57
64
|
transition-timing-function: cubic-bezier(0.62, 0.05, 0.36, 0.95);
|
|
58
65
|
}
|
|
66
|
+
.semi-progress-vertical .semi-progress-track-inner-indeterminate {
|
|
67
|
+
height: 35%;
|
|
68
|
+
width: 100%;
|
|
69
|
+
transition: none;
|
|
70
|
+
animation: semi-progress-indeterminate-slide-vertical 1.6s ease-in-out infinite;
|
|
71
|
+
}
|
|
59
72
|
.semi-progress-vertical .semi-progress-line-text {
|
|
60
73
|
font-weight: 600;
|
|
61
74
|
margin-top: 8px;
|
|
@@ -77,6 +90,11 @@
|
|
|
77
90
|
transform-origin: 50% 50%;
|
|
78
91
|
stroke: var(--semi-color-success);
|
|
79
92
|
}
|
|
93
|
+
.semi-progress-circle-ring-inner-indeterminate {
|
|
94
|
+
transform: rotate(0deg);
|
|
95
|
+
transform-origin: 50% 50%;
|
|
96
|
+
animation: semi-progress-indeterminate-rotate 1.2s linear infinite;
|
|
97
|
+
}
|
|
80
98
|
.semi-progress-circle-text {
|
|
81
99
|
position: absolute;
|
|
82
100
|
top: 50%;
|
|
@@ -88,6 +106,36 @@
|
|
|
88
106
|
color: var(--semi-color-mode-minor-text);
|
|
89
107
|
}
|
|
90
108
|
|
|
109
|
+
@keyframes semi-progress-indeterminate-slide {
|
|
110
|
+
0% {
|
|
111
|
+
transform: translateX(-120%);
|
|
112
|
+
}
|
|
113
|
+
55% {
|
|
114
|
+
transform: translateX(300%);
|
|
115
|
+
}
|
|
116
|
+
100% {
|
|
117
|
+
transform: translateX(300%);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
@keyframes semi-progress-indeterminate-slide-vertical {
|
|
121
|
+
0% {
|
|
122
|
+
transform: translateY(-120%);
|
|
123
|
+
}
|
|
124
|
+
55% {
|
|
125
|
+
transform: translateY(300%);
|
|
126
|
+
}
|
|
127
|
+
100% {
|
|
128
|
+
transform: translateY(300%);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
@keyframes semi-progress-indeterminate-rotate {
|
|
132
|
+
from {
|
|
133
|
+
transform: rotate(0deg);
|
|
134
|
+
}
|
|
135
|
+
to {
|
|
136
|
+
transform: rotate(360deg);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
91
139
|
.semi-rtl .semi-progress,
|
|
92
140
|
.semi-portal-rtl .semi-progress {
|
|
93
141
|
direction: rtl;
|
|
@@ -23,6 +23,7 @@ $module: #{$prefix}-progress;
|
|
|
23
23
|
.#{$module}-track {
|
|
24
24
|
height: 100%;
|
|
25
25
|
width: 100%;
|
|
26
|
+
overflow: hidden;
|
|
26
27
|
}
|
|
27
28
|
|
|
28
29
|
.#{$module}-track-inner {
|
|
@@ -33,6 +34,12 @@ $module: #{$prefix}-progress;
|
|
|
33
34
|
transition-timing-function: $motion-progress-transition_timing_function;
|
|
34
35
|
}
|
|
35
36
|
|
|
37
|
+
.#{$module}-track-inner-indeterminate {
|
|
38
|
+
width: 35%;
|
|
39
|
+
transition: none;
|
|
40
|
+
animation: semi-progress-indeterminate-slide 1.6s ease-in-out infinite;
|
|
41
|
+
}
|
|
42
|
+
|
|
36
43
|
.#{$module}-line-text {
|
|
37
44
|
min-width: $width-progress_line_text;
|
|
38
45
|
font-weight: $font-progress_line_text-fontWeight;
|
|
@@ -57,6 +64,7 @@ $module: #{$prefix}-progress;
|
|
|
57
64
|
.#{$module}-track {
|
|
58
65
|
height: 100%;
|
|
59
66
|
width: 100%;
|
|
67
|
+
overflow: hidden;
|
|
60
68
|
}
|
|
61
69
|
|
|
62
70
|
.#{$module}-track-inner {
|
|
@@ -67,6 +75,13 @@ $module: #{$prefix}-progress;
|
|
|
67
75
|
transition-timing-function: $motion-progress-transition_timing_function;
|
|
68
76
|
}
|
|
69
77
|
|
|
78
|
+
.#{$module}-track-inner-indeterminate {
|
|
79
|
+
height: 35%;
|
|
80
|
+
width: 100%;
|
|
81
|
+
transition: none;
|
|
82
|
+
animation: semi-progress-indeterminate-slide-vertical 1.6s ease-in-out infinite;
|
|
83
|
+
}
|
|
84
|
+
|
|
70
85
|
.#{$module}-line-text {
|
|
71
86
|
font-weight: $font-progress_line_text-fontWeight;
|
|
72
87
|
margin-top: $spacing-progress_vertical_line_text-marginTop;
|
|
@@ -95,6 +110,12 @@ $module: #{$prefix}-progress;
|
|
|
95
110
|
stroke: $color-progress_track_inner-bg;
|
|
96
111
|
}
|
|
97
112
|
|
|
113
|
+
&-ring-inner-indeterminate {
|
|
114
|
+
transform: rotate(0deg);
|
|
115
|
+
transform-origin: 50% 50%;
|
|
116
|
+
animation: semi-progress-indeterminate-rotate 1.2s linear infinite;
|
|
117
|
+
}
|
|
118
|
+
|
|
98
119
|
&-text {
|
|
99
120
|
position: absolute;
|
|
100
121
|
top: 50%;
|
|
@@ -108,4 +129,42 @@ $module: #{$prefix}-progress;
|
|
|
108
129
|
}
|
|
109
130
|
}
|
|
110
131
|
|
|
111
|
-
@
|
|
132
|
+
@keyframes semi-progress-indeterminate-slide {
|
|
133
|
+
0% {
|
|
134
|
+
transform: translateX(-120%);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
55% {
|
|
138
|
+
transform: translateX(300%);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
100% {
|
|
142
|
+
transform: translateX(300%);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
@keyframes semi-progress-indeterminate-slide-vertical {
|
|
147
|
+
0% {
|
|
148
|
+
transform: translateY(-120%);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
55% {
|
|
152
|
+
transform: translateY(300%);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
100% {
|
|
156
|
+
transform: translateY(300%);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
@keyframes semi-progress-indeterminate-rotate {
|
|
161
|
+
from {
|
|
162
|
+
transform: rotate(0deg);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
to {
|
|
166
|
+
transform: rotate(360deg);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
@import './rtl.scss';
|
|
@@ -94,6 +94,33 @@ declare class UploadFoundation<P = Record<string, any>, S = Record<string, any>>
|
|
|
94
94
|
* When paste event is successfully handled, we ignore the subsequent keydown event.
|
|
95
95
|
*/
|
|
96
96
|
_pasteHandled: boolean;
|
|
97
|
+
/**
|
|
98
|
+
* Working copy of fileList used during a synchronous startUpload batch.
|
|
99
|
+
* Do NOT rely on React state fileList here: setState is async (batched in React 18),
|
|
100
|
+
* so reading getState('fileList') between iterations of a sync loop returns a stale
|
|
101
|
+
* snapshot and earlier removals get overwritten by later ones (see #3335).
|
|
102
|
+
* Seeded at the start of startUpload and cleared when the sync loop finishes (see
|
|
103
|
+
* try/finally in startUpload); async (promise) results fall back to _latestFileList
|
|
104
|
+
* then React state.
|
|
105
|
+
*/
|
|
106
|
+
_batchFileList: Array<BaseFileItem> | null;
|
|
107
|
+
/**
|
|
108
|
+
* The most recent fileList produced by handleBeforeUploadResultInObject.
|
|
109
|
+
* Unlike React state (which is flushed asynchronously under React 18 automatic
|
|
110
|
+
* batching), this is updated synchronously, so a later async beforeUpload result
|
|
111
|
+
* composes with earlier sync removals of the same batch instead of reading a stale
|
|
112
|
+
* snapshot (mixed sync + async beforeUpload results, see #3335).
|
|
113
|
+
*/
|
|
114
|
+
_latestFileList: Array<BaseFileItem> | null;
|
|
115
|
+
/**
|
|
116
|
+
* Unify all fileList commits: keep _latestFileList in sync synchronously so async
|
|
117
|
+
* beforeUpload results compose with recent mutations even before React flushes
|
|
118
|
+
* setState (React 18 automatic batching). (#3335)
|
|
119
|
+
*/
|
|
120
|
+
updateFileListInternal(newFileList: Array<BaseFileItem>, callback?: () => void): void;
|
|
121
|
+
/** Keep the synchronous snapshot aligned with an externally controlled fileList. */
|
|
122
|
+
syncLatestFileList(fileList: Array<BaseFileItem>): void;
|
|
123
|
+
getLatestFileList(): Array<BaseFileItem>;
|
|
97
124
|
constructor(adapter: UploadAdapter<P, S>);
|
|
98
125
|
init(): void;
|
|
99
126
|
/**
|
|
@@ -41,6 +41,25 @@ const {
|
|
|
41
41
|
TRIGGER_AUTO
|
|
42
42
|
} = strings;
|
|
43
43
|
class UploadFoundation extends BaseFoundation {
|
|
44
|
+
/**
|
|
45
|
+
* Unify all fileList commits: keep _latestFileList in sync synchronously so async
|
|
46
|
+
* beforeUpload results compose with recent mutations even before React flushes
|
|
47
|
+
* setState (React 18 automatic batching). (#3335)
|
|
48
|
+
*/
|
|
49
|
+
updateFileListInternal(newFileList, callback) {
|
|
50
|
+
if (this._batchFileList) {
|
|
51
|
+
this._batchFileList = newFileList;
|
|
52
|
+
}
|
|
53
|
+
this._latestFileList = newFileList;
|
|
54
|
+
this._adapter.updateFileList(newFileList, callback);
|
|
55
|
+
}
|
|
56
|
+
/** Keep the synchronous snapshot aligned with an externally controlled fileList. */
|
|
57
|
+
syncLatestFileList(fileList) {
|
|
58
|
+
this._latestFileList = fileList.slice();
|
|
59
|
+
}
|
|
60
|
+
getLatestFileList() {
|
|
61
|
+
return this._batchFileList || this._latestFileList || this.getState('fileList');
|
|
62
|
+
}
|
|
44
63
|
constructor(adapter) {
|
|
45
64
|
super(Object.assign({}, adapter));
|
|
46
65
|
this.destroyState = false;
|
|
@@ -55,10 +74,29 @@ class UploadFoundation extends BaseFoundation {
|
|
|
55
74
|
* When paste event is successfully handled, we ignore the subsequent keydown event.
|
|
56
75
|
*/
|
|
57
76
|
this._pasteHandled = false;
|
|
77
|
+
/**
|
|
78
|
+
* Working copy of fileList used during a synchronous startUpload batch.
|
|
79
|
+
* Do NOT rely on React state fileList here: setState is async (batched in React 18),
|
|
80
|
+
* so reading getState('fileList') between iterations of a sync loop returns a stale
|
|
81
|
+
* snapshot and earlier removals get overwritten by later ones (see #3335).
|
|
82
|
+
* Seeded at the start of startUpload and cleared when the sync loop finishes (see
|
|
83
|
+
* try/finally in startUpload); async (promise) results fall back to _latestFileList
|
|
84
|
+
* then React state.
|
|
85
|
+
*/
|
|
86
|
+
this._batchFileList = null;
|
|
87
|
+
/**
|
|
88
|
+
* The most recent fileList produced by handleBeforeUploadResultInObject.
|
|
89
|
+
* Unlike React state (which is flushed asynchronously under React 18 automatic
|
|
90
|
+
* batching), this is updated synchronously, so a later async beforeUpload result
|
|
91
|
+
* composes with earlier sync removals of the same batch instead of reading a stale
|
|
92
|
+
* snapshot (mixed sync + async beforeUpload results, see #3335).
|
|
93
|
+
*/
|
|
94
|
+
this._latestFileList = null;
|
|
58
95
|
}
|
|
59
96
|
init() {
|
|
60
97
|
// make sure state reset, otherwise may cause upload abort in React StrictMode, like https://github.com/DouyinFE/semi-design/pull/843
|
|
61
98
|
this.destroyState = false;
|
|
99
|
+
this.syncLatestFileList(this.getState('fileList'));
|
|
62
100
|
// In controlled mode, parent may keep and pass back fileList with blob url.
|
|
63
101
|
// Sync them into internal map so later remove/clear can revoke correctly.
|
|
64
102
|
this._syncLocalUrlsFromFileList();
|
|
@@ -108,6 +146,8 @@ class UploadFoundation extends BaseFoundation {
|
|
|
108
146
|
if (!disabled) {
|
|
109
147
|
this.unbindPastingHandler();
|
|
110
148
|
}
|
|
149
|
+
this._batchFileList = null;
|
|
150
|
+
this._latestFileList = null;
|
|
111
151
|
this.destroyState = true;
|
|
112
152
|
}
|
|
113
153
|
/**
|
|
@@ -324,7 +364,7 @@ class UploadFoundation extends BaseFoundation {
|
|
|
324
364
|
currentFile: newFileItem,
|
|
325
365
|
fileList: newFileList
|
|
326
366
|
});
|
|
327
|
-
this.
|
|
367
|
+
this.updateFileListInternal(newFileList, () => {
|
|
328
368
|
this._adapter.resetReplaceInput();
|
|
329
369
|
if (!newFileItem._sizeInvalid) {
|
|
330
370
|
this.upload(newFileItem);
|
|
@@ -368,7 +408,7 @@ class UploadFoundation extends BaseFoundation {
|
|
|
368
408
|
fileList: currentFiles,
|
|
369
409
|
currentFile: currentFiles[0]
|
|
370
410
|
});
|
|
371
|
-
this.
|
|
411
|
+
this.updateFileListInternal(currentFiles, () => {
|
|
372
412
|
if (uploadTrigger === TRIGGER_AUTO) {
|
|
373
413
|
this.startUpload(currentFiles);
|
|
374
414
|
}
|
|
@@ -392,7 +432,7 @@ class UploadFoundation extends BaseFoundation {
|
|
|
392
432
|
});
|
|
393
433
|
}
|
|
394
434
|
});
|
|
395
|
-
this.
|
|
435
|
+
this.updateFileListInternal(fileList, () => {
|
|
396
436
|
if (uploadTrigger === TRIGGER_AUTO) {
|
|
397
437
|
this.startUpload(currentFiles);
|
|
398
438
|
}
|
|
@@ -476,7 +516,7 @@ class UploadFoundation extends BaseFoundation {
|
|
|
476
516
|
fileList: newFileList,
|
|
477
517
|
currentFile: null
|
|
478
518
|
});
|
|
479
|
-
this.
|
|
519
|
+
this.updateFileListInternal(newFileList, () => {
|
|
480
520
|
if (uploadTrigger === TRIGGER_AUTO) {
|
|
481
521
|
this.startUpload(fileItemList);
|
|
482
522
|
}
|
|
@@ -485,15 +525,24 @@ class UploadFoundation extends BaseFoundation {
|
|
|
485
525
|
/* istanbul ignore next */
|
|
486
526
|
manualUpload() {
|
|
487
527
|
// find the list of files that have not been uploaded
|
|
488
|
-
const waitToUploadFileList = this.
|
|
528
|
+
const waitToUploadFileList = this.getLatestFileList().filter(item => item.status === FILE_STATUS_WAIT_UPLOAD);
|
|
489
529
|
this.startUpload(waitToUploadFileList);
|
|
490
530
|
}
|
|
491
531
|
startUpload(fileList) {
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
532
|
+
// Seed a working copy so multiple synchronous beforeUpload results
|
|
533
|
+
// (e.g. several files returning autoRemove) compose correctly instead of each
|
|
534
|
+
// reading a stale React state snapshot and overwriting earlier removals. (#3335)
|
|
535
|
+
// try/finally guarantees the working copy is cleared even if beforeUpload throws.
|
|
536
|
+
this._batchFileList = this.getLatestFileList().slice();
|
|
537
|
+
try {
|
|
538
|
+
fileList.forEach(file => {
|
|
539
|
+
if (!file._sizeInvalid) {
|
|
540
|
+
this.upload(file);
|
|
541
|
+
}
|
|
542
|
+
});
|
|
543
|
+
} finally {
|
|
544
|
+
this._batchFileList = null;
|
|
545
|
+
}
|
|
497
546
|
}
|
|
498
547
|
upload(file) {
|
|
499
548
|
const {
|
|
@@ -504,9 +553,7 @@ class UploadFoundation extends BaseFoundation {
|
|
|
504
553
|
return;
|
|
505
554
|
}
|
|
506
555
|
if (typeof beforeUpload === 'function') {
|
|
507
|
-
const
|
|
508
|
-
fileList
|
|
509
|
-
} = this.getStates();
|
|
556
|
+
const fileList = this.getLatestFileList();
|
|
510
557
|
const buResult = this._adapter.notifyBeforeUpload({
|
|
511
558
|
file,
|
|
512
559
|
fileList
|
|
@@ -571,7 +618,11 @@ class UploadFoundation extends BaseFoundation {
|
|
|
571
618
|
validateMessage,
|
|
572
619
|
fileInstance
|
|
573
620
|
} = buResult;
|
|
574
|
-
|
|
621
|
+
// During a synchronous startUpload batch, read from the working copy; for async
|
|
622
|
+
// (promise) results read from the most recent synchronous snapshot, falling back
|
|
623
|
+
// to React state. This keeps mixed sync + async beforeUpload results composing
|
|
624
|
+
// correctly under React 18 automatic batching. (#3335)
|
|
625
|
+
let newFileList = this.getLatestFileList().slice();
|
|
575
626
|
if (autoRemove) {
|
|
576
627
|
this._releaseFileUrl(file.uid);
|
|
577
628
|
newFileList = newFileList.filter(item => item.uid !== file.uid);
|
|
@@ -593,7 +644,7 @@ class UploadFoundation extends BaseFoundation {
|
|
|
593
644
|
}
|
|
594
645
|
newFileList[index].shouldUpload = shouldUpload;
|
|
595
646
|
}
|
|
596
|
-
this.
|
|
647
|
+
this.updateFileListInternal(newFileList);
|
|
597
648
|
this._adapter.notifyChange({
|
|
598
649
|
fileList: newFileList,
|
|
599
650
|
currentFile: file
|
|
@@ -707,10 +758,7 @@ class UploadFoundation extends BaseFoundation {
|
|
|
707
758
|
e,
|
|
708
759
|
fileInstance
|
|
709
760
|
} = _ref2;
|
|
710
|
-
const
|
|
711
|
-
fileList
|
|
712
|
-
} = this.getStates();
|
|
713
|
-
const newFileList = fileList.slice();
|
|
761
|
+
const newFileList = this.getLatestFileList().slice();
|
|
714
762
|
let percent = 0;
|
|
715
763
|
if (e.total > 0) {
|
|
716
764
|
percent = Number((e.loaded / e.total * 100 * numbers.PROGRESS_COEFFICIENT).toFixed(0)) || 0;
|
|
@@ -722,7 +770,7 @@ class UploadFoundation extends BaseFoundation {
|
|
|
722
770
|
newFileList[index].percent = percent;
|
|
723
771
|
newFileList[index].status = FILE_STATUS_UPLOADING;
|
|
724
772
|
this._adapter.notifyProgress(percent, fileInstance, newFileList);
|
|
725
|
-
this.
|
|
773
|
+
this.updateFileListInternal(newFileList);
|
|
726
774
|
this._adapter.notifyChange({
|
|
727
775
|
fileList: newFileList,
|
|
728
776
|
currentFile: newFileList[index]
|
|
@@ -734,9 +782,7 @@ class UploadFoundation extends BaseFoundation {
|
|
|
734
782
|
xhr,
|
|
735
783
|
fileInstance
|
|
736
784
|
} = _ref3;
|
|
737
|
-
const
|
|
738
|
-
fileList
|
|
739
|
-
} = this.getStates();
|
|
785
|
+
const fileList = this.getLatestFileList();
|
|
740
786
|
const index = this._getFileIndex(fileInstance, fileList);
|
|
741
787
|
if (index < 0) {
|
|
742
788
|
return;
|
|
@@ -764,9 +810,7 @@ class UploadFoundation extends BaseFoundation {
|
|
|
764
810
|
xhr,
|
|
765
811
|
response
|
|
766
812
|
} = _ref4;
|
|
767
|
-
const
|
|
768
|
-
fileList
|
|
769
|
-
} = this.getStates();
|
|
813
|
+
const fileList = this.getLatestFileList();
|
|
770
814
|
let body = null;
|
|
771
815
|
const index = this._getFileIndex(fileInstance, fileList);
|
|
772
816
|
if (index < 0) {
|
|
@@ -817,7 +861,7 @@ class UploadFoundation extends BaseFoundation {
|
|
|
817
861
|
fileList: newFileList,
|
|
818
862
|
currentFile: newFileList[index]
|
|
819
863
|
});
|
|
820
|
-
this.
|
|
864
|
+
this.updateFileListInternal(newFileList);
|
|
821
865
|
}
|
|
822
866
|
_getFileIndex(file, fileList) {
|
|
823
867
|
return fileList.findIndex(item => item.uid === file.uid);
|
|
@@ -845,7 +889,7 @@ class UploadFoundation extends BaseFoundation {
|
|
|
845
889
|
newFileList.splice(index, 1);
|
|
846
890
|
this._releaseFileUrl(file.uid);
|
|
847
891
|
this._adapter.notifyRemove(file.fileInstance, newFileList, file);
|
|
848
|
-
this.
|
|
892
|
+
this.updateFileListInternal(newFileList);
|
|
849
893
|
this._adapter.notifyChange({
|
|
850
894
|
fileList: newFileList,
|
|
851
895
|
currentFile: file
|
|
@@ -858,9 +902,7 @@ class UploadFoundation extends BaseFoundation {
|
|
|
858
902
|
xhr,
|
|
859
903
|
fileInstance
|
|
860
904
|
} = _ref5;
|
|
861
|
-
const
|
|
862
|
-
fileList
|
|
863
|
-
} = this.getStates();
|
|
905
|
+
const fileList = this.getLatestFileList();
|
|
864
906
|
const index = this._getFileIndex(fileInstance, fileList);
|
|
865
907
|
if (index < 0) {
|
|
866
908
|
return;
|
|
@@ -878,7 +920,7 @@ class UploadFoundation extends BaseFoundation {
|
|
|
878
920
|
newFileList[index].response = error;
|
|
879
921
|
newFileList[index].event = e;
|
|
880
922
|
this._adapter.notifyError(error, fileInstance, newFileList, xhr);
|
|
881
|
-
this.
|
|
923
|
+
this.updateFileListInternal(newFileList);
|
|
882
924
|
this._adapter.notifyChange({
|
|
883
925
|
currentFile: newFileList[index],
|
|
884
926
|
fileList: newFileList
|
|
@@ -899,7 +941,7 @@ class UploadFoundation extends BaseFoundation {
|
|
|
899
941
|
return;
|
|
900
942
|
}
|
|
901
943
|
this._releaseAllFileUrls();
|
|
902
|
-
this.
|
|
944
|
+
this.updateFileListInternal([]);
|
|
903
945
|
this._adapter.notifyClear();
|
|
904
946
|
this._adapter.notifyChange({
|
|
905
947
|
fileList: []
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@douyinfe/semi-foundation",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.103.0",
|
|
4
4
|
"description": "",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"clean": "rimraf lib",
|
|
@@ -14225,8 +14225,8 @@
|
|
|
14225
14225
|
}
|
|
14226
14226
|
},
|
|
14227
14227
|
"dependencies": {
|
|
14228
|
-
"@douyinfe/semi-animation": "2.
|
|
14229
|
-
"@douyinfe/semi-json-viewer-core": "2.
|
|
14228
|
+
"@douyinfe/semi-animation": "2.103.0",
|
|
14229
|
+
"@douyinfe/semi-json-viewer-core": "2.103.0",
|
|
14230
14230
|
"@mdx-js/mdx": "^3.0.1",
|
|
14231
14231
|
"async-validator": "^3.5.0",
|
|
14232
14232
|
"classnames": "^2.2.6",
|
|
@@ -14247,7 +14247,7 @@
|
|
|
14247
14247
|
"*.scss",
|
|
14248
14248
|
"*.css"
|
|
14249
14249
|
],
|
|
14250
|
-
"gitHead": "
|
|
14250
|
+
"gitHead": "c1e3632b90b242defdfe4bfb267bcfaffbb23295",
|
|
14251
14251
|
"devDependencies": {
|
|
14252
14252
|
"@babel/plugin-transform-runtime": "^7.15.8",
|
|
14253
14253
|
"@babel/preset-env": "^7.15.8",
|
package/progress/progress.scss
CHANGED
|
@@ -23,6 +23,7 @@ $module: #{$prefix}-progress;
|
|
|
23
23
|
.#{$module}-track {
|
|
24
24
|
height: 100%;
|
|
25
25
|
width: 100%;
|
|
26
|
+
overflow: hidden;
|
|
26
27
|
}
|
|
27
28
|
|
|
28
29
|
.#{$module}-track-inner {
|
|
@@ -33,6 +34,12 @@ $module: #{$prefix}-progress;
|
|
|
33
34
|
transition-timing-function: $motion-progress-transition_timing_function;
|
|
34
35
|
}
|
|
35
36
|
|
|
37
|
+
.#{$module}-track-inner-indeterminate {
|
|
38
|
+
width: 35%;
|
|
39
|
+
transition: none;
|
|
40
|
+
animation: semi-progress-indeterminate-slide 1.6s ease-in-out infinite;
|
|
41
|
+
}
|
|
42
|
+
|
|
36
43
|
.#{$module}-line-text {
|
|
37
44
|
min-width: $width-progress_line_text;
|
|
38
45
|
font-weight: $font-progress_line_text-fontWeight;
|
|
@@ -57,6 +64,7 @@ $module: #{$prefix}-progress;
|
|
|
57
64
|
.#{$module}-track {
|
|
58
65
|
height: 100%;
|
|
59
66
|
width: 100%;
|
|
67
|
+
overflow: hidden;
|
|
60
68
|
}
|
|
61
69
|
|
|
62
70
|
.#{$module}-track-inner {
|
|
@@ -67,6 +75,13 @@ $module: #{$prefix}-progress;
|
|
|
67
75
|
transition-timing-function: $motion-progress-transition_timing_function;
|
|
68
76
|
}
|
|
69
77
|
|
|
78
|
+
.#{$module}-track-inner-indeterminate {
|
|
79
|
+
height: 35%;
|
|
80
|
+
width: 100%;
|
|
81
|
+
transition: none;
|
|
82
|
+
animation: semi-progress-indeterminate-slide-vertical 1.6s ease-in-out infinite;
|
|
83
|
+
}
|
|
84
|
+
|
|
70
85
|
.#{$module}-line-text {
|
|
71
86
|
font-weight: $font-progress_line_text-fontWeight;
|
|
72
87
|
margin-top: $spacing-progress_vertical_line_text-marginTop;
|
|
@@ -95,6 +110,12 @@ $module: #{$prefix}-progress;
|
|
|
95
110
|
stroke: $color-progress_track_inner-bg;
|
|
96
111
|
}
|
|
97
112
|
|
|
113
|
+
&-ring-inner-indeterminate {
|
|
114
|
+
transform: rotate(0deg);
|
|
115
|
+
transform-origin: 50% 50%;
|
|
116
|
+
animation: semi-progress-indeterminate-rotate 1.2s linear infinite;
|
|
117
|
+
}
|
|
118
|
+
|
|
98
119
|
&-text {
|
|
99
120
|
position: absolute;
|
|
100
121
|
top: 50%;
|
|
@@ -108,4 +129,42 @@ $module: #{$prefix}-progress;
|
|
|
108
129
|
}
|
|
109
130
|
}
|
|
110
131
|
|
|
111
|
-
@
|
|
132
|
+
@keyframes semi-progress-indeterminate-slide {
|
|
133
|
+
0% {
|
|
134
|
+
transform: translateX(-120%);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
55% {
|
|
138
|
+
transform: translateX(300%);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
100% {
|
|
142
|
+
transform: translateX(300%);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
@keyframes semi-progress-indeterminate-slide-vertical {
|
|
147
|
+
0% {
|
|
148
|
+
transform: translateY(-120%);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
55% {
|
|
152
|
+
transform: translateY(300%);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
100% {
|
|
156
|
+
transform: translateY(300%);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
@keyframes semi-progress-indeterminate-rotate {
|
|
161
|
+
from {
|
|
162
|
+
transform: rotate(0deg);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
to {
|
|
166
|
+
transform: rotate(360deg);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
@import './rtl.scss';
|
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 => {
|