@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.
Files changed (77) hide show
  1. package/aiChatInput/foundation.ts +3 -1
  2. package/autoComplete/foundation.ts +3 -2
  3. package/cascader/foundation.ts +8 -3
  4. package/collapsible/foundation.ts +1 -0
  5. package/datePicker/datePicker.scss +2 -2
  6. package/descriptions/foundation.ts +3 -1
  7. package/form/foundation.ts +61 -29
  8. package/form/interface.ts +9 -2
  9. package/input/textareaFoundation.ts +34 -1
  10. package/lib/cjs/aiChatInput/foundation.js +3 -1
  11. package/lib/cjs/autoComplete/foundation.d.ts +1 -0
  12. package/lib/cjs/autoComplete/foundation.js +1 -1
  13. package/lib/cjs/cascader/foundation.d.ts +1 -0
  14. package/lib/cjs/cascader/foundation.js +8 -3
  15. package/lib/cjs/collapsible/foundation.d.ts +1 -0
  16. package/lib/cjs/datePicker/datePicker.css +2 -2
  17. package/lib/cjs/datePicker/datePicker.scss +2 -2
  18. package/lib/cjs/descriptions/foundation.js +3 -1
  19. package/lib/cjs/form/foundation.d.ts +5 -5
  20. package/lib/cjs/form/foundation.js +58 -21
  21. package/lib/cjs/form/interface.d.ts +8 -2
  22. package/lib/cjs/input/textareaFoundation.d.ts +12 -0
  23. package/lib/cjs/input/textareaFoundation.js +39 -0
  24. package/lib/cjs/modal/modalFoundation.d.ts +6 -0
  25. package/lib/cjs/modal/modalFoundation.js +36 -4
  26. package/lib/cjs/pagination/foundation.js +28 -7
  27. package/lib/cjs/pincode/foundation.d.ts +1 -1
  28. package/lib/cjs/resizable/resizable.css +3 -3
  29. package/lib/cjs/resizable/variables.scss +2 -2
  30. package/lib/cjs/select/foundation.d.ts +1 -1
  31. package/lib/cjs/select/foundation.js +19 -8
  32. package/lib/cjs/steps/bacisSteps.scss +8 -2
  33. package/lib/cjs/steps/steps.css +6 -0
  34. package/lib/cjs/upload/foundation.d.ts +8 -1
  35. package/lib/cjs/upload/foundation.js +70 -22
  36. package/lib/cjs/utils/escapeHtml.d.ts +9 -0
  37. package/lib/cjs/utils/escapeHtml.js +90 -0
  38. package/lib/cjs/utils/object.js +3 -1
  39. package/lib/es/aiChatInput/foundation.js +3 -1
  40. package/lib/es/autoComplete/foundation.d.ts +1 -0
  41. package/lib/es/autoComplete/foundation.js +1 -1
  42. package/lib/es/cascader/foundation.d.ts +1 -0
  43. package/lib/es/cascader/foundation.js +8 -3
  44. package/lib/es/collapsible/foundation.d.ts +1 -0
  45. package/lib/es/datePicker/datePicker.css +2 -2
  46. package/lib/es/datePicker/datePicker.scss +2 -2
  47. package/lib/es/descriptions/foundation.js +3 -1
  48. package/lib/es/form/foundation.d.ts +5 -5
  49. package/lib/es/form/foundation.js +58 -21
  50. package/lib/es/form/interface.d.ts +8 -2
  51. package/lib/es/input/textareaFoundation.d.ts +12 -0
  52. package/lib/es/input/textareaFoundation.js +39 -0
  53. package/lib/es/modal/modalFoundation.d.ts +6 -0
  54. package/lib/es/modal/modalFoundation.js +36 -4
  55. package/lib/es/pagination/foundation.js +28 -7
  56. package/lib/es/pincode/foundation.d.ts +1 -1
  57. package/lib/es/resizable/resizable.css +3 -3
  58. package/lib/es/resizable/variables.scss +2 -2
  59. package/lib/es/select/foundation.d.ts +1 -1
  60. package/lib/es/select/foundation.js +19 -8
  61. package/lib/es/steps/bacisSteps.scss +8 -2
  62. package/lib/es/steps/steps.css +6 -0
  63. package/lib/es/upload/foundation.d.ts +8 -1
  64. package/lib/es/upload/foundation.js +70 -22
  65. package/lib/es/utils/escapeHtml.d.ts +9 -0
  66. package/lib/es/utils/escapeHtml.js +84 -0
  67. package/lib/es/utils/object.js +3 -1
  68. package/modal/modalFoundation.ts +33 -32
  69. package/package.json +35 -5
  70. package/pagination/foundation.ts +25 -7
  71. package/pincode/foundation.ts +1 -1
  72. package/resizable/variables.scss +2 -2
  73. package/select/foundation.ts +19 -8
  74. package/steps/bacisSteps.scss +8 -2
  75. package/upload/foundation.ts +81 -24
  76. package/utils/escapeHtml.ts +94 -0
  77. 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
