@douyinfe/semi-foundation 2.24.0 → 2.24.1-alpha.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.
@@ -1,21 +1,23 @@
1
1
  import { isEqual, get, difference, isUndefined, assign, cloneDeep, isEmpty, isNumber, includes } from 'lodash';
2
2
  import BaseFoundation, { DefaultAdapter } from '../base/foundation';
3
+ import { filter } from '../tree/treeUtil';
4
+ import { Motion } from '../utils/type';
3
5
  import {
4
- filter,
6
+ convertDataToEntities,
7
+ findKeysForValues,
8
+ normalizedArr,
9
+ isValid,
10
+ calcMergeType,
5
11
  findAncestorKeys,
12
+
6
13
  calcCheckedKeysForUnchecked,
14
+
7
15
  calcCheckedKeysForChecked,
16
+
8
17
  calcCheckedKeys,
18
+
9
19
  findDescendantKeys,
10
20
  normalizeKeyList
11
- } from '../tree/treeUtil';
12
- import { Motion } from '../utils/type';
13
- import {
14
- convertDataToEntities,
15
- findKeysForValues,
16
- normalizedArr,
17
- isValid,
18
- calcMergeType
19
21
  } from './util';
20
22
  import { strings } from './constants';
21
23
  import isEnterPress from '../utils/isEnterPress';
@@ -24,7 +26,8 @@ export interface BasicData {
24
26
  data: BasicCascaderData;
25
27
  disabled: boolean;
26
28
  key: string;
27
- searchText: any[]
29
+ searchText: any[];
30
+ childrenKeys?: string[]
28
31
  }
29
32
 
30
33
  export interface BasicEntities {
@@ -35,6 +38,7 @@ export interface BasicEntity {
35
38
  _notExist?: boolean;
36
39
  /* children list */
37
40
  children?: Array<BasicEntity>;
41
+ childrenKeys?: string[];
38
42
  /* treedata */
39
43
  data: BasicCascaderData;
40
44
  /* index */
@@ -60,7 +64,8 @@ export interface BasicCascaderData {
60
64
  disabled?: boolean;
61
65
  isLeaf?: boolean;
62
66
  loading?: boolean;
63
- children?: BasicCascaderData[]
67
+ children?: BasicCascaderData[];
68
+ childrenKeys?: string[]
64
69
  }
65
70
 
66
71
  export type CascaderType = 'large' | 'small' | 'default';
@@ -262,11 +267,19 @@ export default class CascaderFoundation extends BaseFoundation<CascaderAdapter,
262
267
  }
263
268
  }
264
269
 
