@douyinfe/semi-foundation 2.93.0 → 2.94.1
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/aiChatInput/foundation.ts +3 -1
- package/autoComplete/foundation.ts +3 -2
- package/cascader/foundation.ts +8 -3
- package/collapsible/foundation.ts +1 -0
- package/datePicker/datePicker.scss +2 -2
- package/descriptions/foundation.ts +3 -1
- package/form/foundation.ts +61 -29
- package/form/interface.ts +9 -2
- package/input/textareaFoundation.ts +34 -1
- package/lib/cjs/aiChatInput/foundation.js +3 -1
- package/lib/cjs/autoComplete/foundation.d.ts +1 -0
- package/lib/cjs/autoComplete/foundation.js +1 -1
- package/lib/cjs/cascader/foundation.d.ts +1 -0
- package/lib/cjs/cascader/foundation.js +8 -3
- package/lib/cjs/collapsible/foundation.d.ts +1 -0
- package/lib/cjs/datePicker/datePicker.css +2 -2
- package/lib/cjs/datePicker/datePicker.scss +2 -2
- package/lib/cjs/descriptions/foundation.js +3 -1
- package/lib/cjs/form/foundation.d.ts +5 -5
- package/lib/cjs/form/foundation.js +58 -21
- package/lib/cjs/form/interface.d.ts +8 -2
- package/lib/cjs/input/textareaFoundation.d.ts +12 -0
- package/lib/cjs/input/textareaFoundation.js +39 -0
- package/lib/cjs/modal/modalFoundation.d.ts +6 -0
- package/lib/cjs/modal/modalFoundation.js +36 -4
- package/lib/cjs/pagination/foundation.js +28 -7
- package/lib/cjs/pincode/foundation.d.ts +1 -1
- package/lib/cjs/resizable/resizable.css +3 -3
- package/lib/cjs/resizable/variables.scss +2 -2
- package/lib/cjs/select/foundation.d.ts +1 -1
- package/lib/cjs/select/foundation.js +19 -8
- package/lib/cjs/steps/bacisSteps.scss +8 -2
- package/lib/cjs/steps/steps.css +6 -0
- package/lib/cjs/upload/foundation.d.ts +8 -1
- package/lib/cjs/upload/foundation.js +70 -22
- package/lib/cjs/utils/escapeHtml.d.ts +9 -0
- package/lib/cjs/utils/escapeHtml.js +90 -0
- package/lib/cjs/utils/object.js +3 -1
- package/lib/es/aiChatInput/foundation.js +3 -1
- package/lib/es/autoComplete/foundation.d.ts +1 -0
- package/lib/es/autoComplete/foundation.js +1 -1
- package/lib/es/cascader/foundation.d.ts +1 -0
- package/lib/es/cascader/foundation.js +8 -3
- package/lib/es/collapsible/foundation.d.ts +1 -0
- package/lib/es/datePicker/datePicker.css +2 -2
- package/lib/es/datePicker/datePicker.scss +2 -2
- package/lib/es/descriptions/foundation.js +3 -1
- package/lib/es/form/foundation.d.ts +5 -5
- package/lib/es/form/foundation.js +58 -21
- package/lib/es/form/interface.d.ts +8 -2
- package/lib/es/input/textareaFoundation.d.ts +12 -0
- package/lib/es/input/textareaFoundation.js +39 -0
- package/lib/es/modal/modalFoundation.d.ts +6 -0
- package/lib/es/modal/modalFoundation.js +36 -4
- package/lib/es/pagination/foundation.js +28 -7
- package/lib/es/pincode/foundation.d.ts +1 -1
- package/lib/es/resizable/resizable.css +3 -3
- package/lib/es/resizable/variables.scss +2 -2
- package/lib/es/select/foundation.d.ts +1 -1
- package/lib/es/select/foundation.js +19 -8
- package/lib/es/steps/bacisSteps.scss +8 -2
- package/lib/es/steps/steps.css +6 -0
- package/lib/es/upload/foundation.d.ts +8 -1
- package/lib/es/upload/foundation.js +70 -22
- package/lib/es/utils/escapeHtml.d.ts +9 -0
- package/lib/es/utils/escapeHtml.js +84 -0
- package/lib/es/utils/object.js +3 -1
- package/modal/modalFoundation.ts +33 -32
- package/package.json +35 -5
- package/pagination/foundation.ts +25 -7
- package/pincode/foundation.ts +1 -1
- package/resizable/variables.scss +2 -2
- package/select/foundation.ts +19 -8
- package/steps/bacisSteps.scss +8 -2
- package/upload/foundation.ts +81 -24
- package/utils/escapeHtml.ts +94 -0
- package/utils/object.ts +3 -1
|
@@ -375,7 +375,9 @@ export default class AIChatInputFoundation extends BaseFoundation<AIChatInputAda
|
|
|
375
375
|
}
|
|
376
376
|
|
|
377
377
|
handRichTextArealKeyDown = (view: any, event: KeyboardEvent) => {
|
|
378
|
-
|
|
378
|
+
if (view.composing) {
|
|
379
|
+
return false;
|
|
380
|
+
}
|
|
379
381
|
const { sendHotKey } = this.getProps();
|
|
380
382
|
const { suggestionVisible, skillVisible } = this.getStates();
|
|
381
383
|
/**
|
|
@@ -18,7 +18,8 @@ export interface DataItem {
|
|
|
18
18
|
|
|
19
19
|
export interface StateOptionItem extends DataItem {
|
|
20
20
|
show?: boolean;
|
|
21
|
-
key?: string | number
|
|
21
|
+
key?: string | number;
|
|
22
|
+
_renderedLabel?: any; // Internal property to store the rendered label from renderItem
|
|
22
23
|
}
|
|
23
24
|
|
|
24
25
|
export type AutoCompleteData = Array<DataItem | string>;
|
|
@@ -144,7 +145,7 @@ class AutoCompleteFoundation<P = Record<string, any>, S = Record<string, any>> e
|
|
|
144
145
|
option = { show: true, ...item };
|
|
145
146
|
}
|
|
146
147
|
if (renderItem && typeof renderItem === 'function') {
|
|
147
|
-
option.
|
|
148
|
+
option._renderedLabel = renderItem(item);
|
|
148
149
|
}
|
|
149
150
|
options.push(option);
|
|
150
151
|
});
|
package/cascader/foundation.ts
CHANGED
|
@@ -166,6 +166,7 @@ export interface BasicCascaderProps {
|
|
|
166
166
|
disableStrictly?: boolean;
|
|
167
167
|
leafOnly?: boolean;
|
|
168
168
|
enableLeafClick?: boolean;
|
|
169
|
+
clickToSelect?: boolean;
|
|
169
170
|
preventScroll?: boolean;
|
|
170
171
|
virtualizeInSearch?: Virtualize;
|
|
171
172
|
checkRelation?: string;
|
|
@@ -734,7 +735,7 @@ export default class CascaderFoundation extends BaseFoundation<CascaderAdapter,
|
|
|
734
735
|
}
|
|
735
736
|
|
|
736
737
|
handleSingleSelect(e: any, item: BasicEntity | BasicData) {
|
|
737
|
-
const { changeOnSelect: allowChange, filterLeafOnly, multiple, enableLeafClick } = this.getProps();
|
|
738
|
+
const { changeOnSelect: allowChange, filterLeafOnly, multiple, enableLeafClick, clickToSelect } = this.getProps();
|
|
738
739
|
const { keyEntities, selectedKeys, isSearching } = this.getStates();
|
|
739
740
|
const filterable = this._isFilterable();
|
|
740
741
|
const { data, key } = item;
|
|
@@ -745,14 +746,18 @@ export default class CascaderFoundation extends BaseFoundation<CascaderAdapter,
|
|
|
745
746
|
const selectedKey = [key];
|
|
746
747
|
const hasChanged = key !== [...selectedKeys][0];
|
|
747
748
|
|
|
748
|
-
|
|
749
|
+
// When clickToSelect is enabled in multiple mode, allow clicking non-leaf nodes to select
|
|
750
|
+
// In single mode, changeOnSelect (allowChange) controls this behavior
|
|
751
|
+
if (!isLeaf && !allowChange && !isSearching && !(multiple && clickToSelect)) {
|
|
749
752
|
this._adapter.updateStates({ activeKeys: new Set(activeKeys) });
|
|
750
753
|
this.notifyIfLoadData(item);
|
|
751
754
|
return;
|
|
752
755
|
}
|
|
753
756
|
if (multiple) {
|
|
754
757
|
this._adapter.updateStates({ activeKeys: new Set(activeKeys) });
|
|
755
|
-
|
|
758
|
+
// clickToSelect: click any node to select (takes precedence over enableLeafClick)
|
|
759
|
+
// enableLeafClick: click leaf node to select
|
|
760
|
+
if (clickToSelect || (isLeaf && enableLeafClick)) {
|
|
756
761
|
this.onItemCheckboxClick(item);
|
|
757
762
|
}
|
|
758
763
|
} else {
|
|
@@ -937,11 +937,11 @@ $module-list: #{$prefix}-scrolllist;
|
|
|
937
937
|
height: fit-content;
|
|
938
938
|
border: none;
|
|
939
939
|
|
|
940
|
-
&:active
|
|
940
|
+
&:active:not(#neverExistElement) {
|
|
941
941
|
background-color: transparent;
|
|
942
942
|
}
|
|
943
943
|
|
|
944
|
-
&:hover {
|
|
944
|
+
&:hover:not(#neverExistElement) {
|
|
945
945
|
background-color: transparent;
|
|
946
946
|
}
|
|
947
947
|
}
|
|
@@ -12,9 +12,11 @@ export default class DescriptionsFoundation<P = Record<string, any>, S = Record<
|
|
|
12
12
|
getHorizontalList() {
|
|
13
13
|
const { column, data, children } = this.getProps();
|
|
14
14
|
const columns = this._adapter.getColumns();
|
|
15
|
+
// Filter out hidden items before grouping
|
|
16
|
+
const visibleColumns = columns.filter(item => !item.hidden);
|
|
15
17
|
const horizontalList = [];
|
|
16
18
|
const curSpan = { totalSpan: 0, itemList: [] };
|
|
17
|
-
for (const item of
|
|
19
|
+
for (const item of visibleColumns) {
|
|
18
20
|
curSpan.totalSpan += item.span || 1;
|
|
19
21
|
curSpan.itemList.push(item);
|
|
20
22
|
if (curSpan.totalSpan >= column) {
|
package/form/foundation.ts
CHANGED
|
@@ -5,7 +5,7 @@ import { isValid } from './utils';
|
|
|
5
5
|
import { isUndefined, isFunction, toPath } from 'lodash';
|
|
6
6
|
import scrollIntoView, { Options as ScrollIntoViewOptions } from 'scroll-into-view-if-needed';
|
|
7
7
|
|
|
8
|
-
import { BaseFormAdapter, FormState, CallOpts, FieldState, FieldStaff, ComponentProps, setValuesConfig, ArrayFieldStaff } from './interface';
|
|
8
|
+
import { BaseFormAdapter, FormState, CallOpts, FieldState, FieldStaff, ComponentProps, setValuesConfig, ArrayFieldStaff, ValidateOptions } from './interface';
|
|
9
9
|
|
|
10
10
|
export type { BaseFormAdapter };
|
|
11
11
|
|
|
@@ -157,17 +157,32 @@ export default class FormFoundation extends BaseFoundation<BaseFormAdapter> {
|
|
|
157
157
|
this.registeredArrayField.set(arrayField, mergeVal);
|
|
158
158
|
}
|
|
159
159
|
|
|
160
|
-
validate(fieldPaths?: Array<string>): Promise<unknown> {
|
|
160
|
+
validate(fieldPaths?: Array<string> | ValidateOptions): Promise<unknown> {
|
|
161
161
|
const { validateFields } = this.getProps();
|
|
162
|
+
|
|
163
|
+
// Parse options
|
|
164
|
+
let fields: Array<string> | undefined;
|
|
165
|
+
let silent = false;
|
|
166
|
+
|
|
167
|
+
if (fieldPaths && !Array.isArray(fieldPaths)) {
|
|
168
|
+
// It's a ValidateOptions object
|
|
169
|
+
const options = fieldPaths as ValidateOptions;
|
|
170
|
+
fields = options.fields as Array<string> | undefined;
|
|
171
|
+
silent = options.silent || false;
|
|
172
|
+
} else {
|
|
173
|
+
// It's an array of fields (or undefined)
|
|
174
|
+
fields = fieldPaths as Array<string> | undefined;
|
|
175
|
+
}
|
|
176
|
+
|
|
162
177
|
if (validateFields && isFunction(validateFields)) {
|
|
163
|
-
return this._formValidate();
|
|
178
|
+
return this._formValidate(silent);
|
|
164
179
|
} else {
|
|
165
|
-
return this._fieldsValidate(
|
|
180
|
+
return this._fieldsValidate(fields, silent);
|
|
166
181
|
}
|
|
167
182
|
}
|
|
168
183
|
|
|
169
184
|
// form level validate
|
|
170
|
-
_formValidate(): Promise<unknown> {
|
|
185
|
+
_formValidate(silent: boolean = false): Promise<unknown> {
|
|
171
186
|
const { values } = this.data;
|
|
172
187
|
const { validateFields } = this.getProps();
|
|
173
188
|
|
|
@@ -182,7 +197,9 @@ export default class FormFoundation extends BaseFoundation<BaseFormAdapter> {
|
|
|
182
197
|
if (!maybePromisedErrors) {
|
|
183
198
|
const _values = this._adapter.cloneDeep(values);
|
|
184
199
|
resolve(_values);
|
|
185
|
-
|
|
200
|
+
if (!silent) {
|
|
201
|
+
this.injectErrorToField({});
|
|
202
|
+
}
|
|
186
203
|
} else if (isPromise(maybePromisedErrors)) {
|
|
187
204
|
maybePromisedErrors.then(
|
|
188
205
|
(result: any) => {
|
|
@@ -190,39 +207,45 @@ export default class FormFoundation extends BaseFoundation<BaseFormAdapter> {
|
|
|
190
207
|
if (!result) {
|
|
191
208
|
const _values = this._adapter.cloneDeep(values);
|
|
192
209
|
resolve(_values);
|
|
193
|
-
|
|
210
|
+
if (!silent) {
|
|
211
|
+
this.injectErrorToField({});
|
|
212
|
+
}
|
|
194
213
|
} else {
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
214
|
+
if (!silent) {
|
|
215
|
+
this.data.errors = result;
|
|
216
|
+
this._adapter.notifyChange(this.data);
|
|
217
|
+
this.injectErrorToField(result);
|
|
218
|
+
this._adapter.forceUpdate();
|
|
219
|
+
this._autoScroll(100);
|
|
220
|
+
}
|
|
201
221
|
reject(result);
|
|
202
222
|
}
|
|
203
223
|
},
|
|
204
224
|
(errors: any) => {
|
|
205
225
|
// validate failed
|
|
206
|
-
|
|
207
|
-
|
|
226
|
+
if (!silent) {
|
|
227
|
+
// this._adapter.notifyChange(this.data);
|
|
228
|
+
this._autoScroll(100);
|
|
229
|
+
}
|
|
208
230
|
reject(errors);
|
|
209
231
|
}
|
|
210
232
|
);
|
|
211
233
|
} else {
|
|
212
234
|
// TODO: current design, returning an empty object will be considered a checksum failure and will be rejected. Only returning an empty string will be considered a success, consider resetting it in 1.0?
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
235
|
+
if (!silent) {
|
|
236
|
+
this.data.errors = maybePromisedErrors;
|
|
237
|
+
this.injectErrorToField(maybePromisedErrors);
|
|
238
|
+
this._adapter.notifyChange(this.data);
|
|
239
|
+
this._adapter.forceUpdate();
|
|
240
|
+
this._autoScroll(100);
|
|
241
|
+
}
|
|
219
242
|
reject(maybePromisedErrors);
|
|
220
243
|
}
|
|
221
244
|
});
|
|
222
245
|
}
|
|
223
246
|
|
|
224
247
|
// field level validate
|
|
225
|
-
_fieldsValidate(fieldPaths: Array<string
|
|
248
|
+
_fieldsValidate(fieldPaths: Array<string>, silent: boolean = false): Promise<unknown> {
|
|
226
249
|
const { values } = this.data;
|
|
227
250
|
// When there is no custom validation function at Form level, perform validation of each Field
|
|
228
251
|
return new Promise((resolve, reject) => {
|
|
@@ -232,25 +255,34 @@ export default class FormFoundation extends BaseFoundation<BaseFormAdapter> {
|
|
|
232
255
|
// Call each fieldApi for verification
|
|
233
256
|
const fieldValue = this.getValue(fieldPath);
|
|
234
257
|
// When centralized verification, no need to trigger forceUpdate and notify
|
|
235
|
-
|
|
258
|
+
// Each field skips individual updates; a single forceUpdate fires after all resolve.
|
|
259
|
+
const opts: CallOpts = {
|
|
236
260
|
notNotify: true,
|
|
237
261
|
notUpdate: true,
|
|
262
|
+
silent,
|
|
238
263
|
};
|
|
239
264
|
const validateResult = field.fieldApi.validate(fieldValue, opts);
|
|
240
265
|
promiseSet.push(validateResult);
|
|
241
|
-
|
|
266
|
+
// Only set touched when not in silent mode
|
|
267
|
+
if (!silent) {
|
|
268
|
+
field.fieldApi.setTouched(true, { notNotify: true, notUpdate: true });
|
|
269
|
+
}
|
|
242
270
|
});
|
|
243
271
|
Promise.all(promiseSet).then(() => {
|
|
244
272
|
// After the centralized verification is completed, trigger notify and forceUpdate once.
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
273
|
+
// But skip UI updates in silent mode
|
|
274
|
+
if (!silent) {
|
|
275
|
+
this._adapter.notifyChange(this.data);
|
|
276
|
+
this._adapter.forceUpdate();
|
|
277
|
+
}
|
|
248
278
|
const errors = this.getError();
|
|
249
279
|
if (this._isValid(targetFields)) {
|
|
250
280
|
const _values = this._adapter.cloneDeep(values);
|
|
251
281
|
resolve(_values);
|
|
252
282
|
} else {
|
|
253
|
-
|
|
283
|
+
if (!silent) {
|
|
284
|
+
this._autoScroll();
|
|
285
|
+
}
|
|
254
286
|
reject(errors);
|
|
255
287
|
}
|
|
256
288
|
});
|
|
@@ -620,7 +652,7 @@ export default class FormFoundation extends BaseFoundation<BaseFormAdapter> {
|
|
|
620
652
|
return {
|
|
621
653
|
...fieldSetterApi,
|
|
622
654
|
reset: (fields?: Array<string>) => this.reset(fields),
|
|
623
|
-
validate: (fields?: Array<string>) => this.validate(fields),
|
|
655
|
+
validate: (fields?: Array<string> | ValidateOptions) => this.validate(fields),
|
|
624
656
|
getValue: (field?: string) => this.getValue(field, { needClone: true }),
|
|
625
657
|
getValues: () => this.getValue(undefined, { needClone: true }),
|
|
626
658
|
getFormState: () => this.getFormState(true),
|
package/form/interface.ts
CHANGED
|
@@ -70,6 +70,13 @@ export type ScrollToErrorOptions<K> = {
|
|
|
70
70
|
scrollOpts?: ScrollIntoViewOptions
|
|
71
71
|
}
|
|
72
72
|
|
|
73
|
+
export interface ValidateOptions<K extends keyof any = keyof any> {
|
|
74
|
+
/** Fields to validate, if not specified, validate all fields */
|
|
75
|
+
fields?: Array<K>;
|
|
76
|
+
/** Whether to validate silently (without updating UI or setting touched state) */
|
|
77
|
+
silent?: boolean;
|
|
78
|
+
}
|
|
79
|
+
|
|
73
80
|
// use object replace Record<string, any>, fix issue 933
|
|
74
81
|
export interface BaseFormApi<T extends object = any> {
|
|
75
82
|
/** get value of field */
|
|
@@ -94,8 +101,8 @@ export interface BaseFormApi<T extends object = any> {
|
|
|
94
101
|
submitForm: () => void;
|
|
95
102
|
/** reset form manual */
|
|
96
103
|
reset: (fields?: Array<string>) => void;
|
|
97
|
-
/** trigger validate
|
|
98
|
-
validate: <K extends keyof T, Params extends Array<K>, V extends Params[number]>(fields?: Params) => Promise<{ [R in V]: T[R] }>;
|
|
104
|
+
/** trigger validate manual */
|
|
105
|
+
validate: <K extends keyof T, Params extends Array<K>, V extends Params[number]>(fields?: Params | ValidateOptions<K>) => Promise<{ [R in V]: T[R] }>;
|
|
99
106
|
getInitValue: <K extends keyof T>(field: K) => any;
|
|
100
107
|
getInitValues: () => any;
|
|
101
108
|
getValues: () => T;
|
|
@@ -28,7 +28,9 @@ export interface TextAreaAdapter extends Partial<DefaultAdapter>, Partial<TextAr
|
|
|
28
28
|
setMinLength(length: number): void;
|
|
29
29
|
notifyPressEnter(e: any): void;
|
|
30
30
|
getRef(): HTMLInputElement;
|
|
31
|
-
notifyHeightUpdate(e: any): void
|
|
31
|
+
notifyHeightUpdate(e: any): void;
|
|
32
|
+
focusInput(): void;
|
|
33
|
+
isEventTarget(e: any): boolean
|
|
32
34
|
}
|
|
33
35
|
|
|
34
36
|
export default class TextAreaFoundation extends BaseFoundation<TextAreaAdapter> {
|
|
@@ -281,4 +283,35 @@ export default class TextAreaFoundation extends BaseFoundation<TextAreaAdapter>
|
|
|
281
283
|
this._adapter.notifyClear(e);
|
|
282
284
|
this.stopPropagation(e);
|
|
283
285
|
}
|
|
286
|
+
|
|
287
|
+
/**
|
|
288
|
+
* trigger when click textarea wrapper
|
|
289
|
+
* @param {Event} e
|
|
290
|
+
*/
|
|
291
|
+
handleClick(e: any) {
|
|
292
|
+
const { disabled, readonly } = this._adapter.getProps();
|
|
293
|
+
const { isFocus } = this._adapter.getStates();
|
|
294
|
+
if (disabled || readonly || isFocus) {
|
|
295
|
+
return;
|
|
296
|
+
}
|
|
297
|
+
// do not handle bubbling up events
|
|
298
|
+
if (this._adapter.isEventTarget(e)) {
|
|
299
|
+
this._adapter.focusInput();
|
|
300
|
+
this._adapter.toggleFocusing(true);
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
/**
|
|
305
|
+
* trigger when click textarea counter
|
|
306
|
+
* @param {Event} e
|
|
307
|
+
*/
|
|
308
|
+
handleCounterClick(e: any) {
|
|
309
|
+
const { disabled, readonly } = this._adapter.getProps();
|
|
310
|
+
const { isFocus } = this._adapter.getStates();
|
|
311
|
+
if (disabled || readonly || isFocus) {
|
|
312
|
+
return;
|
|
313
|
+
}
|
|
314
|
+
this._adapter.focusInput();
|
|
315
|
+
this._adapter.toggleFocusing(true);
|
|
316
|
+
}
|
|
284
317
|
}
|
|
@@ -360,7 +360,9 @@ class AIChatInputFoundation extends _foundation.default {
|
|
|
360
360
|
};
|
|
361
361
|
this.handRichTextArealKeyDown = (view, event) => {
|
|
362
362
|
var _a;
|
|
363
|
-
|
|
363
|
+
if (view.composing) {
|
|
364
|
+
return false;
|
|
365
|
+
}
|
|
364
366
|
const {
|
|
365
367
|
sendHotKey
|
|
366
368
|
} = this.getProps();
|
|
@@ -13,6 +13,7 @@ export interface DataItem {
|
|
|
13
13
|
export interface StateOptionItem extends DataItem {
|
|
14
14
|
show?: boolean;
|
|
15
15
|
key?: string | number;
|
|
16
|
+
_renderedLabel?: any;
|
|
16
17
|
}
|
|
17
18
|
export type AutoCompleteData = Array<DataItem | string>;
|
|
18
19
|
export interface AutoCompleteAdapter<P = Record<string, any>, S = Record<string, any>> extends KeyboardAdapter<P, S> {
|
|
@@ -119,7 +119,7 @@ class AutoCompleteFoundation extends _foundation.default {
|
|
|
119
119
|
}, item);
|
|
120
120
|
}
|
|
121
121
|
if (renderItem && typeof renderItem === 'function') {
|
|
122
|
-
option.
|
|
122
|
+
option._renderedLabel = renderItem(item);
|
|
123
123
|
}
|
|
124
124
|
options.push(option);
|
|
125
125
|
});
|
|
@@ -555,7 +555,8 @@ class CascaderFoundation extends _foundation.default {
|
|
|
555
555
|
changeOnSelect: allowChange,
|
|
556
556
|
filterLeafOnly,
|
|
557
557
|
multiple,
|
|
558
|
-
enableLeafClick
|
|
558
|
+
enableLeafClick,
|
|
559
|
+
clickToSelect
|
|
559
560
|
} = this.getProps();
|
|
560
561
|
const {
|
|
561
562
|
keyEntities,
|
|
@@ -571,7 +572,9 @@ class CascaderFoundation extends _foundation.default {
|
|
|
571
572
|
const activeKeys = keyEntities[key].path;
|
|
572
573
|
const selectedKey = [key];
|
|
573
574
|
const hasChanged = key !== [...selectedKeys][0];
|
|
574
|
-
|
|
575
|
+
// When clickToSelect is enabled in multiple mode, allow clicking non-leaf nodes to select
|
|
576
|
+
// In single mode, changeOnSelect (allowChange) controls this behavior
|
|
577
|
+
if (!isLeaf && !allowChange && !isSearching && !(multiple && clickToSelect)) {
|
|
575
578
|
this._adapter.updateStates({
|
|
576
579
|
activeKeys: new Set(activeKeys)
|
|
577
580
|
});
|
|
@@ -582,7 +585,9 @@ class CascaderFoundation extends _foundation.default {
|
|
|
582
585
|
this._adapter.updateStates({
|
|
583
586
|
activeKeys: new Set(activeKeys)
|
|
584
587
|
});
|
|
585
|
-
|
|
588
|
+
// clickToSelect: click any node to select (takes precedence over enableLeafClick)
|
|
589
|
+
// enableLeafClick: click leaf node to select
|
|
590
|
+
if (clickToSelect || isLeaf && enableLeafClick) {
|
|
586
591
|
this.onItemCheckboxClick(item);
|
|
587
592
|
}
|
|
588
593
|
} else {
|
|
@@ -624,10 +624,10 @@
|
|
|
624
624
|
height: fit-content;
|
|
625
625
|
border: none;
|
|
626
626
|
}
|
|
627
|
-
.semi-datepicker-range-input-wrapper .semi-input-wrapper:active {
|
|
627
|
+
.semi-datepicker-range-input-wrapper .semi-input-wrapper:active:not(#neverExistElement) {
|
|
628
628
|
background-color: transparent;
|
|
629
629
|
}
|
|
630
|
-
.semi-datepicker-range-input-wrapper .semi-input-wrapper:hover {
|
|
630
|
+
.semi-datepicker-range-input-wrapper .semi-input-wrapper:hover:not(#neverExistElement) {
|
|
631
631
|
background-color: transparent;
|
|
632
632
|
}
|
|
633
633
|
.semi-datepicker-range-input-wrapper-focus {
|
|
@@ -937,11 +937,11 @@ $module-list: #{$prefix}-scrolllist;
|
|
|
937
937
|
height: fit-content;
|
|
938
938
|
border: none;
|
|
939
939
|
|
|
940
|
-
&:active
|
|
940
|
+
&:active:not(#neverExistElement) {
|
|
941
941
|
background-color: transparent;
|
|
942
942
|
}
|
|
943
943
|
|
|
944
|
-
&:hover {
|
|
944
|
+
&:hover:not(#neverExistElement) {
|
|
945
945
|
background-color: transparent;
|
|
946
946
|
}
|
|
947
947
|
}
|
|
@@ -17,12 +17,14 @@ class DescriptionsFoundation extends _foundation.default {
|
|
|
17
17
|
children
|
|
18
18
|
} = this.getProps();
|
|
19
19
|
const columns = this._adapter.getColumns();
|
|
20
|
+
// Filter out hidden items before grouping
|
|
21
|
+
const visibleColumns = columns.filter(item => !item.hidden);
|
|
20
22
|
const horizontalList = [];
|
|
21
23
|
const curSpan = {
|
|
22
24
|
totalSpan: 0,
|
|
23
25
|
itemList: []
|
|
24
26
|
};
|
|
25
|
-
for (const item of
|
|
27
|
+
for (const item of visibleColumns) {
|
|
26
28
|
curSpan.totalSpan += item.span || 1;
|
|
27
29
|
curSpan.itemList.push(item);
|
|
28
30
|
if (curSpan.totalSpan >= column) {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import BaseFoundation from '../base/foundation';
|
|
2
2
|
import { Options as ScrollIntoViewOptions } from 'scroll-into-view-if-needed';
|
|
3
|
-
import { BaseFormAdapter, FormState, CallOpts, FieldState, FieldStaff, ComponentProps, setValuesConfig, ArrayFieldStaff } from './interface';
|
|
3
|
+
import { BaseFormAdapter, FormState, CallOpts, FieldState, FieldStaff, ComponentProps, setValuesConfig, ArrayFieldStaff, ValidateOptions } from './interface';
|
|
4
4
|
export type { BaseFormAdapter };
|
|
5
5
|
type ScrollToErrorOpts = {
|
|
6
6
|
field?: string;
|
|
@@ -21,9 +21,9 @@ export default class FormFoundation extends BaseFoundation<BaseFormAdapter> {
|
|
|
21
21
|
unRegisterArrayField(arrayField: string): void;
|
|
22
22
|
getArrayField(arrayField: string): ArrayFieldStaff;
|
|
23
23
|
updateArrayField(arrayField: string, updateValue: any): void;
|
|
24
|
-
validate(fieldPaths?: Array<string>): Promise<unknown>;
|
|
25
|
-
_formValidate(): Promise<unknown>;
|
|
26
|
-
_fieldsValidate(fieldPaths: Array<string
|
|
24
|
+
validate(fieldPaths?: Array<string> | ValidateOptions): Promise<unknown>;
|
|
25
|
+
_formValidate(silent?: boolean): Promise<unknown>;
|
|
26
|
+
_fieldsValidate(fieldPaths: Array<string>, silent?: boolean): Promise<unknown>;
|
|
27
27
|
submit(e?: any): void;
|
|
28
28
|
/**
|
|
29
29
|
* Case A:
|
|
@@ -77,7 +77,7 @@ export default class FormFoundation extends BaseFoundation<BaseFormAdapter> {
|
|
|
77
77
|
};
|
|
78
78
|
getFormApi(): {
|
|
79
79
|
reset: (fields?: Array<string>) => void;
|
|
80
|
-
validate: (fields?: Array<string>) => Promise<unknown>;
|
|
80
|
+
validate: (fields?: Array<string> | ValidateOptions) => Promise<unknown>;
|
|
81
81
|
getValue: (field?: string) => any;
|
|
82
82
|
getValues: () => any;
|
|
83
83
|
getFormState: () => FormState<any>;
|
|
@@ -138,14 +138,27 @@ class FormFoundation extends _foundation.default {
|
|
|
138
138
|
const {
|
|
139
139
|
validateFields
|
|
140
140
|
} = this.getProps();
|
|
141
|
+
// Parse options
|
|
142
|
+
let fields;
|
|
143
|
+
let silent = false;
|
|
144
|
+
if (fieldPaths && !Array.isArray(fieldPaths)) {
|
|
145
|
+
// It's a ValidateOptions object
|
|
146
|
+
const options = fieldPaths;
|
|
147
|
+
fields = options.fields;
|
|
148
|
+
silent = options.silent || false;
|
|
149
|
+
} else {
|
|
150
|
+
// It's an array of fields (or undefined)
|
|
151
|
+
fields = fieldPaths;
|
|
152
|
+
}
|
|
141
153
|
if (validateFields && (0, _isFunction2.default)(validateFields)) {
|
|
142
|
-
return this._formValidate();
|
|
154
|
+
return this._formValidate(silent);
|
|
143
155
|
} else {
|
|
144
|
-
return this._fieldsValidate(
|
|
156
|
+
return this._fieldsValidate(fields, silent);
|
|
145
157
|
}
|
|
146
158
|
}
|
|
147
159
|
// form level validate
|
|
148
160
|
_formValidate() {
|
|
161
|
+
let silent = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;
|
|
149
162
|
const {
|
|
150
163
|
values
|
|
151
164
|
} = this.data;
|
|
@@ -163,41 +176,52 @@ class FormFoundation extends _foundation.default {
|
|
|
163
176
|
if (!maybePromisedErrors) {
|
|
164
177
|
const _values = this._adapter.cloneDeep(values);
|
|
165
178
|
resolve(_values);
|
|
166
|
-
|
|
179
|
+
if (!silent) {
|
|
180
|
+
this.injectErrorToField({});
|
|
181
|
+
}
|
|
167
182
|
} else if ((0, _isPromise.default)(maybePromisedErrors)) {
|
|
168
183
|
maybePromisedErrors.then(result => {
|
|
169
184
|
// validate success,clear error
|
|
170
185
|
if (!result) {
|
|
171
186
|
const _values = this._adapter.cloneDeep(values);
|
|
172
187
|
resolve(_values);
|
|
173
|
-
|
|
188
|
+
if (!silent) {
|
|
189
|
+
this.injectErrorToField({});
|
|
190
|
+
}
|
|
174
191
|
} else {
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
192
|
+
if (!silent) {
|
|
193
|
+
this.data.errors = result;
|
|
194
|
+
this._adapter.notifyChange(this.data);
|
|
195
|
+
this.injectErrorToField(result);
|
|
196
|
+
this._adapter.forceUpdate();
|
|
197
|
+
this._autoScroll(100);
|
|
198
|
+
}
|
|
180
199
|
reject(result);
|
|
181
200
|
}
|
|
182
201
|
}, errors => {
|
|
183
202
|
// validate failed
|
|
184
|
-
|
|
185
|
-
|
|
203
|
+
if (!silent) {
|
|
204
|
+
// this._adapter.notifyChange(this.data);
|
|
205
|
+
this._autoScroll(100);
|
|
206
|
+
}
|
|
186
207
|
reject(errors);
|
|
187
208
|
});
|
|
188
209
|
} else {
|
|
189
210
|
// TODO: current design, returning an empty object will be considered a checksum failure and will be rejected. Only returning an empty string will be considered a success, consider resetting it in 1.0?
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
211
|
+
if (!silent) {
|
|
212
|
+
this.data.errors = maybePromisedErrors;
|
|
213
|
+
this.injectErrorToField(maybePromisedErrors);
|
|
214
|
+
this._adapter.notifyChange(this.data);
|
|
215
|
+
this._adapter.forceUpdate();
|
|
216
|
+
this._autoScroll(100);
|
|
217
|
+
}
|
|
195
218
|
reject(maybePromisedErrors);
|
|
196
219
|
}
|
|
197
220
|
});
|
|
198
221
|
}
|
|
199
222
|
// field level validate
|
|
200
223
|
_fieldsValidate(fieldPaths) {
|
|
224
|
+
let silent = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
|
|
201
225
|
const {
|
|
202
226
|
values
|
|
203
227
|
} = this.data;
|
|
@@ -209,24 +233,37 @@ class FormFoundation extends _foundation.default {
|
|
|
209
233
|
// Call each fieldApi for verification
|
|
210
234
|
const fieldValue = this.getValue(fieldPath);
|
|
211
235
|
// When centralized verification, no need to trigger forceUpdate and notify
|
|
236
|
+
// Each field skips individual updates; a single forceUpdate fires after all resolve.
|
|
212
237
|
const opts = {
|
|
213
238
|
notNotify: true,
|
|
214
|
-
notUpdate: true
|
|
239
|
+
notUpdate: true,
|
|
240
|
+
silent
|
|
215
241
|
};
|
|
216
242
|
const validateResult = field.fieldApi.validate(fieldValue, opts);
|
|
217
243
|
promiseSet.push(validateResult);
|
|
218
|
-
|
|
244
|
+
// Only set touched when not in silent mode
|
|
245
|
+
if (!silent) {
|
|
246
|
+
field.fieldApi.setTouched(true, {
|
|
247
|
+
notNotify: true,
|
|
248
|
+
notUpdate: true
|
|
249
|
+
});
|
|
250
|
+
}
|
|
219
251
|
});
|
|
220
252
|
Promise.all(promiseSet).then(() => {
|
|
221
253
|
// After the centralized verification is completed, trigger notify and forceUpdate once.
|
|
222
|
-
|
|
223
|
-
|
|
254
|
+
// But skip UI updates in silent mode
|
|
255
|
+
if (!silent) {
|
|
256
|
+
this._adapter.notifyChange(this.data);
|
|
257
|
+
this._adapter.forceUpdate();
|
|
258
|
+
}
|
|
224
259
|
const errors = this.getError();
|
|
225
260
|
if (this._isValid(targetFields)) {
|
|
226
261
|
const _values = this._adapter.cloneDeep(values);
|
|
227
262
|
resolve(_values);
|
|
228
263
|
} else {
|
|
229
|
-
|
|
264
|
+
if (!silent) {
|
|
265
|
+
this._autoScroll();
|
|
266
|
+
}
|
|
230
267
|
reject(errors);
|
|
231
268
|
}
|
|
232
269
|
});
|