@douyinfe/semi-foundation 2.94.1 → 2.95.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/datePicker/foundation.ts +13 -4
- package/input/foundation.ts +16 -1
- package/input/textareaFoundation.ts +18 -4
- package/inputNumber/foundation.ts +20 -3
- package/lib/cjs/datePicker/foundation.js +12 -3
- package/lib/cjs/input/foundation.js +19 -0
- package/lib/cjs/input/textareaFoundation.js +18 -4
- package/lib/cjs/inputNumber/foundation.js +19 -3
- package/lib/cjs/transfer/foundation.d.ts +12 -0
- package/lib/cjs/transfer/foundation.js +24 -3
- package/lib/cjs/upload/foundation.d.ts +5 -5
- package/lib/es/datePicker/foundation.js +13 -4
- package/lib/es/input/foundation.js +19 -0
- package/lib/es/input/textareaFoundation.js +18 -4
- package/lib/es/inputNumber/foundation.js +19 -3
- package/lib/es/transfer/foundation.d.ts +12 -0
- package/lib/es/transfer/foundation.js +24 -3
- package/lib/es/upload/foundation.d.ts +5 -5
- package/package.json +4 -4
- package/transfer/foundation.ts +30 -3
- package/upload/foundation.ts +5 -5
package/datePicker/foundation.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { format, isValid, isSameSecond, isEqual as isDateEqual, isDate } from 'date-fns';
|
|
1
|
+
import { format, isValid, isSameSecond, isEqual as isDateEqual, isDate, startOfDay } from 'date-fns';
|
|
2
2
|
import { get, isObject, isString, isEqual, isFunction } from 'lodash';
|
|
3
3
|
|
|
4
4
|
import BaseFoundation, { DefaultAdapter } from '../base/foundation';
|
|
@@ -1228,9 +1228,18 @@ export default class DatePickerFoundation extends BaseFoundation<DatePickerAdapt
|
|
|
1228
1228
|
let isSomeDateDisabled = false;
|
|
1229
1229
|
for (const date of value) {
|
|
1230
1230
|
// skip check if date is null
|
|
1231
|
-
if (!isNullOrUndefined(date)
|
|
1232
|
-
|
|
1233
|
-
|
|
1231
|
+
if (!isNullOrUndefined(date)) {
|
|
1232
|
+
/**
|
|
1233
|
+
* When type is dateTimeRange, disabledDate should only consider the date part, not the time part.
|
|
1234
|
+
* This ensures that when user adjusts time, the date disable check is not affected by the time portion.
|
|
1235
|
+
* For example, if the user selects start date X and end date X+7 (exactly 7 days), then adjusts
|
|
1236
|
+
* the end time to 10:00, the end date should still be valid, not be incorrectly judged as disabled.
|
|
1237
|
+
*/
|
|
1238
|
+
const dateToCheck = this._isRangeType() && isValid(date) ? startOfDay(date) : date;
|
|
1239
|
+
if (this.disabledDisposeDate(dateToCheck, disabledOptions)) {
|
|
1240
|
+
isSomeDateDisabled = true;
|
|
1241
|
+
break;
|
|
1242
|
+
}
|
|
1234
1243
|
}
|
|
1235
1244
|
}
|
|
1236
1245
|
return isSomeDateDisabled;
|
package/input/foundation.ts
CHANGED
|
@@ -59,7 +59,15 @@ class InputFoundation extends BaseFoundation<InputAdapter> {
|
|
|
59
59
|
}
|
|
60
60
|
|
|
61
61
|
handleChange(value: any, e: any) {
|
|
62
|
+
const { composition } = this._adapter.getProps();
|
|
62
63
|
let nextValue = value;
|
|
64
|
+
// When composition is enabled and in IME composing, only update internal state without triggering onChange
|
|
65
|
+
// We still need to update internal state for both controlled and uncontrolled components,
|
|
66
|
+
// so that the input can correctly display the composing text
|
|
67
|
+
if (composition && this.compositionEnter) {
|
|
68
|
+
this._adapter.setValue(nextValue);
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
63
71
|
if (!this.compositionEnter) {
|
|
64
72
|
nextValue = this.getNextValue(nextValue);
|
|
65
73
|
}
|
|
@@ -101,9 +109,16 @@ class InputFoundation extends BaseFoundation<InputAdapter> {
|
|
|
101
109
|
}
|
|
102
110
|
|
|
103
111
|
handleCompositionEnd = (e: any) => {
|
|
112
|
+
const { composition } = this._adapter.getProps();
|
|
104
113
|
const value = e.target.value;
|
|
105
114
|
this.compositionEnter = false;
|
|
106
|
-
this._adapter.notifyCompositionEnd(e);
|
|
115
|
+
this._adapter.notifyCompositionEnd(e);
|
|
116
|
+
// When composition is enabled, trigger onChange after IME composition ends
|
|
117
|
+
if (composition) {
|
|
118
|
+
const nextValue = this.getNextValue(value);
|
|
119
|
+
this.changeInput(nextValue, e);
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
107
122
|
const { getValueLength, maxLength, minLength } = this.getProps();
|
|
108
123
|
if (!isFunction(getValueLength)) {
|
|
109
124
|
return;
|
|
@@ -63,8 +63,15 @@ export default class TextAreaFoundation extends BaseFoundation<TextAreaAdapter>
|
|
|
63
63
|
}
|
|
64
64
|
|
|
65
65
|
handleChange(value: string, e: any) {
|
|
66
|
-
const {
|
|
66
|
+
const { composition } = this._adapter.getProps();
|
|
67
67
|
let nextValue = value;
|
|
68
|
+
// When composition is enabled and in IME composing, only update internal state without triggering onChange
|
|
69
|
+
// We still need to update internal state for both controlled and uncontrolled components,
|
|
70
|
+
// so that the textarea can correctly display the composing text
|
|
71
|
+
if (composition && this.compositionEnter) {
|
|
72
|
+
this._adapter.setValue(nextValue);
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
68
75
|
if (!this.compositionEnter) {
|
|
69
76
|
nextValue = this.getNextValue(nextValue);
|
|
70
77
|
}
|
|
@@ -100,20 +107,27 @@ export default class TextAreaFoundation extends BaseFoundation<TextAreaAdapter>
|
|
|
100
107
|
}
|
|
101
108
|
|
|
102
109
|
handleCompositionEnd = (e: any) => {
|
|
110
|
+
const { composition } = this._adapter.getProps();
|
|
111
|
+
const value = e.target.value;
|
|
103
112
|
this.compositionEnter = false;
|
|
104
|
-
this._adapter.notifyCompositionEnd(e);
|
|
113
|
+
this._adapter.notifyCompositionEnd(e);
|
|
114
|
+
// When composition is enabled, trigger onChange after IME composition ends
|
|
115
|
+
if (composition) {
|
|
116
|
+
const nextValue = this.getNextValue(value);
|
|
117
|
+
this._changeValue(nextValue, e);
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
105
120
|
const { getValueLength, maxLength, minLength } = this.getProps();
|
|
106
121
|
if (!isFunction(getValueLength)) {
|
|
107
122
|
return;
|
|
108
123
|
}
|
|
109
|
-
const value = e.target.value;
|
|
110
124
|
if (maxLength) {
|
|
111
125
|
const nextValue = this.handleVisibleMaxLength(value);
|
|
112
126
|
nextValue !== value && this._changeValue(nextValue, e);
|
|
113
127
|
}
|
|
114
128
|
if (minLength) {
|
|
115
129
|
this.handleVisibleMinLength(value);
|
|
116
|
-
}
|
|
130
|
+
}
|
|
117
131
|
}
|
|
118
132
|
|
|
119
133
|
handleCompositionUpdate = (e) => {
|
|
@@ -405,7 +405,22 @@ class InputNumberFoundation extends BaseFoundation<InputNumberAdapter> {
|
|
|
405
405
|
|
|
406
406
|
const propsValue = this._isControlledComponent('value') ? value : defaultValue;
|
|
407
407
|
|
|
408
|
-
|
|
408
|
+
// Align init logic with controlled update logic in semi-ui:
|
|
409
|
+
// - When propsValue is a number, we should apply formatter first (doFormat)
|
|
410
|
+
// so that parser/formatter pairs like percentage (value=1 => display=100)
|
|
411
|
+
// work correctly on first mount.
|
|
412
|
+
// - When propsValue is a string, keep original behavior.
|
|
413
|
+
let valueStr: any = propsValue;
|
|
414
|
+
// NOTE: Currency mode should keep the original behavior (number stays number) because
|
|
415
|
+
// parseInternationalCurrency expects locale-formatted strings and relies on symbols
|
|
416
|
+
// computed during init().
|
|
417
|
+
if (typeof propsValue === 'number' && !this._isCurrency()) {
|
|
418
|
+
valueStr = this.doFormat(propsValue);
|
|
419
|
+
} else if (!this._isCurrency()) {
|
|
420
|
+
valueStr = toString(propsValue);
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
const tmpNumber = this.doParse(valueStr, false, true, true);
|
|
409
424
|
|
|
410
425
|
let number = null;
|
|
411
426
|
if (typeof tmpNumber === 'number' && !isNaN(tmpNumber)) {
|
|
@@ -593,7 +608,8 @@ class InputNumberFoundation extends BaseFoundation<InputNumberAdapter> {
|
|
|
593
608
|
const parser = this.getProp('parser');
|
|
594
609
|
|
|
595
610
|
if (typeof parser === 'function') {
|
|
596
|
-
|
|
611
|
+
const parsedValue = parser(value) as unknown;
|
|
612
|
+
value = typeof parsedValue === 'number' ? toString(parsedValue) : parsedValue as string;
|
|
597
613
|
}
|
|
598
614
|
|
|
599
615
|
if (needCheckPrec && typeof value === 'string') {
|
|
@@ -648,7 +664,8 @@ class InputNumberFoundation extends BaseFoundation<InputNumberAdapter> {
|
|
|
648
664
|
}
|
|
649
665
|
|
|
650
666
|
if (typeof parser === 'function') {
|
|
651
|
-
|
|
667
|
+
const parsedValue = parser(value) as unknown;
|
|
668
|
+
value = typeof parsedValue === 'number' ? toString(parsedValue) : parsedValue as string;
|
|
652
669
|
}
|
|
653
670
|
if (needAdjustPrec) {
|
|
654
671
|
value = this._adjustPrec(value);
|
|
@@ -1046,9 +1046,18 @@ class DatePickerFoundation extends _foundation.default {
|
|
|
1046
1046
|
let isSomeDateDisabled = false;
|
|
1047
1047
|
for (const date of value) {
|
|
1048
1048
|
// skip check if date is null
|
|
1049
|
-
if (!(0, _isNullOrUndefined.default)(date)
|
|
1050
|
-
|
|
1051
|
-
|
|
1049
|
+
if (!(0, _isNullOrUndefined.default)(date)) {
|
|
1050
|
+
/**
|
|
1051
|
+
* When type is dateTimeRange, disabledDate should only consider the date part, not the time part.
|
|
1052
|
+
* This ensures that when user adjusts time, the date disable check is not affected by the time portion.
|
|
1053
|
+
* For example, if the user selects start date X and end date X+7 (exactly 7 days), then adjusts
|
|
1054
|
+
* the end time to 10:00, the end date should still be valid, not be incorrectly judged as disabled.
|
|
1055
|
+
*/
|
|
1056
|
+
const dateToCheck = this._isRangeType() && (0, _dateFns.isValid)(date) ? (0, _dateFns.startOfDay)(date) : date;
|
|
1057
|
+
if (this.disabledDisposeDate(dateToCheck, disabledOptions)) {
|
|
1058
|
+
isSomeDateDisabled = true;
|
|
1059
|
+
break;
|
|
1060
|
+
}
|
|
1052
1061
|
}
|
|
1053
1062
|
}
|
|
1054
1063
|
return isSomeDateDisabled;
|
|
@@ -61,9 +61,18 @@ class InputFoundation extends _foundation.default {
|
|
|
61
61
|
this._adapter.notifyCompositionStart(e);
|
|
62
62
|
};
|
|
63
63
|
this.handleCompositionEnd = e => {
|
|
64
|
+
const {
|
|
65
|
+
composition
|
|
66
|
+
} = this._adapter.getProps();
|
|
64
67
|
const value = e.target.value;
|
|
65
68
|
this.compositionEnter = false;
|
|
66
69
|
this._adapter.notifyCompositionEnd(e);
|
|
70
|
+
// When composition is enabled, trigger onChange after IME composition ends
|
|
71
|
+
if (composition) {
|
|
72
|
+
const nextValue = this.getNextValue(value);
|
|
73
|
+
this.changeInput(nextValue, e);
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
67
76
|
const {
|
|
68
77
|
getValueLength,
|
|
69
78
|
maxLength,
|
|
@@ -95,7 +104,17 @@ class InputFoundation extends _foundation.default {
|
|
|
95
104
|
this._adapter.setValue(value);
|
|
96
105
|
}
|
|
97
106
|
handleChange(value, e) {
|
|
107
|
+
const {
|
|
108
|
+
composition
|
|
109
|
+
} = this._adapter.getProps();
|
|
98
110
|
let nextValue = value;
|
|
111
|
+
// When composition is enabled and in IME composing, only update internal state without triggering onChange
|
|
112
|
+
// We still need to update internal state for both controlled and uncontrolled components,
|
|
113
|
+
// so that the input can correctly display the composing text
|
|
114
|
+
if (composition && this.compositionEnter) {
|
|
115
|
+
this._adapter.setValue(nextValue);
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
99
118
|
if (!this.compositionEnter) {
|
|
100
119
|
nextValue = this.getNextValue(nextValue);
|
|
101
120
|
}
|
|
@@ -59,8 +59,18 @@ class TextAreaFoundation extends _foundation.default {
|
|
|
59
59
|
this._adapter.notifyCompositionStart(e);
|
|
60
60
|
};
|
|
61
61
|
this.handleCompositionEnd = e => {
|
|
62
|
+
const {
|
|
63
|
+
composition
|
|
64
|
+
} = this._adapter.getProps();
|
|
65
|
+
const value = e.target.value;
|
|
62
66
|
this.compositionEnter = false;
|
|
63
67
|
this._adapter.notifyCompositionEnd(e);
|
|
68
|
+
// When composition is enabled, trigger onChange after IME composition ends
|
|
69
|
+
if (composition) {
|
|
70
|
+
const nextValue = this.getNextValue(value);
|
|
71
|
+
this._changeValue(nextValue, e);
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
64
74
|
const {
|
|
65
75
|
getValueLength,
|
|
66
76
|
maxLength,
|
|
@@ -69,7 +79,6 @@ class TextAreaFoundation extends _foundation.default {
|
|
|
69
79
|
if (!(0, _isFunction2.default)(getValueLength)) {
|
|
70
80
|
return;
|
|
71
81
|
}
|
|
72
|
-
const value = e.target.value;
|
|
73
82
|
if (maxLength) {
|
|
74
83
|
const nextValue = this.handleVisibleMaxLength(value);
|
|
75
84
|
nextValue !== value && this._changeValue(nextValue, e);
|
|
@@ -110,11 +119,16 @@ class TextAreaFoundation extends _foundation.default {
|
|
|
110
119
|
}
|
|
111
120
|
handleChange(value, e) {
|
|
112
121
|
const {
|
|
113
|
-
|
|
114
|
-
minLength,
|
|
115
|
-
getValueLength
|
|
122
|
+
composition
|
|
116
123
|
} = this._adapter.getProps();
|
|
117
124
|
let nextValue = value;
|
|
125
|
+
// When composition is enabled and in IME composing, only update internal state without triggering onChange
|
|
126
|
+
// We still need to update internal state for both controlled and uncontrolled components,
|
|
127
|
+
// so that the textarea can correctly display the composing text
|
|
128
|
+
if (composition && this.compositionEnter) {
|
|
129
|
+
this._adapter.setValue(nextValue);
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
118
132
|
if (!this.compositionEnter) {
|
|
119
133
|
nextValue = this.getNextValue(nextValue);
|
|
120
134
|
}
|
|
@@ -346,7 +346,21 @@ class InputNumberFoundation extends _foundation.default {
|
|
|
346
346
|
value
|
|
347
347
|
} = this.getProps();
|
|
348
348
|
const propsValue = this._isControlledComponent('value') ? value : defaultValue;
|
|
349
|
-
|
|
349
|
+
// Align init logic with controlled update logic in semi-ui:
|
|
350
|
+
// - When propsValue is a number, we should apply formatter first (doFormat)
|
|
351
|
+
// so that parser/formatter pairs like percentage (value=1 => display=100)
|
|
352
|
+
// work correctly on first mount.
|
|
353
|
+
// - When propsValue is a string, keep original behavior.
|
|
354
|
+
let valueStr = propsValue;
|
|
355
|
+
// NOTE: Currency mode should keep the original behavior (number stays number) because
|
|
356
|
+
// parseInternationalCurrency expects locale-formatted strings and relies on symbols
|
|
357
|
+
// computed during init().
|
|
358
|
+
if (typeof propsValue === 'number' && !this._isCurrency()) {
|
|
359
|
+
valueStr = this.doFormat(propsValue);
|
|
360
|
+
} else if (!this._isCurrency()) {
|
|
361
|
+
valueStr = (0, _toString2.default)(propsValue);
|
|
362
|
+
}
|
|
363
|
+
const tmpNumber = this.doParse(valueStr, false, true, true);
|
|
350
364
|
let number = null;
|
|
351
365
|
if (typeof tmpNumber === 'number' && !isNaN(tmpNumber)) {
|
|
352
366
|
number = tmpNumber;
|
|
@@ -524,7 +538,8 @@ class InputNumberFoundation extends _foundation.default {
|
|
|
524
538
|
}
|
|
525
539
|
const parser = this.getProp('parser');
|
|
526
540
|
if (typeof parser === 'function') {
|
|
527
|
-
|
|
541
|
+
const parsedValue = parser(value);
|
|
542
|
+
value = typeof parsedValue === 'number' ? (0, _toString2.default)(parsedValue) : parsedValue;
|
|
528
543
|
}
|
|
529
544
|
if (needCheckPrec && typeof value === 'string') {
|
|
530
545
|
const zeroIsValid = value.indexOf('.') === -1 || value.indexOf('.') > -1 && (value === '0' || value.lastIndexOf('0') < value.length - 1);
|
|
@@ -568,7 +583,8 @@ class InputNumberFoundation extends _foundation.default {
|
|
|
568
583
|
value = this.parseInternationalCurrency(value);
|
|
569
584
|
}
|
|
570
585
|
if (typeof parser === 'function') {
|
|
571
|
-
|
|
586
|
+
const parsedValue = parser(value);
|
|
587
|
+
value = typeof parsedValue === 'number' ? (0, _toString2.default)(parsedValue) : parsedValue;
|
|
572
588
|
}
|
|
573
589
|
if (needAdjustPrec) {
|
|
574
590
|
value = this._adjustPrec(value);
|
|
@@ -7,6 +7,7 @@ export interface BasicDataItem {
|
|
|
7
7
|
value?: string | number;
|
|
8
8
|
disabled?: boolean;
|
|
9
9
|
style?: any;
|
|
10
|
+
fullPath?: Array<BasicDataItem>;
|
|
10
11
|
className?: string;
|
|
11
12
|
}
|
|
12
13
|
export type DataItemMap = Map<number | string, BasicDataItem>;
|
|
@@ -36,6 +37,17 @@ export default class TransferFoundation<P = Record<string, any>, S = Record<stri
|
|
|
36
37
|
_generateGroupedData(dataSource: any[]): any[];
|
|
37
38
|
_generateTreeData(dataSource: any[]): any[];
|
|
38
39
|
_generatePath(item: BasicResolvedDataItem): any;
|
|
40
|
+
_getFullPath(item: BasicResolvedDataItem): {
|
|
41
|
+
[x: string]: any;
|
|
42
|
+
key: string | number;
|
|
43
|
+
label?: any;
|
|
44
|
+
value?: string | number;
|
|
45
|
+
disabled?: boolean;
|
|
46
|
+
style?: any;
|
|
47
|
+
fullPath?: BasicDataItem[];
|
|
48
|
+
className?: string;
|
|
49
|
+
}[];
|
|
50
|
+
_injectFullPath(item: BasicResolvedDataItem): BasicResolvedDataItem;
|
|
39
51
|
handleInputChange(inputVal: string, notify: boolean): void;
|
|
40
52
|
handleAll(wantAllChecked: boolean): void;
|
|
41
53
|
handleClear(): void;
|
|
@@ -26,6 +26,25 @@ class TransferFoundation extends _foundation.default {
|
|
|
26
26
|
} = item;
|
|
27
27
|
return path.map(p => p.label).join(' > ');
|
|
28
28
|
}
|
|
29
|
+
_getFullPath(item) {
|
|
30
|
+
const {
|
|
31
|
+
type,
|
|
32
|
+
showPath
|
|
33
|
+
} = this.getProps();
|
|
34
|
+
if (type !== _constants.strings.TYPE_TREE_TO_LIST || showPath !== true || !Array.isArray(item.path)) {
|
|
35
|
+
return undefined;
|
|
36
|
+
}
|
|
37
|
+
return item.path.map(pathItem => Object.assign({}, pathItem));
|
|
38
|
+
}
|
|
39
|
+
_injectFullPath(item) {
|
|
40
|
+
const fullPath = this._getFullPath(item);
|
|
41
|
+
if (!fullPath) {
|
|
42
|
+
return item;
|
|
43
|
+
}
|
|
44
|
+
return Object.assign(Object.assign({}, item), {
|
|
45
|
+
fullPath
|
|
46
|
+
});
|
|
47
|
+
}
|
|
29
48
|
handleInputChange(inputVal, notify) {
|
|
30
49
|
const {
|
|
31
50
|
data
|
|
@@ -128,15 +147,16 @@ class TransferFoundation extends _foundation.default {
|
|
|
128
147
|
disabled
|
|
129
148
|
} = this.getProps();
|
|
130
149
|
const selectedItems = this._adapter.getSelected();
|
|
150
|
+
const changedItem = this._injectFullPath(item);
|
|
131
151
|
if (disabled || item.disabled) {
|
|
132
152
|
return;
|
|
133
153
|
}
|
|
134
154
|
if (selectedItems.has(item.key)) {
|
|
135
155
|
selectedItems.delete(item.key);
|
|
136
|
-
this._adapter.notifyDeselect(
|
|
156
|
+
this._adapter.notifyDeselect(changedItem);
|
|
137
157
|
} else {
|
|
138
158
|
selectedItems.set(item.key, item);
|
|
139
|
-
this._adapter.notifySelect(
|
|
159
|
+
this._adapter.notifySelect(changedItem);
|
|
140
160
|
}
|
|
141
161
|
if (!this._isControlledComponent()) {
|
|
142
162
|
this._adapter.updateSelected(selectedItems);
|
|
@@ -182,7 +202,8 @@ class TransferFoundation extends _foundation.default {
|
|
|
182
202
|
const items = [];
|
|
183
203
|
const values = [];
|
|
184
204
|
for (const item of selectedItems) {
|
|
185
|
-
|
|
205
|
+
let obj = type === _constants.strings.TYPE_GROUP_LIST ? (0, _omit2.default)(item[1], '_parent') : item[1];
|
|
206
|
+
obj = this._injectFullPath(obj);
|
|
186
207
|
items.push(obj);
|
|
187
208
|
values.push(obj.value);
|
|
188
209
|
}
|
|
@@ -44,11 +44,11 @@ export interface AfterUploadResult {
|
|
|
44
44
|
}
|
|
45
45
|
export interface UploadAdapter<P = Record<string, any>, S = Record<string, any>> extends DefaultAdapter<P, S> {
|
|
46
46
|
notifyFileSelect: (files: Array<CustomFile>) => void;
|
|
47
|
-
notifyError: (error: XhrError, fileInstance:
|
|
48
|
-
notifySuccess: (body: any, fileInstance:
|
|
49
|
-
notifyProgress: (percent: number, fileInstance:
|
|
50
|
-
notifyRemove: (file:
|
|
51
|
-
notifySizeError: (file:
|
|
47
|
+
notifyError: (error: XhrError, fileInstance: CustomFile, fileList: Array<BaseFileItem>, xhr: XMLHttpRequest) => void;
|
|
48
|
+
notifySuccess: (body: any, fileInstance: CustomFile, newFileList: Array<BaseFileItem>) => void;
|
|
49
|
+
notifyProgress: (percent: number, fileInstance: CustomFile, newFileList: Array<BaseFileItem>) => void;
|
|
50
|
+
notifyRemove: (file: CustomFile, newFileList: Array<BaseFileItem>, fileItem: BaseFileItem) => void;
|
|
51
|
+
notifySizeError: (file: CustomFile, fileList: Array<BaseFileItem>) => void;
|
|
52
52
|
notifyExceed: (files: Array<File>) => void;
|
|
53
53
|
updateFileList: (newFileList: Array<BaseFileItem>, callback?: () => void) => void;
|
|
54
54
|
notifyBeforeUpload: ({ file, fileList }: {
|
|
@@ -3,7 +3,7 @@ import _isEqual from "lodash/isEqual";
|
|
|
3
3
|
import _isString from "lodash/isString";
|
|
4
4
|
import _isObject from "lodash/isObject";
|
|
5
5
|
import _get from "lodash/get";
|
|
6
|
-
import { format, isValid, isSameSecond, isEqual as isDateEqual, isDate } from 'date-fns';
|
|
6
|
+
import { format, isValid, isSameSecond, isEqual as isDateEqual, isDate, startOfDay } from 'date-fns';
|
|
7
7
|
import BaseFoundation from '../base/foundation';
|
|
8
8
|
import { isValidDate, isTimestamp } from './_utils/index';
|
|
9
9
|
import isNullOrUndefined from '../utils/isNullOrUndefined';
|
|
@@ -1039,9 +1039,18 @@ export default class DatePickerFoundation extends BaseFoundation {
|
|
|
1039
1039
|
let isSomeDateDisabled = false;
|
|
1040
1040
|
for (const date of value) {
|
|
1041
1041
|
// skip check if date is null
|
|
1042
|
-
if (!isNullOrUndefined(date)
|
|
1043
|
-
|
|
1044
|
-
|
|
1042
|
+
if (!isNullOrUndefined(date)) {
|
|
1043
|
+
/**
|
|
1044
|
+
* When type is dateTimeRange, disabledDate should only consider the date part, not the time part.
|
|
1045
|
+
* This ensures that when user adjusts time, the date disable check is not affected by the time portion.
|
|
1046
|
+
* For example, if the user selects start date X and end date X+7 (exactly 7 days), then adjusts
|
|
1047
|
+
* the end time to 10:00, the end date should still be valid, not be incorrectly judged as disabled.
|
|
1048
|
+
*/
|
|
1049
|
+
const dateToCheck = this._isRangeType() && isValid(date) ? startOfDay(date) : date;
|
|
1050
|
+
if (this.disabledDisposeDate(dateToCheck, disabledOptions)) {
|
|
1051
|
+
isSomeDateDisabled = true;
|
|
1052
|
+
break;
|
|
1053
|
+
}
|
|
1045
1054
|
}
|
|
1046
1055
|
}
|
|
1047
1056
|
return isSomeDateDisabled;
|
|
@@ -54,9 +54,18 @@ class InputFoundation extends BaseFoundation {
|
|
|
54
54
|
this._adapter.notifyCompositionStart(e);
|
|
55
55
|
};
|
|
56
56
|
this.handleCompositionEnd = e => {
|
|
57
|
+
const {
|
|
58
|
+
composition
|
|
59
|
+
} = this._adapter.getProps();
|
|
57
60
|
const value = e.target.value;
|
|
58
61
|
this.compositionEnter = false;
|
|
59
62
|
this._adapter.notifyCompositionEnd(e);
|
|
63
|
+
// When composition is enabled, trigger onChange after IME composition ends
|
|
64
|
+
if (composition) {
|
|
65
|
+
const nextValue = this.getNextValue(value);
|
|
66
|
+
this.changeInput(nextValue, e);
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
60
69
|
const {
|
|
61
70
|
getValueLength,
|
|
62
71
|
maxLength,
|
|
@@ -88,7 +97,17 @@ class InputFoundation extends BaseFoundation {
|
|
|
88
97
|
this._adapter.setValue(value);
|
|
89
98
|
}
|
|
90
99
|
handleChange(value, e) {
|
|
100
|
+
const {
|
|
101
|
+
composition
|
|
102
|
+
} = this._adapter.getProps();
|
|
91
103
|
let nextValue = value;
|
|
104
|
+
// When composition is enabled and in IME composing, only update internal state without triggering onChange
|
|
105
|
+
// We still need to update internal state for both controlled and uncontrolled components,
|
|
106
|
+
// so that the input can correctly display the composing text
|
|
107
|
+
if (composition && this.compositionEnter) {
|
|
108
|
+
this._adapter.setValue(nextValue);
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
92
111
|
if (!this.compositionEnter) {
|
|
93
112
|
nextValue = this.getNextValue(nextValue);
|
|
94
113
|
}
|
|
@@ -52,8 +52,18 @@ export default class TextAreaFoundation extends BaseFoundation {
|
|
|
52
52
|
this._adapter.notifyCompositionStart(e);
|
|
53
53
|
};
|
|
54
54
|
this.handleCompositionEnd = e => {
|
|
55
|
+
const {
|
|
56
|
+
composition
|
|
57
|
+
} = this._adapter.getProps();
|
|
58
|
+
const value = e.target.value;
|
|
55
59
|
this.compositionEnter = false;
|
|
56
60
|
this._adapter.notifyCompositionEnd(e);
|
|
61
|
+
// When composition is enabled, trigger onChange after IME composition ends
|
|
62
|
+
if (composition) {
|
|
63
|
+
const nextValue = this.getNextValue(value);
|
|
64
|
+
this._changeValue(nextValue, e);
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
57
67
|
const {
|
|
58
68
|
getValueLength,
|
|
59
69
|
maxLength,
|
|
@@ -62,7 +72,6 @@ export default class TextAreaFoundation extends BaseFoundation {
|
|
|
62
72
|
if (!_isFunction(getValueLength)) {
|
|
63
73
|
return;
|
|
64
74
|
}
|
|
65
|
-
const value = e.target.value;
|
|
66
75
|
if (maxLength) {
|
|
67
76
|
const nextValue = this.handleVisibleMaxLength(value);
|
|
68
77
|
nextValue !== value && this._changeValue(nextValue, e);
|
|
@@ -103,11 +112,16 @@ export default class TextAreaFoundation extends BaseFoundation {
|
|
|
103
112
|
}
|
|
104
113
|
handleChange(value, e) {
|
|
105
114
|
const {
|
|
106
|
-
|
|
107
|
-
minLength,
|
|
108
|
-
getValueLength
|
|
115
|
+
composition
|
|
109
116
|
} = this._adapter.getProps();
|
|
110
117
|
let nextValue = value;
|
|
118
|
+
// When composition is enabled and in IME composing, only update internal state without triggering onChange
|
|
119
|
+
// We still need to update internal state for both controlled and uncontrolled components,
|
|
120
|
+
// so that the textarea can correctly display the composing text
|
|
121
|
+
if (composition && this.compositionEnter) {
|
|
122
|
+
this._adapter.setValue(nextValue);
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
111
125
|
if (!this.compositionEnter) {
|
|
112
126
|
nextValue = this.getNextValue(nextValue);
|
|
113
127
|
}
|
|
@@ -339,7 +339,21 @@ class InputNumberFoundation extends BaseFoundation {
|
|
|
339
339
|
value
|
|
340
340
|
} = this.getProps();
|
|
341
341
|
const propsValue = this._isControlledComponent('value') ? value : defaultValue;
|
|
342
|
-
|
|
342
|
+
// Align init logic with controlled update logic in semi-ui:
|
|
343
|
+
// - When propsValue is a number, we should apply formatter first (doFormat)
|
|
344
|
+
// so that parser/formatter pairs like percentage (value=1 => display=100)
|
|
345
|
+
// work correctly on first mount.
|
|
346
|
+
// - When propsValue is a string, keep original behavior.
|
|
347
|
+
let valueStr = propsValue;
|
|
348
|
+
// NOTE: Currency mode should keep the original behavior (number stays number) because
|
|
349
|
+
// parseInternationalCurrency expects locale-formatted strings and relies on symbols
|
|
350
|
+
// computed during init().
|
|
351
|
+
if (typeof propsValue === 'number' && !this._isCurrency()) {
|
|
352
|
+
valueStr = this.doFormat(propsValue);
|
|
353
|
+
} else if (!this._isCurrency()) {
|
|
354
|
+
valueStr = _toString(propsValue);
|
|
355
|
+
}
|
|
356
|
+
const tmpNumber = this.doParse(valueStr, false, true, true);
|
|
343
357
|
let number = null;
|
|
344
358
|
if (typeof tmpNumber === 'number' && !isNaN(tmpNumber)) {
|
|
345
359
|
number = tmpNumber;
|
|
@@ -517,7 +531,8 @@ class InputNumberFoundation extends BaseFoundation {
|
|
|
517
531
|
}
|
|
518
532
|
const parser = this.getProp('parser');
|
|
519
533
|
if (typeof parser === 'function') {
|
|
520
|
-
|
|
534
|
+
const parsedValue = parser(value);
|
|
535
|
+
value = typeof parsedValue === 'number' ? _toString(parsedValue) : parsedValue;
|
|
521
536
|
}
|
|
522
537
|
if (needCheckPrec && typeof value === 'string') {
|
|
523
538
|
const zeroIsValid = value.indexOf('.') === -1 || value.indexOf('.') > -1 && (value === '0' || value.lastIndexOf('0') < value.length - 1);
|
|
@@ -561,7 +576,8 @@ class InputNumberFoundation extends BaseFoundation {
|
|
|
561
576
|
value = this.parseInternationalCurrency(value);
|
|
562
577
|
}
|
|
563
578
|
if (typeof parser === 'function') {
|
|
564
|
-
|
|
579
|
+
const parsedValue = parser(value);
|
|
580
|
+
value = typeof parsedValue === 'number' ? _toString(parsedValue) : parsedValue;
|
|
565
581
|
}
|
|
566
582
|
if (needAdjustPrec) {
|
|
567
583
|
value = this._adjustPrec(value);
|
|
@@ -7,6 +7,7 @@ export interface BasicDataItem {
|
|
|
7
7
|
value?: string | number;
|
|
8
8
|
disabled?: boolean;
|
|
9
9
|
style?: any;
|
|
10
|
+
fullPath?: Array<BasicDataItem>;
|
|
10
11
|
className?: string;
|
|
11
12
|
}
|
|
12
13
|
export type DataItemMap = Map<number | string, BasicDataItem>;
|
|
@@ -36,6 +37,17 @@ export default class TransferFoundation<P = Record<string, any>, S = Record<stri
|
|
|
36
37
|
_generateGroupedData(dataSource: any[]): any[];
|
|
37
38
|
_generateTreeData(dataSource: any[]): any[];
|
|
38
39
|
_generatePath(item: BasicResolvedDataItem): any;
|
|
40
|
+
_getFullPath(item: BasicResolvedDataItem): {
|
|
41
|
+
[x: string]: any;
|
|
42
|
+
key: string | number;
|
|
43
|
+
label?: any;
|
|
44
|
+
value?: string | number;
|
|
45
|
+
disabled?: boolean;
|
|
46
|
+
style?: any;
|
|
47
|
+
fullPath?: BasicDataItem[];
|
|
48
|
+
className?: string;
|
|
49
|
+
}[];
|
|
50
|
+
_injectFullPath(item: BasicResolvedDataItem): BasicResolvedDataItem;
|
|
39
51
|
handleInputChange(inputVal: string, notify: boolean): void;
|
|
40
52
|
handleAll(wantAllChecked: boolean): void;
|
|
41
53
|
handleClear(): void;
|
|
@@ -19,6 +19,25 @@ export default class TransferFoundation extends BaseFoundation {
|
|
|
19
19
|
} = item;
|
|
20
20
|
return path.map(p => p.label).join(' > ');
|
|
21
21
|
}
|
|
22
|
+
_getFullPath(item) {
|
|
23
|
+
const {
|
|
24
|
+
type,
|
|
25
|
+
showPath
|
|
26
|
+
} = this.getProps();
|
|
27
|
+
if (type !== strings.TYPE_TREE_TO_LIST || showPath !== true || !Array.isArray(item.path)) {
|
|
28
|
+
return undefined;
|
|
29
|
+
}
|
|
30
|
+
return item.path.map(pathItem => Object.assign({}, pathItem));
|
|
31
|
+
}
|
|
32
|
+
_injectFullPath(item) {
|
|
33
|
+
const fullPath = this._getFullPath(item);
|
|
34
|
+
if (!fullPath) {
|
|
35
|
+
return item;
|
|
36
|
+
}
|
|
37
|
+
return Object.assign(Object.assign({}, item), {
|
|
38
|
+
fullPath
|
|
39
|
+
});
|
|
40
|
+
}
|
|
22
41
|
handleInputChange(inputVal, notify) {
|
|
23
42
|
const {
|
|
24
43
|
data
|
|
@@ -121,15 +140,16 @@ export default class TransferFoundation extends BaseFoundation {
|
|
|
121
140
|
disabled
|
|
122
141
|
} = this.getProps();
|
|
123
142
|
const selectedItems = this._adapter.getSelected();
|
|
143
|
+
const changedItem = this._injectFullPath(item);
|
|
124
144
|
if (disabled || item.disabled) {
|
|
125
145
|
return;
|
|
126
146
|
}
|
|
127
147
|
if (selectedItems.has(item.key)) {
|
|
128
148
|
selectedItems.delete(item.key);
|
|
129
|
-
this._adapter.notifyDeselect(
|
|
149
|
+
this._adapter.notifyDeselect(changedItem);
|
|
130
150
|
} else {
|
|
131
151
|
selectedItems.set(item.key, item);
|
|
132
|
-
this._adapter.notifySelect(
|
|
152
|
+
this._adapter.notifySelect(changedItem);
|
|
133
153
|
}
|
|
134
154
|
if (!this._isControlledComponent()) {
|
|
135
155
|
this._adapter.updateSelected(selectedItems);
|
|
@@ -175,7 +195,8 @@ export default class TransferFoundation extends BaseFoundation {
|
|
|
175
195
|
const items = [];
|
|
176
196
|
const values = [];
|
|
177
197
|
for (const item of selectedItems) {
|
|
178
|
-
|
|
198
|
+
let obj = type === strings.TYPE_GROUP_LIST ? _omit(item[1], '_parent') : item[1];
|
|
199
|
+
obj = this._injectFullPath(obj);
|
|
179
200
|
items.push(obj);
|
|
180
201
|
values.push(obj.value);
|
|
181
202
|
}
|
|
@@ -44,11 +44,11 @@ export interface AfterUploadResult {
|
|
|
44
44
|
}
|
|
45
45
|
export interface UploadAdapter<P = Record<string, any>, S = Record<string, any>> extends DefaultAdapter<P, S> {
|
|
46
46
|
notifyFileSelect: (files: Array<CustomFile>) => void;
|
|
47
|
-
notifyError: (error: XhrError, fileInstance:
|
|
48
|
-
notifySuccess: (body: any, fileInstance:
|
|
49
|
-
notifyProgress: (percent: number, fileInstance:
|
|
50
|
-
notifyRemove: (file:
|
|
51
|
-
notifySizeError: (file:
|
|
47
|
+
notifyError: (error: XhrError, fileInstance: CustomFile, fileList: Array<BaseFileItem>, xhr: XMLHttpRequest) => void;
|
|
48
|
+
notifySuccess: (body: any, fileInstance: CustomFile, newFileList: Array<BaseFileItem>) => void;
|
|
49
|
+
notifyProgress: (percent: number, fileInstance: CustomFile, newFileList: Array<BaseFileItem>) => void;
|
|
50
|
+
notifyRemove: (file: CustomFile, newFileList: Array<BaseFileItem>, fileItem: BaseFileItem) => void;
|
|
51
|
+
notifySizeError: (file: CustomFile, fileList: Array<BaseFileItem>) => void;
|
|
52
52
|
notifyExceed: (files: Array<File>) => void;
|
|
53
53
|
updateFileList: (newFileList: Array<BaseFileItem>, callback?: () => void) => void;
|
|
54
54
|
notifyBeforeUpload: ({ file, fileList }: {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@douyinfe/semi-foundation",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.95.0",
|
|
4
4
|
"description": "",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"clean": "rimraf lib",
|
|
@@ -14210,8 +14210,8 @@
|
|
|
14210
14210
|
}
|
|
14211
14211
|
},
|
|
14212
14212
|
"dependencies": {
|
|
14213
|
-
"@douyinfe/semi-animation": "2.
|
|
14214
|
-
"@douyinfe/semi-json-viewer-core": "2.
|
|
14213
|
+
"@douyinfe/semi-animation": "2.95.0",
|
|
14214
|
+
"@douyinfe/semi-json-viewer-core": "2.95.0",
|
|
14215
14215
|
"@mdx-js/mdx": "^3.0.1",
|
|
14216
14216
|
"async-validator": "^3.5.0",
|
|
14217
14217
|
"classnames": "^2.2.6",
|
|
@@ -14232,7 +14232,7 @@
|
|
|
14232
14232
|
"*.scss",
|
|
14233
14233
|
"*.css"
|
|
14234
14234
|
],
|
|
14235
|
-
"gitHead": "
|
|
14235
|
+
"gitHead": "557af73624148fb5753012118f4170ef191b4cf9",
|
|
14236
14236
|
"devDependencies": {
|
|
14237
14237
|
"@babel/plugin-transform-runtime": "^7.15.8",
|
|
14238
14238
|
"@babel/preset-env": "^7.15.8",
|
package/transfer/foundation.ts
CHANGED
|
@@ -13,6 +13,7 @@ export interface BasicDataItem {
|
|
|
13
13
|
value?: string | number;
|
|
14
14
|
disabled?: boolean;
|
|
15
15
|
style?: any;
|
|
16
|
+
fullPath?: Array<BasicDataItem>;
|
|
16
17
|
className?: string
|
|
17
18
|
}
|
|
18
19
|
|
|
@@ -60,6 +61,29 @@ export default class TransferFoundation<P = Record<string, any>, S = Record<stri
|
|
|
60
61
|
return path.map((p: any) => p.label).join(' > ');
|
|
61
62
|
}
|
|
62
63
|
|
|
64
|
+
_getFullPath(item: BasicResolvedDataItem) {
|
|
65
|
+
const { type, showPath } = this.getProps() as { type?: string; showPath?: boolean };
|
|
66
|
+
|
|
67
|
+
if (type !== strings.TYPE_TREE_TO_LIST || showPath !== true || !Array.isArray(item.path)) {
|
|
68
|
+
return undefined;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
return item.path.map((pathItem: BasicDataItem) => ({ ...pathItem }));
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
_injectFullPath(item: BasicResolvedDataItem) {
|
|
75
|
+
const fullPath = this._getFullPath(item);
|
|
76
|
+
|
|
77
|
+
if (!fullPath) {
|
|
78
|
+
return item;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
return {
|
|
82
|
+
...item,
|
|
83
|
+
fullPath,
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
|
|
63
87
|
handleInputChange(inputVal: string, notify: boolean) {
|
|
64
88
|
const { data } = this.getStates();
|
|
65
89
|
const { filter, type } = this.getProps();
|
|
@@ -148,15 +172,16 @@ export default class TransferFoundation<P = Record<string, any>, S = Record<stri
|
|
|
148
172
|
handleSelectOrRemove(item: BasicResolvedDataItem) {
|
|
149
173
|
const { disabled } = this.getProps();
|
|
150
174
|
const selectedItems = this._adapter.getSelected();
|
|
175
|
+
const changedItem = this._injectFullPath(item);
|
|
151
176
|
if (disabled || item.disabled) {
|
|
152
177
|
return;
|
|
153
178
|
}
|
|
154
179
|
if (selectedItems.has(item.key)) {
|
|
155
180
|
selectedItems.delete(item.key);
|
|
156
|
-
this._adapter.notifyDeselect(
|
|
181
|
+
this._adapter.notifyDeselect(changedItem);
|
|
157
182
|
} else {
|
|
158
183
|
selectedItems.set(item.key, item);
|
|
159
|
-
this._adapter.notifySelect(
|
|
184
|
+
this._adapter.notifySelect(changedItem);
|
|
160
185
|
}
|
|
161
186
|
if (!this._isControlledComponent()) {
|
|
162
187
|
this._adapter.updateSelected(selectedItems);
|
|
@@ -201,7 +226,9 @@ export default class TransferFoundation<P = Record<string, any>, S = Record<stri
|
|
|
201
226
|
const items = [];
|
|
202
227
|
const values = [];
|
|
203
228
|
for (const item of selectedItems) {
|
|
204
|
-
|
|
229
|
+
let obj = (type === strings.TYPE_GROUP_LIST ? omit(item[1], '_parent') : item[1]) as BasicResolvedDataItem;
|
|
230
|
+
obj = this._injectFullPath(obj);
|
|
231
|
+
|
|
205
232
|
items.push(obj);
|
|
206
233
|
values.push(obj.value);
|
|
207
234
|
}
|
package/upload/foundation.ts
CHANGED
|
@@ -66,11 +66,11 @@ export interface AfterUploadResult {
|
|
|
66
66
|
|
|
67
67
|
export interface UploadAdapter<P = Record<string, any>, S = Record<string, any>> extends DefaultAdapter<P, S> {
|
|
68
68
|
notifyFileSelect: (files: Array<CustomFile>) => void;
|
|
69
|
-
notifyError: (error: XhrError, fileInstance:
|
|
70
|
-
notifySuccess: (body: any, fileInstance:
|
|
71
|
-
notifyProgress: (percent: number, fileInstance:
|
|
72
|
-
notifyRemove: (file:
|
|
73
|
-
notifySizeError: (file:
|
|
69
|
+
notifyError: (error: XhrError, fileInstance: CustomFile, fileList: Array<BaseFileItem>, xhr: XMLHttpRequest) => void;
|
|
70
|
+
notifySuccess: (body: any, fileInstance: CustomFile, newFileList: Array<BaseFileItem>) => void;
|
|
71
|
+
notifyProgress: (percent: number, fileInstance: CustomFile, newFileList: Array<BaseFileItem>) => void;
|
|
72
|
+
notifyRemove: (file: CustomFile, newFileList: Array<BaseFileItem>, fileItem: BaseFileItem) => void;
|
|
73
|
+
notifySizeError: (file: CustomFile, fileList: Array<BaseFileItem>) => void;
|
|
74
74
|
notifyExceed: (files: Array<File>) => void;
|
|
75
75
|
updateFileList: (newFileList: Array<BaseFileItem>, callback?: () => void) => void;
|
|
76
76
|
notifyBeforeUpload: ({ file, fileList }: { file: BaseFileItem; fileList: Array<BaseFileItem> }) => boolean | BeforeUploadObjectResult | Promise<BeforeUploadObjectResult>;
|