foundation.ts 8.6 KB

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