- // console.log('outer key down handle');
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.label = renderItem(item);
148
+ option._renderedLabel = renderItem(item);
148
149
  }
149
150
  options.push(option);
150
151
  });
@@ -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
- if (!isLeaf && !allowChange && !isSearching) {
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
- if (isLeaf && enableLeafClick) {
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 {
@@ -6,6 +6,7 @@ export interface CollapsibleFoundationProps {
6
6
  keepDOM?: boolean;
7
7
  className?: string;
8
8
  collapseHeight?: number;
9
+ collapseHeightAdaptive?: boolean;
9
10
  reCalcKey?: number | string;
10
11
  id?: string;
11
12
  fade?: boolean
@@ -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 columns) {
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) {
@@ -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(fieldPaths);
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
- this.injectErrorToField({});
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
- this.injectErrorToField({});
210
+ if (!silent) {
211
+ this.injectErrorToField({});
212
+ }
194
213
  } else {
195
- this.data.errors = result;
196
- this._adapter.notifyChange(this.data);
197
-
198
- this.injectErrorToField(result);
199
- this._adapter.forceUpdate();
200
- this._autoScroll(100);
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
- // this._adapter.notifyChange(this.data);
207
- this._autoScroll(100);
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
- this.data.errors = maybePromisedErrors;
214
- this.injectErrorToField(maybePromisedErrors);
215
- this._adapter.notifyChange(this.data);
216
-
217
- this._adapter.forceUpdate();
218
- this._autoScroll(100);
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>): Promise<unknown> {
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
- const opts = {
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
- field.fieldApi.setTouched(true, opts);
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
- this._adapter.notifyChange(this.data);
246
-
247
- this._adapter.forceUpdate();
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
- this._autoScroll();
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 manual */
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
- // console.log('outer key down handle');
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.label = renderItem(item);
122
+ option._renderedLabel = renderItem(item);
123
123
  }
124
124
  options.push(option);
125
125
  });
@@ -110,6 +110,7 @@ export interface BasicCascaderProps {
110
110
  disableStrictly?: boolean;
111
111
  leafOnly?: boolean;
112
112
  enableLeafClick?: boolean;
113
+ clickToSelect?: boolean;
113
114
  preventScroll?: boolean;
114
115
  virtualizeInSearch?: Virtualize;
115
116
  checkRelation?: string;
@@ -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
- if (!isLeaf && !allowChange && !isSearching) {
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
- if (isLeaf && enableLeafClick) {
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 {
@@ -5,6 +5,7 @@ export interface CollapsibleFoundationProps {
5
5
  keepDOM?: boolean;
6
6
  className?: string;
7
7
  collapseHeight?: number;
8
+ collapseHeightAdaptive?: boolean;
8
9
  reCalcKey?: number | string;
9
10
  id?: string;
10
11
  fade?: boolean;
@@ -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 columns) {
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>): Promise<unknown>;
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(fieldPaths);
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
- this.injectErrorToField({});
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
- this.injectErrorToField({});
188
+ if (!silent) {
189
+ this.injectErrorToField({});
190
+ }
174
191
  } else {
175
- this.data.errors = result;
176
- this._adapter.notifyChange(this.data);
177
- this.injectErrorToField(result);
178
- this._adapter.forceUpdate();
179
- this._autoScroll(100);
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
- // this._adapter.notifyChange(this.data);
185
- this._autoScroll(100);
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
- this.data.errors = maybePromisedErrors;
191
- this.injectErrorToField(maybePromisedErrors);
192
- this._adapter.notifyChange(this.data);
193
- this._adapter.forceUpdate();
194
- this._autoScroll(100);
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
- field.fieldApi.setTouched(true, opts);
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
- this._adapter.notifyChange(this.data);
223
- this._adapter.forceUpdate();
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
- this._autoScroll();
264
+ if (!silent) {
265
+ this._autoScroll();
266
+ }
230
267
  reject(errors);
231
268
  }
232
269
  });