foundation.ts 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230
  1. import { omit } from 'lodash';
  2. import BaseFoundation, { DefaultAdapter } from '../base/foundation';
  3. import { BasicValue as BasicTreeValue } from '../tree/foundation';
  4. import { strings } from './constants';
  5. import { _generateGroupedData, _generateTreeData } from './transferUtils';
  6. import arrayMove from '../utils/arrayMove';
  7. export interface BasicDataItem {
  8. [x: string]: any;
  9. /* key is required */
  10. key: string | number;
  11. label?: any;
  12. value?: string | number;
  13. disabled?: boolean;
  14. style?: any;
  15. className?: string
  16. }
  17. export type DataItemMap = Map<number | string, BasicDataItem>;
  18. export interface OnSortEndProps {
  19. oldIndex: number;
  20. newIndex: number
  21. }
  22. export interface BasicResolvedDataItem extends BasicDataItem {
  23. _parent?: {
  24. title: string
  25. };
  26. _optionKey?: string | number
  27. }
  28. export interface TransferAdapter<P = Record<string, any>, S = Record<string, any>> extends DefaultAdapter<P, S> {
  29. getSelected: () => DataItemMap;
  30. updateSelected: (selectedItems: DataItemMap) => void;
  31. notifyChange: (values: Array<number | string>, items: Array<BasicDataItem>) => void;
  32. notifySearch: (input: string) => void;
  33. notifySelect: (items: BasicDataItem) => void;
  34. notifyDeselect: (items: BasicDataItem) => void;
  35. updateInput: (input: string) => void;
  36. updateSearchResult: (searchResult: Set<number | string>) => void;
  37. searchTree: (keyword: string) => void
  38. }
  39. // eslint-disable-next-line max-len
  40. export default class TransferFoundation<P = Record<string, any>, S = Record<string, any>> extends BaseFoundation<TransferAdapter<P, S>> {
  41. constructor(adapter: TransferAdapter<P, S>) {
  42. super({ ...adapter });
  43. }
  44. _generateGroupedData(dataSource: any[]) {
  45. return _generateGroupedData(dataSource);
  46. }
  47. _generateTreeData(dataSource: any[]) {
  48. return _generateTreeData(dataSource);
  49. }
  50. _generatePath(item: BasicResolvedDataItem) {
  51. const { path = [] } = item;
  52. return path.map((p: any) => p.label).join(' > ');
  53. }
  54. handleInputChange(inputVal: string) {
  55. const { data } = this.getStates();
  56. const { filter, type } = this.getProps();
  57. if (type === strings.TYPE_TREE_TO_LIST) {
  58. const searchResult = new Set(data.map((item: BasicResolvedDataItem) => item.key)) as Set<number | string>;
  59. this._adapter.searchTree(inputVal);
  60. this._adapter.notifySearch(inputVal);
  61. this._adapter.updateInput(inputVal);
  62. this._adapter.updateSearchResult(searchResult);
  63. return;
  64. }
  65. const filterFunc = typeof filter === 'function' ?
  66. (item: BasicResolvedDataItem) => filter(inputVal, item) :
  67. (item: BasicResolvedDataItem) => typeof item.label === 'string' && item.label.includes(inputVal);
  68. const searchData = data.filter(filterFunc);
  69. const searchResult = new Set(searchData.map((item: BasicResolvedDataItem) => item.key)) as Set<number | string>;
  70. this._adapter.notifySearch(inputVal);
  71. this._adapter.updateInput(inputVal);
  72. this._adapter.updateSearchResult(searchResult);
  73. }
  74. // Select or cancel all unhidden items
  75. handleAll(wantAllChecked: boolean) {
  76. const { disabled, type } = this.getProps();
  77. const { selectedItems, data, searchResult, inputValue } = this.getStates();
  78. if (disabled) {
  79. return;
  80. }
  81. const inSearchMode = inputValue !== '';
  82. let operateData = [];
  83. operateData = inSearchMode ? data.filter((item: BasicResolvedDataItem) => searchResult.has(item.key)) : data;
  84. operateData = type === strings.TYPE_TREE_TO_LIST ? data : operateData;
  85. let newSelectedItems = new Map();
  86. switch (true) {
  87. case !wantAllChecked:
  88. newSelectedItems = new Map(selectedItems);
  89. operateData.forEach((item: BasicResolvedDataItem) => {
  90. // If the item is disabled, keep it
  91. if (!item.disabled) {
  92. newSelectedItems.delete(item.key);
  93. }
  94. });
  95. break;
  96. case wantAllChecked:
  97. newSelectedItems = new Map(selectedItems);
  98. operateData.forEach((item: BasicResolvedDataItem) => {
  99. if (item.disabled) {
  100. // The disabled item, judge whether it is selected, if it is selected, still need to add the selection
  101. if (selectedItems.has(item.key)) {
  102. newSelectedItems.set(item.key, item);
  103. }
  104. return;
  105. }
  106. newSelectedItems.set(item.key, item);
  107. });
  108. break;
  109. default:
  110. break;
  111. }
  112. if (!this._isControlledComponent()) {
  113. this._adapter.updateSelected(newSelectedItems);
  114. }
  115. this._notifyChange(newSelectedItems);
  116. }
  117. handleClear() {
  118. const { disabled } = this.getProps();
  119. const { selectedItems, data } = this.getStates();
  120. if (disabled) {
  121. return;
  122. }
  123. const newSelectedItems = new Map(selectedItems) as DataItemMap;
  124. data.forEach((item: BasicResolvedDataItem) => {
  125. // If the item is disabled, keep it
  126. if (!item.disabled) {
  127. newSelectedItems.delete(item.key);
  128. }
  129. });
  130. if (!this._isControlledComponent()) {
  131. this._adapter.updateSelected(newSelectedItems);
  132. }
  133. this._notifyChange(newSelectedItems);
  134. }
  135. handleSelectOrRemove(item: BasicResolvedDataItem) {
  136. const { disabled } = this.getProps();
  137. const selectedItems = this._adapter.getSelected();
  138. if (disabled || item.disabled) {
  139. return;
  140. }
  141. if (selectedItems.has(item.key)) {
  142. selectedItems.delete(item.key);
  143. this._adapter.notifyDeselect(item);
  144. } else {
  145. selectedItems.set(item.key, item);
  146. this._adapter.notifySelect(item);
  147. }
  148. if (!this._isControlledComponent()) {
  149. this._adapter.updateSelected(selectedItems);
  150. }
  151. this._notifyChange(selectedItems);
  152. }
  153. handleSelect(values: BasicTreeValue) {
  154. const { disabled } = this.getProps();
  155. const selectedItems = this._adapter.getSelected();
  156. const { data } = this.getStates();
  157. const dataItems = data.map((d: BasicResolvedDataItem) => [d.value, d]);
  158. const allItemsMap = new Map(dataItems);
  159. const nextSelectedItemsMap = new Map();
  160. if (disabled) {
  161. return;
  162. }
  163. (values as any).forEach((value: any) => {
  164. const node = allItemsMap.get(value) as BasicResolvedDataItem;
  165. // The value passed in is an array of the value used, but the internal selectedItems stores a map of keys
  166. if (selectedItems.has(node.key)) {
  167. nextSelectedItemsMap.set(node.key, node);
  168. return;
  169. }
  170. if (node.disabled) {
  171. return;
  172. }
  173. nextSelectedItemsMap.set(node.key, node);
  174. return;
  175. });
  176. if (!this._isControlledComponent()) {
  177. this._adapter.updateSelected(nextSelectedItemsMap);
  178. }
  179. this._notifyChange(nextSelectedItemsMap);
  180. }
  181. getValuesAndItemsFromMap(selectedItems: DataItemMap) {
  182. const { type } = this.getProps();
  183. const items = [];
  184. const values = [];
  185. for (const item of selectedItems) {
  186. const obj = (type === strings.TYPE_GROUP_LIST ? omit(item[1], '_parent') : item[1]) as BasicDataItem;
  187. items.push(obj);
  188. values.push(obj.value);
  189. }
  190. return { items, values };
  191. }
  192. _notifyChange(selectedItems: DataItemMap) {
  193. const { items, values } = this.getValuesAndItemsFromMap(selectedItems);
  194. this._adapter.notifyChange(values, items);
  195. }
  196. handleSortEnd(callbackProps: OnSortEndProps) {
  197. const { oldIndex, newIndex } = callbackProps;
  198. const selectedItems = this._adapter.getSelected();
  199. let selectedArr = [...selectedItems.values()];
  200. selectedArr = arrayMove(selectedArr, oldIndex, newIndex);
  201. let newSelectedItems = new Map();
  202. selectedArr.forEach(option => {
  203. newSelectedItems = newSelectedItems.set(option.key, option);
  204. });
  205. this._adapter.updateSelected(newSelectedItems);
  206. this._notifyChange(newSelectedItems);
  207. }
  208. }