@douyinfe/semi-foundation 2.96.1 → 2.97.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/cascader/foundation.ts +74 -19
- package/datePicker/datePicker.scss +100 -5
- package/form/foundation.ts +7 -3
- package/form/utils.ts +7 -2
- package/image/previewImageFoundation.ts +34 -3
- package/image/previewInnerFoundation.ts +15 -4
- package/input/textarea.scss +35 -0
- package/lib/cjs/cascader/foundation.d.ts +12 -0
- package/lib/cjs/cascader/foundation.js +68 -23
- package/lib/cjs/datePicker/datePicker.css +67 -5
- package/lib/cjs/datePicker/datePicker.scss +100 -5
- package/lib/cjs/form/foundation.d.ts +1 -1
- package/lib/cjs/form/foundation.js +6 -6
- package/lib/cjs/form/utils.js +5 -2
- package/lib/cjs/image/previewImageFoundation.d.ts +4 -0
- package/lib/cjs/image/previewImageFoundation.js +33 -2
- package/lib/cjs/image/previewInnerFoundation.d.ts +1 -0
- package/lib/cjs/image/previewInnerFoundation.js +17 -4
- package/lib/cjs/input/textarea.css +17 -0
- package/lib/cjs/input/textarea.scss +35 -0
- package/lib/cjs/navigation/navigation.css +2 -1
- package/lib/cjs/navigation/navigation.scss +1 -0
- package/lib/cjs/navigation/variables.scss +1 -1
- package/lib/cjs/overflowList/foundation.d.ts +1 -0
- package/lib/cjs/overflowList/foundation.js +51 -1
- package/lib/cjs/select/foundation.d.ts +1 -1
- package/lib/cjs/select/foundation.js +28 -2
- package/lib/cjs/switch/switch.css +1 -0
- package/lib/cjs/switch/switch.scss +1 -0
- package/lib/cjs/switch/variables.scss +2 -1
- package/lib/cjs/table/foundation.js +2 -1
- package/lib/cjs/tag/tag.css +26 -0
- package/lib/cjs/tag/tag.scss +33 -0
- package/lib/cjs/tagInput/tagInput.css +17 -0
- package/lib/cjs/tagInput/tagInput.scss +18 -0
- package/lib/cjs/timePicker/constants.d.ts +1 -0
- package/lib/cjs/timePicker/foundation.d.ts +7 -1
- package/lib/cjs/timePicker/foundation.js +62 -11
- package/lib/es/cascader/foundation.d.ts +12 -0
- package/lib/es/cascader/foundation.js +68 -23
- package/lib/es/datePicker/datePicker.css +67 -5
- package/lib/es/datePicker/datePicker.scss +100 -5
- package/lib/es/form/foundation.d.ts +1 -1
- package/lib/es/form/foundation.js +6 -6
- package/lib/es/form/utils.js +5 -2
- package/lib/es/image/previewImageFoundation.d.ts +4 -0
- package/lib/es/image/previewImageFoundation.js +33 -2
- package/lib/es/image/previewInnerFoundation.d.ts +1 -0
- package/lib/es/image/previewInnerFoundation.js +17 -4
- package/lib/es/input/textarea.css +17 -0
- package/lib/es/input/textarea.scss +35 -0
- package/lib/es/navigation/navigation.css +2 -1
- package/lib/es/navigation/navigation.scss +1 -0
- package/lib/es/navigation/variables.scss +1 -1
- package/lib/es/overflowList/foundation.d.ts +1 -0
- package/lib/es/overflowList/foundation.js +51 -1
- package/lib/es/select/foundation.d.ts +1 -1
- package/lib/es/select/foundation.js +28 -2
- package/lib/es/switch/switch.css +1 -0
- package/lib/es/switch/switch.scss +1 -0
- package/lib/es/switch/variables.scss +2 -1
- package/lib/es/table/foundation.js +2 -1
- package/lib/es/tag/tag.css +26 -0
- package/lib/es/tag/tag.scss +33 -0
- package/lib/es/tagInput/tagInput.css +17 -0
- package/lib/es/tagInput/tagInput.scss +18 -0
- package/lib/es/timePicker/constants.d.ts +1 -0
- package/lib/es/timePicker/foundation.d.ts +7 -1
- package/lib/es/timePicker/foundation.js +62 -11
- package/navigation/navigation.scss +1 -0
- package/navigation/variables.scss +1 -1
- package/overflowList/foundation.ts +48 -2
- package/package.json +4 -4
- package/select/foundation.ts +27 -2
- package/switch/switch.scss +1 -0
- package/switch/variables.scss +2 -1
- package/table/foundation.ts +2 -1
- package/tag/tag.scss +33 -0
- package/tagInput/tagInput.scss +18 -0
- package/timePicker/constants.ts +2 -0
- package/timePicker/foundation.ts +62 -10
|
@@ -34,11 +34,21 @@ class OverflowListFoundation extends _foundation.default {
|
|
|
34
34
|
} = this.getProps();
|
|
35
35
|
const {
|
|
36
36
|
visibleState,
|
|
37
|
-
overflow
|
|
37
|
+
overflow,
|
|
38
|
+
scrollOverflow
|
|
38
39
|
} = this.getStates();
|
|
39
40
|
if (!this.isScrollMode()) {
|
|
40
41
|
return overflow;
|
|
41
42
|
}
|
|
43
|
+
// Scroll mode relies on IntersectionObserver to compute visibility.
|
|
44
|
+
// During recalculation (e.g. items changed, or before observer fires),
|
|
45
|
+
// keep last computed overflow to avoid UI flicker (tabs arrows/menu state).
|
|
46
|
+
if (!visibleState || visibleState.size === 0) {
|
|
47
|
+
if (Array.isArray(scrollOverflow) && scrollOverflow.length === 2) {
|
|
48
|
+
return scrollOverflow;
|
|
49
|
+
}
|
|
50
|
+
return [[], []];
|
|
51
|
+
}
|
|
42
52
|
const visibleStateArr = items.map(_ref => {
|
|
43
53
|
let {
|
|
44
54
|
key
|
|
@@ -47,11 +57,49 @@ class OverflowListFoundation extends _foundation.default {
|
|
|
47
57
|
});
|
|
48
58
|
const visibleStart = visibleStateArr.indexOf(true);
|
|
49
59
|
const visibleEnd = visibleStateArr.lastIndexOf(true);
|
|
60
|
+
// If no item is visible (e.g. initial layout not ready or list out of viewport),
|
|
61
|
+
// treat it as "unknown" and keep last computed overflow to avoid wrong enabling.
|
|
62
|
+
if (visibleStart < 0 || visibleEnd < 0) {
|
|
63
|
+
if (Array.isArray(scrollOverflow) && scrollOverflow.length === 2) {
|
|
64
|
+
return scrollOverflow;
|
|
65
|
+
}
|
|
66
|
+
return [[], []];
|
|
67
|
+
}
|
|
50
68
|
const overflowList = [];
|
|
51
69
|
overflowList[0] = visibleStart >= 0 ? items.slice(0, visibleStart) : [];
|
|
52
70
|
overflowList[1] = visibleEnd >= 0 ? items.slice(visibleEnd + 1, items.length) : items.slice();
|
|
53
71
|
return overflowList;
|
|
54
72
|
}
|
|
73
|
+
_syncScrollOverflowCache(nextVisibleState) {
|
|
74
|
+
if (!this.isScrollMode()) {
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
const {
|
|
78
|
+
items
|
|
79
|
+
} = this.getProps();
|
|
80
|
+
const visibleStateArr = items.map(_ref2 => {
|
|
81
|
+
let {
|
|
82
|
+
key
|
|
83
|
+
} = _ref2;
|
|
84
|
+
return Boolean(nextVisibleState.get(key));
|
|
85
|
+
});
|
|
86
|
+
const visibleStart = visibleStateArr.indexOf(true);
|
|
87
|
+
const visibleEnd = visibleStateArr.lastIndexOf(true);
|
|
88
|
+
// No visible items means the result is not reliable; keep cache and stay "calculating"
|
|
89
|
+
if (visibleStart < 0 || visibleEnd < 0) {
|
|
90
|
+
this._adapter.updateStates({
|
|
91
|
+
isScrollOverflowCalculating: true
|
|
92
|
+
});
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
const overflowList = [];
|
|
96
|
+
overflowList[0] = visibleStart >= 0 ? items.slice(0, visibleStart) : [];
|
|
97
|
+
overflowList[1] = visibleEnd >= 0 ? items.slice(visibleEnd + 1, items.length) : items.slice();
|
|
98
|
+
this._adapter.updateStates({
|
|
99
|
+
scrollOverflow: overflowList,
|
|
100
|
+
isScrollOverflowCalculating: false
|
|
101
|
+
});
|
|
102
|
+
}
|
|
55
103
|
handleIntersect(entries) {
|
|
56
104
|
const visibleState = (0, _fastCopy.default)(this.getState('visibleState'));
|
|
57
105
|
const res = {};
|
|
@@ -80,6 +128,8 @@ class OverflowListFoundation extends _foundation.default {
|
|
|
80
128
|
}
|
|
81
129
|
this.previousY = currentY;
|
|
82
130
|
this._adapter.updateVisibleState(visibleState);
|
|
131
|
+
// Keep scroll overflow cache in sync for stable UI
|
|
132
|
+
this._syncScrollOverflowCache(visibleState);
|
|
83
133
|
this._adapter.notifyIntersect(res);
|
|
84
134
|
}
|
|
85
135
|
handleCollapseOverflow() {
|
|
@@ -54,7 +54,7 @@ export default class SelectFoundation extends BaseFoundation<SelectAdapter> {
|
|
|
54
54
|
constructor(adapter: SelectAdapter);
|
|
55
55
|
_keydownHandler: (...arg: any[]) => void | null;
|
|
56
56
|
init(): void;
|
|
57
|
-
focus(optionsForOpen?: BasicOptionProps[]): void;
|
|
57
|
+
focus(optionsForOpen?: BasicOptionProps[], openDropdown?: boolean): void;
|
|
58
58
|
_focusTrigger(): void;
|
|
59
59
|
destroy(): void;
|
|
60
60
|
_setDropdownWidth(): void;
|
|
@@ -46,15 +46,40 @@ class SelectFoundation extends _foundation.default {
|
|
|
46
46
|
this.focus(originalOptions);
|
|
47
47
|
}
|
|
48
48
|
}
|
|
49
|
-
focus(optionsForOpen) {
|
|
49
|
+
focus(optionsForOpen, openDropdown) {
|
|
50
50
|
const isFilterable = this._isFilterable();
|
|
51
51
|
const isMultiple = this._isMultiple();
|
|
52
52
|
const {
|
|
53
53
|
isOpen
|
|
54
54
|
} = this.getStates();
|
|
55
|
+
const {
|
|
56
|
+
searchPosition
|
|
57
|
+
} = this.getProps();
|
|
58
|
+
// Default to true if not specified (backward compatibility)
|
|
59
|
+
const shouldOpenDropdown = openDropdown !== false;
|
|
55
60
|
this._adapter.updateFocusState(true);
|
|
56
61
|
this._adapter.setIsFocusInContainer(false);
|
|
57
62
|
if (isFilterable) {
|
|
63
|
+
/**
|
|
64
|
+
* When openDropdown is false, we only want to "refocus" the Select
|
|
65
|
+
* without changing dropdown visibility.
|
|
66
|
+
*
|
|
67
|
+
* NOTE: For searchPosition='dropdown', the search input is rendered in dropdown,
|
|
68
|
+
* so we should NOT toggle trigger input(showInput) here, otherwise it may affect
|
|
69
|
+
* tabIndex / focusability unexpectedly.
|
|
70
|
+
*/
|
|
71
|
+
if (!shouldOpenDropdown) {
|
|
72
|
+
if (searchPosition === _constants.strings.SEARCH_POSITION_TRIGGER) {
|
|
73
|
+
if (isMultiple) {
|
|
74
|
+
this.focusInput();
|
|
75
|
+
} else {
|
|
76
|
+
this.toggle2SearchInput(true);
|
|
77
|
+
}
|
|
78
|
+
} else {
|
|
79
|
+
this._focusTrigger();
|
|
80
|
+
}
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
58
83
|
if (isMultiple) {
|
|
59
84
|
// when filter and multiple, focus input and open dropdown
|
|
60
85
|
this.focusInput();
|
|
@@ -1092,7 +1117,8 @@ class SelectFoundation extends _foundation.default {
|
|
|
1092
1117
|
this.clearInput(e);
|
|
1093
1118
|
}
|
|
1094
1119
|
// after click showClear button, the select need to be focused
|
|
1095
|
-
|
|
1120
|
+
// but don't open dropdown to avoid focus state confusion
|
|
1121
|
+
this.focus(undefined, false);
|
|
1096
1122
|
this.clearSelected();
|
|
1097
1123
|
// prevent this click open dropdown
|
|
1098
1124
|
e.stopPropagation();
|
|
@@ -30,7 +30,8 @@ $color-switch_disabled-border-default: var(--semi-color-border); // 禁用态开
|
|
|
30
30
|
$color-switch_disabled-bg-hover: transparent; // 禁用态开关背景色 - 悬浮
|
|
31
31
|
$color-switch_checked_disabled-bg-default: var(--semi-color-success-disabled); // 禁用开启态开关背景颜色
|
|
32
32
|
$color-switch_checked_disabled-border-default: transparent; // 禁用开启态开关描边颜色
|
|
33
|
-
$color-switch_knob-bg-default: rgba(var(--semi-white), 1); // 开关滑块背景颜色
|
|
33
|
+
$color-switch_knob-bg-default: rgba(var(--semi-white), 1); // 开关滑块背景颜色 - 关闭态
|
|
34
|
+
$color-switch_knob-bg-checked: rgba(var(--semi-white), 1); // 开关滑块背景颜色 - 开启态
|
|
34
35
|
$color-switch_knob-border-default: var(--semi-color-border); // 开关滑块描边颜色
|
|
35
36
|
$color-switch_checked-text-default: var(--semi-color-white); // 开启态开关文案颜色
|
|
36
37
|
$color-switch_unchecked-text-default: var(--semi-color-text-2); // 关闭态开关文案颜色
|
|
@@ -888,7 +888,8 @@ class TableFoundation extends _foundation.default {
|
|
|
888
888
|
let e = arguments.length > 1 ? arguments[1] : undefined;
|
|
889
889
|
let check = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
|
|
890
890
|
var _a, _b, _c, _d;
|
|
891
|
-
|
|
891
|
+
/* Do not call stopPropagation here, otherwise the click event registered via onHeaderCell
|
|
892
|
+
will be blocked when the click hot area is the whole title (#1861). */
|
|
892
893
|
/* if mouse down to the resizable handle, do not trigger the sorting,fix #2802
|
|
893
894
|
The target of the click event may be different from the target of the mousedown,
|
|
894
895
|
e.g: Press the mouse, move to another node and then release it,
|
package/lib/cjs/tag/tag.css
CHANGED
|
@@ -505,6 +505,32 @@
|
|
|
505
505
|
color: var(--semi-color-text-0);
|
|
506
506
|
}
|
|
507
507
|
|
|
508
|
+
.semi-tag-split {
|
|
509
|
+
display: inline-flex;
|
|
510
|
+
align-items: center;
|
|
511
|
+
}
|
|
512
|
+
.semi-tag-split .semi-tag {
|
|
513
|
+
border-radius: 0;
|
|
514
|
+
margin-right: 1px;
|
|
515
|
+
}
|
|
516
|
+
.semi-tag-split .semi-tag-first {
|
|
517
|
+
border-top-left-radius: var(--semi-border-radius-small);
|
|
518
|
+
border-bottom-left-radius: var(--semi-border-radius-small);
|
|
519
|
+
}
|
|
520
|
+
.semi-tag-split .semi-tag-last {
|
|
521
|
+
border-top-right-radius: var(--semi-border-radius-small);
|
|
522
|
+
border-bottom-right-radius: var(--semi-border-radius-small);
|
|
523
|
+
margin-right: unset;
|
|
524
|
+
}
|
|
525
|
+
.semi-tag-split .semi-tag-circle.semi-tag-first {
|
|
526
|
+
border-top-left-radius: var(--semi-border-radius-full);
|
|
527
|
+
border-bottom-left-radius: var(--semi-border-radius-full);
|
|
528
|
+
}
|
|
529
|
+
.semi-tag-split .semi-tag-circle.semi-tag-last {
|
|
530
|
+
border-top-right-radius: var(--semi-border-radius-full);
|
|
531
|
+
border-bottom-right-radius: var(--semi-border-radius-full);
|
|
532
|
+
}
|
|
533
|
+
|
|
508
534
|
.semi-rtl .semi-tag,
|
|
509
535
|
.semi-portal-rtl .semi-tag {
|
|
510
536
|
direction: rtl;
|
package/lib/cjs/tag/tag.scss
CHANGED
|
@@ -329,4 +329,37 @@ $types: "ghost", "solid", "light";
|
|
|
329
329
|
color: $color-tag_avatar-text-default;
|
|
330
330
|
}
|
|
331
331
|
|
|
332
|
+
.#{$module}-split {
|
|
333
|
+
display: inline-flex;
|
|
334
|
+
align-items: center;
|
|
335
|
+
|
|
336
|
+
.#{$module} {
|
|
337
|
+
border-radius: 0;
|
|
338
|
+
margin-right: 1px;
|
|
339
|
+
|
|
340
|
+
&-first {
|
|
341
|
+
border-top-left-radius: $radius-tag;
|
|
342
|
+
border-bottom-left-radius: $radius-tag;
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
&-last {
|
|
346
|
+
border-top-right-radius: $radius-tag;
|
|
347
|
+
border-bottom-right-radius: $radius-tag;
|
|
348
|
+
margin-right: unset;
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
&-circle {
|
|
352
|
+
&.#{$module}-first {
|
|
353
|
+
border-top-left-radius: $radius-tag_circle;
|
|
354
|
+
border-bottom-left-radius: $radius-tag_circle;
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
&.#{$module}-last {
|
|
358
|
+
border-top-right-radius: $radius-tag_circle;
|
|
359
|
+
border-bottom-right-radius: $radius-tag_circle;
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
|
|
332
365
|
@import './rtl.scss';
|
|
@@ -112,6 +112,8 @@
|
|
|
112
112
|
padding-left: 4px;
|
|
113
113
|
padding-right: 4px;
|
|
114
114
|
overflow: hidden;
|
|
115
|
+
position: relative;
|
|
116
|
+
/* hidden mirror used to measure input text width */
|
|
115
117
|
}
|
|
116
118
|
.semi-tagInput-wrapper-tag {
|
|
117
119
|
margin-right: 4px;
|
|
@@ -154,6 +156,8 @@
|
|
|
154
156
|
.semi-tagInput-wrapper .semi-tagInput-wrapper-input {
|
|
155
157
|
flex-grow: 1;
|
|
156
158
|
width: min-content;
|
|
159
|
+
min-width: 2px;
|
|
160
|
+
max-width: 100%;
|
|
157
161
|
border: none;
|
|
158
162
|
outline: none;
|
|
159
163
|
background-color: transparent;
|
|
@@ -195,6 +199,19 @@
|
|
|
195
199
|
height: 24px;
|
|
196
200
|
line-height: 24px;
|
|
197
201
|
}
|
|
202
|
+
.semi-tagInput-wrapper-inputMirror {
|
|
203
|
+
position: absolute;
|
|
204
|
+
top: 0;
|
|
205
|
+
left: 0;
|
|
206
|
+
visibility: hidden;
|
|
207
|
+
pointer-events: none;
|
|
208
|
+
height: 0;
|
|
209
|
+
overflow: hidden;
|
|
210
|
+
white-space: pre;
|
|
211
|
+
font-size: 14px;
|
|
212
|
+
font-weight: 400;
|
|
213
|
+
font-family: inherit;
|
|
214
|
+
}
|
|
198
215
|
.semi-tagInput-clearBtn {
|
|
199
216
|
display: flex;
|
|
200
217
|
justify-content: center;
|
|
@@ -137,6 +137,7 @@ $module: #{$prefix}-tagInput;
|
|
|
137
137
|
padding-left: $spacing-extra-tight;
|
|
138
138
|
padding-right: $spacing-extra-tight;
|
|
139
139
|
overflow: hidden;
|
|
140
|
+
position: relative;
|
|
140
141
|
|
|
141
142
|
&-tag {
|
|
142
143
|
margin-right: $spacing-extra-tight;
|
|
@@ -190,6 +191,8 @@ $module: #{$prefix}-tagInput;
|
|
|
190
191
|
& &-input {
|
|
191
192
|
flex-grow: 1;
|
|
192
193
|
width: min-content;
|
|
194
|
+
min-width: 2px;
|
|
195
|
+
max-width: 100%;
|
|
193
196
|
// min-width: 38px;
|
|
194
197
|
border: none;
|
|
195
198
|
outline: none;
|
|
@@ -240,6 +243,21 @@ $module: #{$prefix}-tagInput;
|
|
|
240
243
|
}
|
|
241
244
|
}
|
|
242
245
|
}
|
|
246
|
+
|
|
247
|
+
/* hidden mirror used to measure input text width */
|
|
248
|
+
&-inputMirror {
|
|
249
|
+
position: absolute;
|
|
250
|
+
top: 0;
|
|
251
|
+
left: 0;
|
|
252
|
+
visibility: hidden;
|
|
253
|
+
pointer-events: none;
|
|
254
|
+
height: 0;
|
|
255
|
+
overflow: hidden;
|
|
256
|
+
white-space: pre;
|
|
257
|
+
font-size: $font-size-regular;
|
|
258
|
+
font-weight: $font-weight-regular;
|
|
259
|
+
font-family: inherit;
|
|
260
|
+
}
|
|
243
261
|
}
|
|
244
262
|
|
|
245
263
|
&-clearBtn {
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { PanelType } from './constants';
|
|
1
2
|
import BaseFoundation, { DefaultAdapter } from '../base/foundation';
|
|
2
3
|
export type Position = 'top' | 'topLeft' | 'topRight' | 'left' | 'leftTop' | 'leftBottom' | 'right' | 'rightTop' | 'rightBottom' | 'bottom' | 'bottomLeft' | 'bottomRight' | 'leftTopOver' | 'rightTopOver';
|
|
3
4
|
export interface TimePickerAdapter<P = Record<string, any>, S = Record<string, any>> extends DefaultAdapter<P, S> {
|
|
@@ -16,11 +17,16 @@ declare class TimePickerFoundation<P = Record<string, any>, S = Record<string, a
|
|
|
16
17
|
constructor(adapter: TimePickerAdapter<P, S>);
|
|
17
18
|
init(): void;
|
|
18
19
|
getPosition(): Position;
|
|
20
|
+
getDisabledTimeFns(panelType: PanelType, dates: Date[]): {
|
|
21
|
+
disabledHours: any;
|
|
22
|
+
disabledMinutes: any;
|
|
23
|
+
disabledSeconds: any;
|
|
24
|
+
};
|
|
19
25
|
isDisabledHMS({ hours, minutes, seconds }: {
|
|
20
26
|
hours: number;
|
|
21
27
|
minutes: number;
|
|
22
28
|
seconds: number;
|
|
23
|
-
}): boolean;
|
|
29
|
+
}, panelType?: PanelType, dates?: Date[]): boolean;
|
|
24
30
|
isValidTimeZone(timeZone: string | number): boolean;
|
|
25
31
|
getDefaultFormatIfNeed(): string;
|
|
26
32
|
/**
|
|
@@ -33,17 +33,44 @@ class TimePickerFoundation extends _foundation.default {
|
|
|
33
33
|
const rtlDirection = direction === 'rtl' ? 'bottomRight' : '';
|
|
34
34
|
return position || rtlDirection || _constants.strings.DEFAULT_POSITION[type];
|
|
35
35
|
}
|
|
36
|
+
getDisabledTimeFns(panelType, dates) {
|
|
37
|
+
const {
|
|
38
|
+
disabledHours,
|
|
39
|
+
disabledMinutes,
|
|
40
|
+
disabledSeconds,
|
|
41
|
+
disabledTime
|
|
42
|
+
} = this.getProps();
|
|
43
|
+
// disabledTime is range-only: only invoke it when the picker is
|
|
44
|
+
// actually a range picker. In single mode panelType has no meaning, so
|
|
45
|
+
// we fall back to the top-level disabledHours / disabledMinutes /
|
|
46
|
+
// disabledSeconds without invoking disabledTime.
|
|
47
|
+
if (typeof disabledTime === 'function' && this._adapter.isRangePicker()) {
|
|
48
|
+
const disabledObj = disabledTime(dates, panelType) || {};
|
|
49
|
+
return {
|
|
50
|
+
disabledHours: disabledObj.disabledHours || disabledHours,
|
|
51
|
+
disabledMinutes: disabledObj.disabledMinutes || disabledMinutes,
|
|
52
|
+
disabledSeconds: disabledObj.disabledSeconds || disabledSeconds
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
return {
|
|
56
|
+
disabledHours,
|
|
57
|
+
disabledMinutes,
|
|
58
|
+
disabledSeconds
|
|
59
|
+
};
|
|
60
|
+
}
|
|
36
61
|
isDisabledHMS(_ref) {
|
|
37
62
|
let {
|
|
38
63
|
hours,
|
|
39
64
|
minutes,
|
|
40
65
|
seconds
|
|
41
66
|
} = _ref;
|
|
67
|
+
let panelType = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 'left';
|
|
68
|
+
let dates = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : [];
|
|
42
69
|
const {
|
|
43
70
|
disabledHours,
|
|
44
71
|
disabledMinutes,
|
|
45
72
|
disabledSeconds
|
|
46
|
-
} = this.
|
|
73
|
+
} = this.getDisabledTimeFns(panelType, dates);
|
|
47
74
|
const hDis = !(0, _isNullOrUndefined.default)(hours) && (0, _utils.hourIsDisabled)(disabledHours, hours);
|
|
48
75
|
const mDis = !(0, _isNullOrUndefined.default)(hours) && !(0, _isNullOrUndefined.default)(minutes) && (0, _utils.minuteIsDisabled)(disabledMinutes, hours, minutes);
|
|
49
76
|
const sDis = !(0, _isNullOrUndefined.default)(hours) && !(0, _isNullOrUndefined.default)(minutes) && !(0, _isNullOrUndefined.default)(seconds) && (0, _utils.secondIsDisabled)(disabledSeconds, hours, minutes, seconds);
|
|
@@ -165,11 +192,23 @@ class TimePickerFoundation extends _foundation.default {
|
|
|
165
192
|
if (this.isValidTimeZone(timeZone)) {
|
|
166
193
|
dates = dates.map(date => (0, _dateFnsExtra.utcToZonedTime)(this.isValidTimeZone(__prevTimeZone) ? (0, _dateFnsExtra.zonedTimeToUtc)(date, __prevTimeZone) : date, timeZone));
|
|
167
194
|
}
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
195
|
+
if (this._adapter.isRangePicker()) {
|
|
196
|
+
invalid = dates.some((d, idx) => {
|
|
197
|
+
const panelType = idx === 1 ? 'right' : 'left';
|
|
198
|
+
return this.isDisabledHMS({
|
|
199
|
+
hours: d.getHours(),
|
|
200
|
+
minutes: d.getMinutes(),
|
|
201
|
+
seconds: d.getSeconds()
|
|
202
|
+
}, panelType, dates);
|
|
203
|
+
});
|
|
204
|
+
} else {
|
|
205
|
+
const d = dates[0];
|
|
206
|
+
invalid = d ? this.isDisabledHMS({
|
|
207
|
+
hours: d.getHours(),
|
|
208
|
+
minutes: d.getMinutes(),
|
|
209
|
+
seconds: d.getSeconds()
|
|
210
|
+
}, 'left', dates) : false;
|
|
211
|
+
}
|
|
173
212
|
}
|
|
174
213
|
const inputValue = this.formatValue(dates);
|
|
175
214
|
this.setState({
|
|
@@ -265,11 +304,23 @@ class TimePickerFoundation extends _foundation.default {
|
|
|
265
304
|
let dates = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [];
|
|
266
305
|
let invalid = dates.some(d => isNaN(Number(d)));
|
|
267
306
|
if (!invalid) {
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
307
|
+
if (this._adapter.isRangePicker()) {
|
|
308
|
+
invalid = dates.some((d, idx) => {
|
|
309
|
+
const panelType = idx === 1 ? 'right' : 'left';
|
|
310
|
+
return this.isDisabledHMS({
|
|
311
|
+
hours: d.getHours(),
|
|
312
|
+
minutes: d.getMinutes(),
|
|
313
|
+
seconds: d.getSeconds()
|
|
314
|
+
}, panelType, dates);
|
|
315
|
+
});
|
|
316
|
+
} else {
|
|
317
|
+
const d = dates[0];
|
|
318
|
+
invalid = d ? this.isDisabledHMS({
|
|
319
|
+
hours: d.getHours(),
|
|
320
|
+
minutes: d.getMinutes(),
|
|
321
|
+
seconds: d.getSeconds()
|
|
322
|
+
}, 'left', dates) : false;
|
|
323
|
+
}
|
|
273
324
|
}
|
|
274
325
|
return invalid;
|
|
275
326
|
}
|
|
@@ -114,6 +114,7 @@ export interface BasicCascaderProps {
|
|
|
114
114
|
preventScroll?: boolean;
|
|
115
115
|
virtualizeInSearch?: Virtualize;
|
|
116
116
|
checkRelation?: string;
|
|
117
|
+
remote?: boolean;
|
|
117
118
|
onClear?: () => void;
|
|
118
119
|
triggerRender?: (props: BasicTriggerRenderProps) => any;
|
|
119
120
|
onListScroll?: (e: any, panel: BasicScrollPanelProps) => void;
|
|
@@ -204,6 +205,17 @@ export default class CascaderFoundation extends BaseFoundation<CascaderAdapter,
|
|
|
204
205
|
getItemPropPath(selectedKey: string, prop: string | any[], keyEntities?: BasicEntities): any[];
|
|
205
206
|
_getCacheValue(keyEntities: BasicEntities): any;
|
|
206
207
|
collectOptions(init?: boolean): void;
|
|
208
|
+
/**
|
|
209
|
+
* Calculate filtered keys based on current props.
|
|
210
|
+
* - In remote mode: do not do local match, treat current treeData nodes as results
|
|
211
|
+
* - In local mode: perform matching by filterTreeNode
|
|
212
|
+
*/
|
|
213
|
+
_calcFilteredKeys(sugInput: string, keyEntities?: BasicEntities): string[];
|
|
214
|
+
/**
|
|
215
|
+
* Sync filteredKeys with latest options/keyEntities WITHOUT triggering onSearch.
|
|
216
|
+
* Used when treeData changes asynchronously in searching state.
|
|
217
|
+
*/
|
|
218
|
+
recalculateFilteredKeys(input?: string, nextKeyEntities?: BasicEntities): void;
|
|
207
219
|
handleValueChange(value: BasicValue): void;
|
|
208
220
|
/**
|
|
209
221
|
* When single selection, the clear objects of
|
|
@@ -211,6 +211,72 @@ export default class CascaderFoundation extends BaseFoundation {
|
|
|
211
211
|
keyEntities
|
|
212
212
|
});
|
|
213
213
|
}
|
|
214
|
+
// If options(treeData) updates during searching (e.g. remote search async update),
|
|
215
|
+
// we need to sync filteredKeys with the latest keyEntities; otherwise the UI may
|
|
216
|
+
// render empty list ("暂无数据") because filteredKeys are based on stale entities.
|
|
217
|
+
// NOTE: updateSelectedKey/updateStates are async in React, so we pass keyEntities
|
|
218
|
+
// explicitly to avoid reading stale state.
|
|
219
|
+
this.recalculateFilteredKeys(undefined, keyEntities);
|
|
220
|
+
}
|
|
221
|
+
/**
|
|
222
|
+
* Calculate filtered keys based on current props.
|
|
223
|
+
* - In remote mode: do not do local match, treat current treeData nodes as results
|
|
224
|
+
* - In local mode: perform matching by filterTreeNode
|
|
225
|
+
*/
|
|
226
|
+
_calcFilteredKeys(sugInput, keyEntities) {
|
|
227
|
+
if (!sugInput) {
|
|
228
|
+
return [];
|
|
229
|
+
}
|
|
230
|
+
const {
|
|
231
|
+
treeNodeFilterProp,
|
|
232
|
+
filterTreeNode,
|
|
233
|
+
filterLeafOnly,
|
|
234
|
+
remote
|
|
235
|
+
} = this.getProps();
|
|
236
|
+
const entities = Object.values(keyEntities !== null && keyEntities !== void 0 ? keyEntities : this.getState('keyEntities'));
|
|
237
|
+
if (remote) {
|
|
238
|
+
return entities.filter(item => !item._notExist).filter(item => filterTreeNode && !filterLeafOnly || this._isLeaf(item.data)).map(item => item.key);
|
|
239
|
+
}
|
|
240
|
+
return entities.filter(item => {
|
|
241
|
+
const {
|
|
242
|
+
key,
|
|
243
|
+
_notExist,
|
|
244
|
+
data
|
|
245
|
+
} = item;
|
|
246
|
+
if (_notExist) {
|
|
247
|
+
return false;
|
|
248
|
+
}
|
|
249
|
+
const filteredPath = this.getItemPropPath(key, treeNodeFilterProp, keyEntities);
|
|
250
|
+
return filter(sugInput, data, filterTreeNode, filteredPath);
|
|
251
|
+
}).filter(item => filterTreeNode && !filterLeafOnly || this._isLeaf(item.data)).map(item => item.key);
|
|
252
|
+
}
|
|
253
|
+
/**
|
|
254
|
+
* Sync filteredKeys with latest options/keyEntities WITHOUT triggering onSearch.
|
|
255
|
+
* Used when treeData changes asynchronously in searching state.
|
|
256
|
+
*/
|
|
257
|
+
recalculateFilteredKeys(input, nextKeyEntities) {
|
|
258
|
+
const isFilterable = this._isFilterable();
|
|
259
|
+
if (!isFilterable) {
|
|
260
|
+
return;
|
|
261
|
+
}
|
|
262
|
+
// When input is not explicitly provided, only recalculate in searching state.
|
|
263
|
+
// Otherwise, treeData updates may incorrectly force component into searching mode
|
|
264
|
+
// because inputValue can be the selected label text in normal (non-searching) state.
|
|
265
|
+
const currentIsSearching = this.getState('isSearching');
|
|
266
|
+
if (_isUndefined(input) && !currentIsSearching) {
|
|
267
|
+
return;
|
|
268
|
+
}
|
|
269
|
+
const sugInput = _isUndefined(input) ? this.getState('inputValue') : input;
|
|
270
|
+
const filteredKeys = this._calcFilteredKeys(sugInput, nextKeyEntities);
|
|
271
|
+
const updateStates = {
|
|
272
|
+
isSearching: Boolean(sugInput),
|
|
273
|
+
filteredKeys: new Set(filteredKeys)
|
|
274
|
+
};
|
|
275
|
+
if (nextKeyEntities) {
|
|
276
|
+
updateStates.keyEntities = nextKeyEntities;
|
|
277
|
+
}
|
|
278
|
+
this._adapter.updateStates(updateStates);
|
|
279
|
+
this._adapter.rePositionDropdown();
|
|
214
280
|
}
|
|
215
281
|
// call when props.value change
|
|
216
282
|
handleValueChange(value) {
|
|
@@ -797,29 +863,7 @@ export default class CascaderFoundation extends BaseFoundation {
|
|
|
797
863
|
}
|
|
798
864
|
handleInputChange(sugInput) {
|
|
799
865
|
this._adapter.updateInputValue(sugInput);
|
|
800
|
-
const
|
|
801
|
-
keyEntities
|
|
802
|
-
} = this.getStates();
|
|
803
|
-
const {
|
|
804
|
-
treeNodeFilterProp,
|
|
805
|
-
filterTreeNode,
|
|
806
|
-
filterLeafOnly
|
|
807
|
-
} = this.getProps();
|
|
808
|
-
let filteredKeys = [];
|
|
809
|
-
if (sugInput) {
|
|
810
|
-
filteredKeys = Object.values(keyEntities).filter(item => {
|
|
811
|
-
const {
|
|
812
|
-
key,
|
|
813
|
-
_notExist,
|
|
814
|
-
data
|
|
815
|
-
} = item;
|
|
816
|
-
if (_notExist) {
|
|
817
|
-
return false;
|
|
818
|
-
}
|
|
819
|
-
const filteredPath = this.getItemPropPath(key, treeNodeFilterProp);
|
|
820
|
-
return filter(sugInput, data, filterTreeNode, filteredPath);
|
|
821
|
-
}).filter(item => filterTreeNode && !filterLeafOnly || this._isLeaf(item)).map(item => item.key);
|
|
822
|
-
}
|
|
866
|
+
const filteredKeys = this._calcFilteredKeys(sugInput);
|
|
823
867
|
this._adapter.updateStates({
|
|
824
868
|
isSearching: Boolean(sugInput),
|
|
825
869
|
filteredKeys: new Set(filteredKeys)
|
|
@@ -890,6 +934,7 @@ export default class CascaderFoundation extends BaseFoundation {
|
|
|
890
934
|
} = this.getStates();
|
|
891
935
|
const isFilterable = this._isFilterable();
|
|
892
936
|
if (isSearching && isFilterable) {
|
|
937
|
+
// Both local & remote search mode should render flattened search list
|
|
893
938
|
return this.getFilteredData();
|
|
894
939
|
}
|
|
895
940
|
return Object.values(keyEntities).filter(item => item.parentKey === null && !item._notExist)
|