265
- _isLeaf(item: BasicCascaderData) {
270
+ // _isLeaf(item: BasicCascaderData) {
271
+ // if (this.getProp('loadData')) {
272
+ // return Boolean(item.isLeaf);
273
+ // }
274
+ // return !item.children || !item.children.length;
275
+ // }
276
+
277
+ _isLeaf(item: BasicEntity | BasicData) {
278
+ const { data, childrenKeys } = item;
266
279
  if (this.getProp('loadData')) {
267
- return Boolean(item.isLeaf);
280
+ return Boolean(data.isLeaf);
268
281
  }
269
- return !item.children || !item.children.length;
282
+ return !childrenKeys || !childrenKeys;
270
283
  }
271
284
 
272
285
  _clearInput() {
@@ -430,7 +443,8 @@ export default class CascaderFoundation extends BaseFoundation<CascaderAdapter,
430
443
  * When changeOnSelect is turned on, or the target option is a leaf option,
431
444
  * the option is considered to be selected, even if the option is disabled
432
445
  */
433
- if (changeOnSelect || this._isLeaf(selectedItem.data)) {
446
+ // if (changeOnSelect || this._isLeaf(selectedItem.data)) {
447
+ if (changeOnSelect || this._isLeaf(selectedItem)) {
434
448
  updateStates.selectedKeys = new Set([selectedKey]);
435
449
  if (!loadingActive.length) {
436
450
  updateStates.activeKeys = new Set(selectedItem.path);
@@ -574,7 +588,7 @@ export default class CascaderFoundation extends BaseFoundation<CascaderAdapter,
574
588
  handleShowNextByHover(item: BasicEntity) {
575
589
  const { keyEntities } = this.getStates();
576
590
  const { data, key } = item;
577
- const isLeaf = this._isLeaf(data);
591
+ const isLeaf = this._isLeaf(item);
578
592
  const activeKeys = keyEntities[key].path;
579
593
  this._adapter.updateStates({
580
594
  activeKeys: new Set(activeKeys)
@@ -657,7 +671,8 @@ export default class CascaderFoundation extends BaseFoundation<CascaderAdapter,
657
671
  notifyIfLoadData(item: BasicEntity | BasicData) {
658
672
  const { data, key } = item;
659
673
  this._adapter.updateStates({ loading: false });
660
- if (!data.isLeaf && !data.children && this.getProp('loadData')) {
674
+ // if (!data.isLeaf && !data.children && this.getProp('loadData')) {
675
+ if (!data.isLeaf && !data.childrenKeys && this.getProp('loadData')) {
661
676
  const { loadedKeys, loadingKeys } = this.getCopyFromState(['loadedKeys', 'loadingKeys']);
662
677
  if (loadedKeys.has(key) || loadingKeys.has(key)) {
663
678
  return;
@@ -676,7 +691,8 @@ export default class CascaderFoundation extends BaseFoundation<CascaderAdapter,
676
691
  const filterable = this._isFilterable();
677
692
  const { data, key } = item;
678
693
 
679
- const isLeaf = this._isLeaf(data);
694
+ // const isLeaf = this._isLeaf(data);
695
+ const isLeaf = this._isLeaf(item);
680
696
 
681
697
  const activeKeys = keyEntities[key].path;
682
698
  const selectedKey = [key];
@@ -872,7 +888,8 @@ export default class CascaderFoundation extends BaseFoundation<CascaderAdapter,
872
888
  })
873
889
  .filter(
874
890
  item => (filterTreeNode && !filterLeafOnly) ||
875
- this._isLeaf(item as unknown as BasicCascaderData)
891
+ // this._isLeaf(item.data)
892
+ this._isLeaf(item)
876
893
  )
877
894
  .map(item => item.key);
878
895
  }
@@ -942,6 +959,7 @@ export default class CascaderFoundation extends BaseFoundation<CascaderAdapter,
942
959
  if (isSearching && isFilterable) {
943
960
  return this.getFilteredData();
944
961
  }
962
+
945
963
  return (Object.values(keyEntities) as BasicEntity[])
946
964
  .filter(item => item.parentKey === null && !item._notExist)
947
965
  // eslint-disable-next-line @typescript-eslint/ban-ts-comment
@@ -965,7 +983,8 @@ export default class CascaderFoundation extends BaseFoundation<CascaderAdapter,
965
983
  data: item.data,
966
984
  key,
967
985
  disabled: isDisabled,
968
- searchText: itemSearchPath
986
+ searchText: itemSearchPath,
987
+ childrenKeys: item.childrenKeys,
969
988
  });
970
989
  });
971
990
  return filteredList;
package/cascader/util.ts CHANGED
@@ -1,10 +1,15 @@
1
1
  import {
2
2
  isNull,
3
3
  isUndefined,
4
- isEqual
4
+ isEqual,
5
+ uniq,
6
+ isEmpty,
7
+ max
5
8
  } from 'lodash';
6
9
  import { strings } from './constants';
7
10
 
11
+ import { KeyEntities, getSortedKeyList } from '../tree/treeUtil';
12
+
8
13
  function getPosition(level: any, index: any) {
9
14
  return `${level}-${index}`;
10
15
  }
@@ -31,8 +36,9 @@ function traverseDataNodes(treeNodes: any, callback: any) {
31
36
  // Process node if is not root
32
37
  if (node) {
33
38
  const key = parent ? getPosition(parent.key, ind) : `${ind}`;
39
+ const { children, ...resetInfo } = node;
34
40
  item = {
35
- data: { ...node },
41
+ data: { ...resetInfo },
36
42
  ind,
37
43
  key,
38
44
  level: parent ? parent.level + 1 : 0,
@@ -65,10 +71,17 @@ export function convertDataToEntities(dataNodes: any) {
65
71
  keyEntities[key] = entity;
66
72
 
67
73
  // Fill children
68
- entity.parent = keyEntities[parentKey];
69
- if (entity.parent) {
70
- entity.parent.children = entity.parent.children || [];
71
- entity.parent.children.push(entity);
74
+ // entity.parent = keyEntities[parentKey];
75
+ // if (entity.parent) {
76
+ // entity.parent.children = entity.parent.children || [];
77
+ // entity.parent.children.push(entity);
78
+ // }
79
+ const entityParent = keyEntities[parentKey];
80
+ if (entityParent) {
81
+ // entityParent.children = entityParent.children || [];
82
+ // entityParent.children.push(entity);
83
+ entityParent.childrenKeys = entityParent.childrenKeys || [];
84
+ entityParent.childrenKeys.push(key);
72
85
  }
73
86
  });
74
87
  return keyEntities;
@@ -92,4 +105,273 @@ export function calcMergeType(autoMergeValue: boolean, leafOnly: boolean): strin
92
105
  mergeType = strings.NONE_MERGE_TYPE;
93
106
  }
94
107
  return mergeType;
108
+ }
109
+
110
+ export function findChildKeys(keys: string[], options: any, omitKeys: any[] = []) {
111
+ const res: any[] = [];
112
+ keys &&
113
+ keys.forEach(key => {
114
+ const opts = options[key];
115
+ // opts &&
116
+ // opts.children &&
117
+ // opts.children.forEach((child: any) => {
118
+ // if (!omitKeys.length || !omitKeys.includes(child.key)) {
119
+ // res.push(child.key);
120
+ // }
121
+ // });
122
+ opts &&
123
+ opts.childrenKeys &&
124
+ opts.childrenKeys.forEach((child: any) => {
125
+ if (!omitKeys.length || !omitKeys.includes(child.key)) {
126
+ res.push(child.key);
127
+ }
128
+ });
129
+ });
130
+ return res;
131
+ }
132
+
133
+ export function findSiblingKeys(selectedKeys: string[], options: any, self = true) {
134
+ const par: any[] = [];
135
+ // selectedKeys.forEach(item => {
136
+ // if (options[item] && options[item].parent) {
137
+ // par.push(options[item].parent.key);
138
+ // }
139
+ // });
140
+ selectedKeys.forEach(item => {
141
+ if (options[item] && options[item].parentKey) {
142
+ par.push(options[item].parentKey);
143
+ }
144
+ });
145
+
146
+
147
+ const res = findChildKeys(uniq(par), options, self ? [] : selectedKeys);
148
+ return res;
149
+ }
150
+
151
+ export function findAncestorKeys(selectedKeys: string[], options: any, self = true) {
152
+ const res: any[] = [];
153
+ // Recursively find the parent element
154
+ // const findPar = (item: any) => {
155
+ // if (item.parent) {
156
+ // res.push(item.parent.key);
157
+ // findPar(item.parent);
158
+ // }
159
+ // };
160
+ const findPar = (item: any) => {
161
+ if (item.parentKey) {
162
+ res.push(item.parentKey);
163
+ findPar(options[item.parentKey]);
164
+ }
165
+ };
166
+ selectedKeys.forEach(item => {
167
+ options[item] && findPar(options[item]);
168
+ if (self) {
169
+ res.push(item);
170
+ }
171
+ });
172
+ return res;
173
+ }
174
+
175
+ export function calcCheckedKeysForUnchecked(key: string, keyEntities: KeyEntities, checkedKeys: Set<string>, halfCheckedKeys: Set<string>) {
176
+ const descendantKeys = findDescendantKeys([key], keyEntities, true);
177
+ const nodeItem = keyEntities[key];
178
+ descendantKeys.forEach(descendantKey => {
179
+ if (checkedKeys.has(descendantKey)) {
180
+ checkedKeys.delete(descendantKey);
181
+ }
182
+ if (halfCheckedKeys.has(descendantKey)) {
183
+ halfCheckedKeys.delete(descendantKey);
184
+ }
185
+ });
186
+ const calcCurrLevel = (node: any) => {
187
+ // const par = node.parent;
188
+ const par = keyEntities[node.parentKey];
189
+ // no parent
190
+ if (!par) {
191
+ return;
192
+ }
193
+ // Has a parent node, and the parent node is not checked or halfChecked
194
+ if (!checkedKeys.has(par.key) && !halfCheckedKeys.has(par.key)) {
195
+ return;
196
+ }
197
+ // Has a parent node, and the parent node is checked or halfChecked
198
+ // eslint-disable-next-line @typescript-eslint/no-shadow
199
+ const { key } = node;
200
+ const siblingKeys = findSiblingKeys([key], keyEntities);
201
+ // eslint-disable-next-line @typescript-eslint/no-shadow
202
+ const anyChecked = siblingKeys.some(key => checkedKeys.has(key) || halfCheckedKeys.has(key));
203
+ const ancestorKeys = findAncestorKeys([key], keyEntities, false);
204
+ // If there is checked or halfChecked in the sibling node, you need to change the parent node to halfChecked
205
+ if (anyChecked) {
206
+ ancestorKeys.forEach(itemKey => {
207
+ if (checkedKeys.has(itemKey)) {
208
+ checkedKeys.delete(itemKey);
209
+ halfCheckedKeys.add(itemKey);
210
+ }
211
+ });
212
+ // If there is no checked or halfChecked in the sibling node, you need to change the parent node to unchecked
213
+ } else {
214
+ if (checkedKeys.has(par.key)) {
215
+ checkedKeys.delete(par.key);
216
+ }
217
+ if (halfCheckedKeys.has(par.key)) {
218
+ halfCheckedKeys.delete(par.key);
219
+ }
220
+ calcCurrLevel(par);
221
+ }
222
+ };
223
+ calcCurrLevel(nodeItem);
224
+ return {
225
+ checkedKeys,
226
+ halfCheckedKeys,
227
+ };
228
+ }
229
+
230
+ export function calcCheckedKeysForChecked(key: string, keyEntities: KeyEntities, checkedKeys: Set<string>, halfCheckedKeys: Set<string>) {
231
+ const descendantKeys = findDescendantKeys([key], keyEntities, true);
232
+ const nodeItem = keyEntities[key];
233
+ checkedKeys = new Set([...checkedKeys, key]);
234
+ const calcCurrLevel = (node: any) => {
235
+ // if (!node.parent) {
236
+ // return;
237
+ // }
238
+ const par = keyEntities[node.parentKey];
239
+ if (!par) {
240
+ return;
241
+ }
242
+ // eslint-disable-next-line @typescript-eslint/no-shadow
243
+ const { key } = node;
244
+ const siblingKeys = findSiblingKeys([key], keyEntities);
245
+ // eslint-disable-next-line @typescript-eslint/no-shadow
246
+ const allChecked = siblingKeys.every(key => checkedKeys.has(key));
247
+ if (!allChecked) {
248
+ const ancestorKeys = findAncestorKeys([key], keyEntities, false);
249
+ halfCheckedKeys = new Set([...halfCheckedKeys, ...ancestorKeys]);
250
+ } else {
251
+ // const par = node.parent;
252
+ checkedKeys.add(par.key);
253
+ calcCurrLevel(par);
254
+ }
255
+ };
256
+ calcCurrLevel(nodeItem);
257
+ return {
258
+ checkedKeys: new Set([...checkedKeys, ...descendantKeys]),
259
+ halfCheckedKeys,
260
+ };
261
+ }
262
+
263
+ export function calcCheckedKeys(values: any, keyEntities: KeyEntities) {
264
+ const keyList = Array.isArray(values) ? values : [values];
265
+ const descendantKeys = findDescendantKeys(keyList, keyEntities, true);
266
+ /**
267
+ * Recursively find the parent element. Because the incoming nodes are all checked,
268
+ * their descendants must be checked. That is to say, if the descendant nodes have
269
+ * disabled+unchecked nodes, their ancestor nodes will definitely not be checked
270
+ */
271
+ const checkedKeys = new Set([...descendantKeys]);
272
+ let halfCheckedKeys = new Set([]);
273
+ let visited: any[] = [];
274
+
275
+ const levelMap:{[key: number]: string[]} = getSortedKeyList(keyList, keyEntities);
276
+
277
+ const calcCurrLevel = (node: any) => {
278
+ // const { key, parent, level } = node;
279
+ const { key, parentKey, level } = node;
280
+ const parent = keyEntities[parentKey];
281
+ // If the node does not have a parent node, or the node has been processed just now, no processing is done
282
+ if (!parent || visited.includes(key)) {
283
+ return;
284
+ }
285
+
286
+ const siblingKeys = findSiblingKeys([key], keyEntities);
287
+ // visited for caching to avoid double counting
288
+ visited = [...visited, ...siblingKeys];
289
+ const allChecked = siblingKeys.every((siblingKey: string) => checkedKeys.has(siblingKey));
290
+ if (!allChecked) {
291
+ const ancestorKeys = findAncestorKeys([key], keyEntities, false);
292
+ halfCheckedKeys = new Set([...halfCheckedKeys, ...ancestorKeys]);
293
+ } else {
294
+ checkedKeys.add(parent.key);
295
+ // IMPORTANT! parent level may not exist in original level map; if add to the end directly may destroy the hierarchical order
296
+ if (level - 1 in levelMap && level) {
297
+ levelMap[level - 1].push(parent.key);
298
+ } else {
299
+ levelMap[level - 1] = [parent.key];
300
+ }
301
+ }
302
+ };
303
+ // Loop keyList from deepest Level to topLevel, bottom up
304
+ while (!isEmpty(levelMap)) {
305
+ const maxLevel = max(Object.keys(levelMap).map(key => Number(key)));
306
+ levelMap[maxLevel].forEach((key: string) => calcCurrLevel(keyEntities[key]));
307
+ delete levelMap[maxLevel];
308
+ }
309
+
310
+ return {
311
+ checkedKeys,
312
+ halfCheckedKeys,
313
+ };
314
+ }
315
+
316
+ export function findDescendantKeys(selectedKeys: string[], options: KeyEntities, self = true) {
317
+ const res: string[] = [];
318
+ const findChild = (item: any) => {
319
+ if (!item) {
320
+ return;
321
+ }
322
+ // const { children } = item;
323
+ // const hasChildren = isValid(children);
324
+ // if (hasChildren) {
325
+ // children.forEach((child: any) => {
326
+ // res.push(child.key);
327
+ // findChild(options[child.key]);
328
+ // });
329
+ // }
330
+ const { childrenKeys } = item;
331
+ const hasChildren = isValid(childrenKeys);
332
+ if (hasChildren) {
333
+ childrenKeys.forEach((childKey: any) => {
334
+ res.push(childKey);
335
+ findChild(options[childKey]);
336
+ });
337
+ }
338
+ };
339
+ selectedKeys.forEach(item => {
340
+ if (self) {
341
+ res.push(item);
342
+ }
343
+ findChild(options[item]);
344
+ });
345
+ return res;
346
+ }
347
+
348
+ export function normalizeKeyList(keyList: any, keyEntities: KeyEntities, leafOnly = false) {
349
+ const res: string[] = [];
350
+ const keyListSet = new Set(keyList);
351
+ if (!leafOnly) {
352
+ keyList.forEach((key: string) => {
353
+ if (!keyEntities[key]) {
354
+ return;
355
+ }
356
+ const { parentKey } = keyEntities[key];
357
+ if (parentKey && keyListSet.has(parentKey)) {
358
+ return;
359
+ }
360
+ res.push(key);
361
+ });
362
+ } else {
363
+ keyList.forEach(key => {
364
+ // if (keyEntities[key] && !isValid(keyEntities[key].children)) {
365
+ if (keyEntities[key] && !isValid(keyEntities[key].childrenKeys)) {
366
+ res.push(key);
367
+ }
368
+ });
369
+ }
370
+ return res;
371
+ }
372
+
373
+ export function calcDisabledKeys(keyEntities: KeyEntities) {
374
+ const disabledKeys = Object.keys(keyEntities).filter(key => keyEntities[key].data.disabled);
375
+ const { checkedKeys } = calcCheckedKeys(disabledKeys, keyEntities);
376
+ return checkedKeys;
95
377
  }
@@ -1,3 +1,4 @@
1
+ import { setMonth, setYear } from 'date-fns';
1
2
  import BaseFoundation, { DefaultAdapter } from '../base/foundation';
2
3
  import { PresetPosition } from './foundation';
3
4
 
@@ -26,7 +27,7 @@ export interface YearAndMonthFoundationState {
26
27
  currentMonth: number
27
28
  }
28
29
  export interface YearAndMonthAdapter extends DefaultAdapter<YearAndMonthFoundationProps, YearAndMonthFoundationState> {
29
- setCurrentYear: (currentYear: number) => void;
30
+ setCurrentYear: (currentYear: number, cb?: () => void) => void;
30
31
  setCurrentMonth: (currentMonth: number) => void;
31
32
  notifySelectYear: (year: number) => void;
32
33
  notifySelectMonth: (month: number) => void;
@@ -61,7 +62,7 @@ export default class YearAndMonthFoundation extends BaseFoundation<YearAndMonthA
61
62
 
62
63
  selectYear(item: YearScrollItem) {
63
64
  const year = item.value;
64
- this._adapter.setCurrentYear(year);
65
+ this._adapter.setCurrentYear(year, () => this.autoSelectMonth(item));
65
66
  this._adapter.notifySelectYear(year);
66
67
  }
67
68
 
@@ -71,6 +72,29 @@ export default class YearAndMonthFoundation extends BaseFoundation<YearAndMonthA
71
72
  this._adapter.notifySelectMonth(month);
72
73
  }
73
74
 
75
+ /**
76
+ * After selecting a year, if the currentMonth is disabled, automatically select a non-disabled month
77
+ */
78
+ autoSelectMonth(item: YearScrollItem) {
79
+ const { disabledDate, locale } = this._adapter.getProps();
80
+ const { months, currentMonth } = this._adapter.getStates();
81
+
82
+ const currentDate = setYear(Date.now(), item.year);
83
+ const isCurrentMonthDisabled = disabledDate(setMonth(currentDate, currentMonth - 1));
84
+ if (isCurrentMonthDisabled) {
85
+ const currentIndex = months.findIndex(({ month }) => month === currentMonth);
86
+ let validMonth: typeof months[number];
87
+ // First look in the back, if you can't find it in the back, then look in the front
88
+ validMonth = months.slice(currentIndex).find(({ month }) => !disabledDate(setMonth(currentDate, month - 1)));
89
+ if (!validMonth) {
90
+ validMonth = months.slice(0, currentIndex).find(({ month }) => !disabledDate(setMonth(currentDate, month - 1)));
91
+ }
92
+ if (validMonth) {
93
+ this.selectMonth({ month: validMonth.month, value: locale.fullMonths[validMonth.month], disabled: false });
94
+ }
95
+ }
96
+ }
97
+
74
98
  backToMain() {
75
99
  this._adapter.notifyBackToMain();
76
100
  }
@@ -4,6 +4,7 @@ export interface BasicData {
4
4
  disabled: boolean;
5
5
  key: string;
6
6
  searchText: any[];
7
+ childrenKeys?: string[];
7
8
  }
8
9
  export interface BasicEntities {
9
10
  [idx: string]: BasicEntity;
@@ -11,6 +12,7 @@ export interface BasicEntities {
11
12
  export interface BasicEntity {
12
13
  _notExist?: boolean;
13
14
  children?: Array<BasicEntity>;
15
+ childrenKeys?: string[];
14
16
  data: BasicCascaderData;
15
17
  ind: number;
16
18
  key: string;
@@ -28,6 +30,7 @@ export interface BasicCascaderData {
28
30
  isLeaf?: boolean;
29
31
  loading?: boolean;
30
32
  children?: BasicCascaderData[];
33
+ childrenKeys?: string[];
31
34
  }
32
35
  export declare type CascaderType = 'large' | 'small' | 'default';
33
36
  export declare type BasicSimpleValueType = string | number | BasicCascaderData;
@@ -171,7 +174,7 @@ export default class CascaderFoundation extends BaseFoundation<CascaderAdapter,
171
174
  _isDisabled(): any;
172
175
  _isFilterable(): boolean;
173
176
  _notifyChange(item: BasicEntity | BasicData | Set<string>): void;
174
- _isLeaf(item: BasicCascaderData): boolean;
177
+ _isLeaf(item: BasicEntity | BasicData): boolean;
175
178
  _clearInput(): void;
176
179
  _notifyBlur(e: any): void;
177
180
  _notifyFocus(e: any): void;