foundation.ts 50 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285
  1. import {
  2. get,
  3. merge,
  4. isFunction,
  5. each,
  6. find,
  7. some,
  8. pull,
  9. isSet,
  10. filter,
  11. isMap,
  12. slice,
  13. isEqual,
  14. isUndefined
  15. } from 'lodash';
  16. import memoizeOne from 'memoize-one';
  17. import { ArrayElement } from '../utils/type';
  18. import { BaseCheckboxProps } from '../checkbox/checkboxFoundation';
  19. import BaseFoundation, { DefaultAdapter } from '../base/foundation';
  20. import { strings, numbers } from './constants';
  21. import { mergeQueries, flattenColumns, filterColumns } from './utils';
  22. import { pullAll, withOrderSort } from '../utils/array';
  23. export interface BaseColumnProps<RecordType> {
  24. align?: BaseAlign;
  25. children?: Array<BaseColumnProps<RecordType>>;
  26. className?: string;
  27. colSpan?: number;
  28. dataIndex?: string;
  29. defaultFilteredValue?: any[];
  30. defaultSortOrder?: BaseSortOrder;
  31. filterChildrenRecord?: boolean;
  32. filterDropdown?: any;
  33. filterDropdownProps?: Record<string, any>;
  34. filterDropdownVisible?: boolean;
  35. filterIcon?: any;
  36. filterMultiple?: boolean;
  37. filteredValue?: any[];
  38. filters?: BaseFilter[];
  39. fixed?: BaseFixed;
  40. key?: string | number;
  41. onCell?: BaseOnCell<RecordType>;
  42. onFilter?: BaseOnFilter<RecordType>;
  43. onFilterDropdownVisibleChange?: BaseOnFilterDropdownVisibleChange;
  44. onHeaderCell?: BaseOnHeaderCell<RecordType>;
  45. render?: (...args: any[]) => any;
  46. renderFilterDropdownItem?: (...args: any[]) => any;
  47. sortChildrenRecord?: boolean;
  48. sortOrder?: BaseSortOrder;
  49. sorter?: BaseSorter<RecordType>;
  50. title?: any;
  51. useFullRender?: boolean;
  52. width?: string | number;
  53. ellipsis?: BaseEllipsis
  54. }
  55. export interface OnChangeExtra {
  56. changeType?: 'sorter' | 'filter' | 'pagination'
  57. }
  58. interface MouseDownTarget {
  59. targetName: string;
  60. className: string
  61. }
  62. export interface TableAdapter<RecordType> extends DefaultAdapter {
  63. resetScrollY: () => void;
  64. setSelectedRowKeys: (selectedRowKeys: BaseRowKeyType[]) => void;
  65. setDisabledRowKeys: (disabledRowKeys: BaseRowKeyType[]) => void;
  66. setCurrentPage: (currentPage: number) => void;
  67. setPagination: (pagination: BasePagination) => void;
  68. setGroups: (groups: Map<string, RecordType[]>) => void;
  69. setDataSource: (dataSource: RecordType[]) => void;
  70. setExpandedRowKeys: (expandedRowKeys: BaseRowKeyType[]) => void;
  71. setQuery: (query?: BaseColumnProps<RecordType>) => void;
  72. setQueries: (queries: BaseColumnProps<RecordType>[]) => void;
  73. setFlattenData: (flattenData: RecordType[]) => void;
  74. setAllRowKeys: (allRowKeys: BaseRowKeyType[]) => void;
  75. setHoveredRowKey: (hoveredRowKey: BaseRowKeyType) => void;
  76. setCachedFilteredSortedDataSource: (filteredSortedDataSource: RecordType[]) => void;
  77. setCachedFilteredSortedRowKeys: (filteredSortedRowKeys: BaseRowKeyType[]) => void;
  78. getCurrentPage: () => number;
  79. getCurrentPageSize: () => number;
  80. getCachedFilteredSortedDataSource: () => RecordType[];
  81. getCachedFilteredSortedRowKeys: () => BaseRowKeyType[];
  82. getCachedFilteredSortedRowKeysSet: () => Set<BaseRowKeyType>;
  83. setAllDisabledRowKeys: (allDisabledRowKeys: BaseRowKeyType[]) => void;
  84. getAllDisabledRowKeys: () => BaseRowKeyType[];
  85. getAllDisabledRowKeysSet: () => Set<BaseRowKeyType>;
  86. notifyFilterDropdownVisibleChange: (visible: boolean, dataIndex: string) => void;
  87. notifyChange: (changeInfo: { pagination: BasePagination; filters: BaseChangeInfoFilter<RecordType>[]; sorter: BaseChangeInfoSorter<RecordType>; extra: OnChangeExtra }) => void;
  88. notifyExpand: (expanded?: boolean, record?: BaseIncludeGroupRecord<RecordType>, mouseEvent?: any) => void;
  89. notifyExpandedRowsChange: (expandedRows: BaseIncludeGroupRecord<RecordType>[]) => void;
  90. notifySelect: (record?: BaseIncludeGroupRecord<RecordType>, selected?: boolean, selectedRows?: BaseIncludeGroupRecord<RecordType>[], nativeEvent?: any) => void;
  91. notifySelectAll: (selected?: boolean, selectedRows?: BaseIncludeGroupRecord<RecordType>[], changedRows?: BaseIncludeGroupRecord<RecordType>[], e?: any) => void;
  92. notifySelectInvert: (record?: RecordType[], selected?: boolean, selectedRows?: BaseIncludeGroupRecord<RecordType>[], nativeEvent?: any) => void;
  93. notifySelectionChange: (selectedRowKeys: BaseRowKeyType[], selectedRows: BaseIncludeGroupRecord<RecordType>[]) => void;
  94. isAnyColumnFixed: (columns?: BaseColumnProps<RecordType>[]) => boolean;
  95. useFixedHeader: () => boolean;
  96. setHeadWidths: (headWidths: Array<BaseHeadWidth>, index?: number) => void;
  97. getHeadWidths: (index?: number) => number[];
  98. getCellWidths: (flattenedColumns: BaseColumnProps<RecordType>[], flattenedWidths?: BaseHeadWidth[], ignoreScrollBarKey?: boolean) => number[];
  99. mergedRowExpandable: (record: RecordType) => boolean;
  100. isAnyColumnUseFullRender: (columns: BaseColumnProps<RecordType>[]) => boolean;
  101. getNormalizeColumns: () => (columns: BaseColumnProps<RecordType>[], children: any) => BaseColumnProps<RecordType>[];
  102. getHandleColumns: () => (queries: BaseColumnProps<RecordType>[], cachedColumns: BaseColumnProps<RecordType>[]) => BaseColumnProps<RecordType>[];
  103. getMergePagination: () => (pagination: BasePagination) => BasePagination;
  104. setBodyHasScrollbar: (bodyHasScrollBar: boolean) => void;
  105. getTableLayout: () => 'fixed' | 'auto'
  106. }
  107. class TableFoundation<RecordType> extends BaseFoundation<TableAdapter<RecordType>> {
  108. memoizedWithFnsColumns: (
  109. queries: BaseColumnProps<RecordType>[],
  110. cachedColumns: BaseColumnProps<RecordType>[],
  111. rowSelectionUpdate: boolean,
  112. hideExpandedColumn: boolean,
  113. bodyHasScrollBar: boolean,
  114. ) => BaseColumnProps<RecordType>[];
  115. memoizedFilterColumns: (columns: BaseColumnProps<RecordType>[], ignoreKeys?: string[]) => BaseColumnProps<RecordType>[];
  116. memoizedFlattenFnsColumns: (columns: BaseColumnProps<RecordType>[], childrenColumnName?: string) => BaseColumnProps<RecordType>[];
  117. memoizedPagination: (pagination: BasePagination) => BasePagination;
  118. /**
  119. * update columns in place, and use default values as initial values if the sorting and filtering columns have no values
  120. */
  121. static initColumnsFilteredValueAndSorterOrder(columns: BaseColumnProps<unknown>[]) {
  122. columns.forEach(column => {
  123. TableFoundation.initFilteredValue(column);
  124. TableFoundation.initSorterOrder(column);
  125. });
  126. return columns;
  127. }
  128. /**
  129. * init filteredValue of filtering column, use defaultFilteredValue or [] when it is undefined
  130. */
  131. static initFilteredValue(column: BaseColumnProps<unknown>) {
  132. const { defaultFilteredValue, filteredValue } = column;
  133. // There may be cases where onFilter is empty, such as server-side filtering
  134. // Because filterValue affects the output of filters, it needs to be initialized here
  135. if (isUndefined(filteredValue)) {
  136. if (Array.isArray(defaultFilteredValue) && defaultFilteredValue.length) {
  137. column.filteredValue = defaultFilteredValue;
  138. } else {
  139. column.filteredValue = [];
  140. }
  141. }
  142. }
  143. /**
  144. * init sortOrder of sorting column, use defaultSortOrder or [] when it is undefined
  145. */
  146. static initSorterOrder(column: BaseColumnProps<unknown>) {
  147. const { defaultSortOrder, sortOrder, sorter } = column;
  148. if (sorter && isUndefined(sortOrder)) {
  149. if (!isUndefined(defaultSortOrder)) {
  150. column.sortOrder = defaultSortOrder;
  151. } else {
  152. column.sortOrder = false;
  153. }
  154. }
  155. }
  156. mouseDownTarget: MouseDownTarget;
  157. constructor(adapter: TableAdapter<RecordType>) {
  158. super({ ...adapter });
  159. /**
  160. * memoized function list
  161. */
  162. const handleColumns: (queries: BaseColumnProps<RecordType>[], cachedColumns: BaseColumnProps<RecordType>[]) => BaseColumnProps<RecordType>[] = this._adapter.getHandleColumns();
  163. const mergePagination: (pagination: BasePagination) => BasePagination = this._adapter.getMergePagination();
  164. this.mouseDownTarget = null ;
  165. this.memoizedWithFnsColumns = memoizeOne(handleColumns, isEqual);
  166. this.memoizedFilterColumns = memoizeOne(filterColumns);
  167. this.memoizedFlattenFnsColumns = memoizeOne(flattenColumns);
  168. this.memoizedPagination = memoizeOne(mergePagination, isEqual);
  169. }
  170. init() {
  171. const dataSource = [...this.getProp('dataSource')];
  172. const { queries } = this._adapter.getStates();
  173. const filteredSortedDataSource = this.getFilteredSortedDataSource(dataSource, queries);
  174. const allDataDisabledRowKeys = this.getAllDisabledRowKeys(filteredSortedDataSource);
  175. const pageData = this.getCurrentPageData(filteredSortedDataSource);
  176. this.setAdapterPageData(pageData);
  177. this.initExpandedRowKeys(pageData);
  178. this.initSelectedRowKeys(pageData);
  179. // cache dataSource after mount, and then calculate it on demand
  180. this.setCachedFilteredSortedDataSource(filteredSortedDataSource);
  181. this.setAllDisabledRowKeys(allDataDisabledRowKeys);
  182. }
  183. initExpandedRowKeys({ groups }: { groups?: Map<string, RecordType[]> } = {}) {
  184. const {
  185. defaultExpandAllRows,
  186. defaultExpandedRowKeys = [],
  187. expandedRowKeys: propExpandedRowKeys = [],
  188. dataSource = [],
  189. expandAllRows,
  190. defaultExpandAllGroupRows,
  191. expandAllGroupRows,
  192. } = this.getProps();
  193. const expandedRowKeys: BaseRowKeyType[] = [];
  194. if (defaultExpandAllRows || expandAllRows) {
  195. this._addNoDuplicatedItemsToArr(
  196. expandedRowKeys,
  197. this.getAllRowKeys(dataSource),
  198. groups && isMap(groups) && groups.size ? Array.from(groups.keys()) : []
  199. );
  200. } else if (defaultExpandAllGroupRows || expandAllGroupRows) {
  201. this._addNoDuplicatedItemsToArr(
  202. expandedRowKeys,
  203. propExpandedRowKeys,
  204. groups && isMap(groups) && groups.size ? Array.from(groups.keys()) : []
  205. );
  206. } else if (Array.isArray(defaultExpandedRowKeys) && defaultExpandedRowKeys.length) {
  207. this._addNoDuplicatedItemsToArr(expandedRowKeys, defaultExpandedRowKeys);
  208. } else if (Array.isArray(propExpandedRowKeys) && propExpandedRowKeys.length) {
  209. this._addNoDuplicatedItemsToArr(expandedRowKeys, propExpandedRowKeys);
  210. }
  211. this._adapter.setExpandedRowKeys(expandedRowKeys);
  212. }
  213. initSelectedRowKeys({ disabledRowKeys }: { disabledRowKeys?: BaseRowKeyType[] }) {
  214. const rowSelection = this.getProp('rowSelection');
  215. const rowKeys: BaseRowKeyType[] = [];
  216. if (rowSelection) {
  217. const selectedRowKeys = get(rowSelection, 'selectedRowKeys');
  218. const defaultSelectedRowKeys = get(rowSelection, 'defaultSelectedRowKeys');
  219. if (Array.isArray(selectedRowKeys)) {
  220. this._addNoDuplicatedItemsToArr(rowKeys, selectedRowKeys);
  221. } else if (Array.isArray(defaultSelectedRowKeys)) {
  222. this._addNoDuplicatedItemsToArr(rowKeys, defaultSelectedRowKeys);
  223. }
  224. if (Array.isArray(disabledRowKeys) && disabledRowKeys.length) {
  225. pull(rowKeys, ...disabledRowKeys);
  226. }
  227. this._adapter.setSelectedRowKeys(rowKeys);
  228. }
  229. }
  230. /**
  231. * Get filtered and sorted data
  232. * @param {Object[]} dataSource
  233. * @param {Object[]} queries
  234. * @returns {Object[]} sortedDataSource
  235. */
  236. getFilteredSortedDataSource(dataSource: RecordType[], queries: BaseColumnProps<RecordType>[]) {
  237. const filteredDataSource = this.filterDataSource(dataSource, queries.filter(
  238. query => {
  239. const currentFilteredValue = query.filteredValue ? query.filteredValue : query.defaultFilteredValue;
  240. return (
  241. isFunction(query.onFilter) &&
  242. Array.isArray(currentFilteredValue) &&
  243. currentFilteredValue.length
  244. );
  245. }
  246. ));
  247. const sortedDataSource = this.sortDataSource(filteredDataSource, queries.filter(query => query && isFunction(query.sorter)));
  248. return sortedDataSource;
  249. }
  250. /**
  251. * get current page data
  252. *
  253. * @param {Array} dataSource
  254. * @param {object} pagination
  255. * @param {object} queries
  256. * @returns {{dataSource: RecordType[], groups: Map<string, Set<string>>, pagination: object, disabledRowKeys: string[], queries: BaseColumnProps[], allRowKeys: string[]}}
  257. */
  258. getCurrentPageData(dataSource?: RecordType[], pagination?: BasePagination, queries?: BaseColumnProps<RecordType>[]) {
  259. const filteredSortedDataSource = this._adapter.getCachedFilteredSortedDataSource();
  260. dataSource = dataSource == null ? [...filteredSortedDataSource] : dataSource;
  261. pagination =
  262. pagination == null ? this.getState('pagination') && { ...this.getState('pagination') } : pagination;
  263. queries = queries == null ? [...this.getState('queries')] : queries;
  264. let groups;
  265. if (this.getProp('groupBy') != null) {
  266. const { groups: groupedGroups, dataSource: groupedData } = this.groupDataSource(dataSource);
  267. dataSource = groupedData;
  268. groups = groupedGroups;
  269. }
  270. pagination = this.normalizePagination(pagination, dataSource);
  271. dataSource = this.limitPageDataSource(dataSource, pagination);
  272. const disabledRowKeys = this.getAllDisabledRowKeys(dataSource);
  273. const allRowKeys = this.getAllRowKeys(dataSource);
  274. const pageData: BasePageData<RecordType> = {
  275. dataSource,
  276. groups,
  277. pagination,
  278. disabledRowKeys,
  279. allRowKeys,
  280. queries,
  281. };
  282. return pageData;
  283. }
  284. /**
  285. * group dataSource, return grouped row keys
  286. *
  287. * @param {*[]} dataSource
  288. * @param {Function|string} groupBy
  289. */
  290. groupDataSource(dataSource: RecordType[], groupBy?: BaseGroupBy<RecordType>) {
  291. groupBy = groupBy == null ? this.getProp('groupBy') : groupBy;
  292. const groups = new Map();
  293. const newDataSource = [];
  294. if (groupBy != null) {
  295. each(dataSource, (record, index) => {
  296. const groupKey = typeof groupBy === 'function' ? groupBy(record) : get(record, groupBy);
  297. if (groupKey != null && groupKey !== '') {
  298. const recordKey = this.getRecordKey(record);
  299. let group = groups.get(groupKey);
  300. if (!isSet(group)) {
  301. group = new Set([recordKey]);
  302. groups.set(groupKey, group);
  303. } else {
  304. group.add(recordKey);
  305. }
  306. }
  307. });
  308. }
  309. if (groups && groups.size) {
  310. groups.forEach((set, key) => {
  311. if (isSet(set)) {
  312. set.forEach(realKey => {
  313. newDataSource.push(this._getRecord(realKey));
  314. });
  315. }
  316. });
  317. } else {
  318. newDataSource.push(...dataSource);
  319. }
  320. return { groups, dataSource: newDataSource };
  321. }
  322. /**
  323. * sort data
  324. *
  325. * @param {Array} dataSource
  326. * @param {Array} sorters
  327. * @returns {Array}
  328. */
  329. sortDataSource(dataSource: RecordType[], sorters: BaseSorterInfo<RecordType>[]) {
  330. each(sorters, sorterObj => {
  331. // const sorterObj = last(sorters) || {};
  332. const { sorter, sortOrder, defaultSortOrder, sortChildrenRecord } = sorterObj;
  333. const currentSortOrder = this.isSortOrderValid(sortOrder) ? sortOrder : defaultSortOrder;
  334. if (isFunction(sorter) && (currentSortOrder && strings.SORT_DIRECTIONS.includes(currentSortOrder))) {
  335. if (sortChildrenRecord) {
  336. const childrenRecordName = this.getProp('childrenRecordName');
  337. dataSource =
  338. dataSource &&
  339. dataSource.map(record => {
  340. const children = this._getRecordChildren(record);
  341. if (Array.isArray(children) && children.length) {
  342. return {
  343. ...record,
  344. [childrenRecordName]: this.sortDataSource(children, [sorterObj]),
  345. };
  346. }
  347. return record;
  348. });
  349. }
  350. dataSource.sort(withOrderSort(sorter, currentSortOrder));
  351. return false;
  352. }
  353. return undefined;
  354. });
  355. return dataSource;
  356. }
  357. /**
  358. * set page number
  359. */
  360. setPage = (currentPage: number, currentPageSize: number) => {
  361. currentPage = currentPage || this._adapter.getCurrentPage();
  362. const currentPagination = this.getState('pagination');
  363. const { dataSource, pagination, disabledRowKeys, allRowKeys } = this.getCurrentPageData(null, {
  364. ...currentPagination,
  365. currentPage,
  366. pageSize: currentPageSize,
  367. });
  368. if (!this._pagerIsControlled() && currentPage > 0) {
  369. this._adapter.setDisabledRowKeys(disabledRowKeys);
  370. this._adapter.setAllRowKeys(allRowKeys);
  371. this._adapter.setPagination(pagination);
  372. this._adapter.setDataSource(dataSource);
  373. }
  374. this._notifyChange(pagination, undefined, undefined, { changeType: 'pagination' });
  375. };
  376. /**
  377. * filter data source
  378. *
  379. * @param {*[]} dataSource
  380. * @param {*[]} filters
  381. * @returns {*[]}
  382. */
  383. filterDataSource(dataSource: RecordType[], filters: BaseChangeInfoFilter<RecordType>[]) {
  384. let filteredData: Map<string, RecordType> | null = null;
  385. let hasValidFilters = false;
  386. const childrenRecordName = this.getProp('childrenRecordName');
  387. each(filters, filterObj => {
  388. const { onFilter, filteredValue, filterChildrenRecord, defaultFilteredValue } = filterObj;
  389. const currentFilteredValue = Array.isArray(filteredValue) ? filteredValue : defaultFilteredValue;
  390. if (typeof onFilter === 'function' && Array.isArray(currentFilteredValue) && currentFilteredValue.length) {
  391. hasValidFilters = true;
  392. if (filteredData === null) {
  393. filteredData = new Map();
  394. } else {
  395. dataSource = Array.from(filteredData && filteredData.values());
  396. filteredData = new Map();
  397. }
  398. each(dataSource, record => {
  399. each(currentFilteredValue, value => {
  400. const childrenRecords = get(record, childrenRecordName);
  401. const recordKey = this.getRecordKey(record);
  402. let filteredChildren;
  403. if (Array.isArray(childrenRecords) && childrenRecords.length && filterChildrenRecord) {
  404. filteredChildren = this.filterDataSource(childrenRecords, [filterObj]);
  405. }
  406. if (Array.isArray(filteredChildren) && filteredChildren.length) {
  407. if (recordKey != null) {
  408. const children = get(filteredData.get(recordKey), childrenRecordName, []);
  409. filteredData.set(recordKey, {
  410. ...record,
  411. [childrenRecordName]: filteredChildren.reduce(
  412. (arr, cur) => {
  413. if (
  414. arr.find((item: any) => this.getRecordKey(item) === this.getRecordKey(cur)) ==
  415. null
  416. ) {
  417. arr.push(cur);
  418. }
  419. return arr;
  420. },
  421. // @ts-ignore
  422. [...children]
  423. ),
  424. });
  425. }
  426. } else if (onFilter(value, record)) {
  427. filteredData.set(recordKey, record);
  428. }
  429. });
  430. });
  431. }
  432. });
  433. if (hasValidFilters) {
  434. dataSource = Array.from(filteredData && filteredData.values());
  435. }
  436. return dataSource;
  437. }
  438. limitPageDataSource(dataSource: RecordType[], pagination: BasePagination) {
  439. dataSource = dataSource == null ? this.getProp('dataSource') : dataSource;
  440. pagination = pagination == null ? this.getState('pagination') : pagination;
  441. let pageData = dataSource;
  442. const pageNo = get(pagination, 'currentPage');
  443. if (this.getProp('pagination') !== false && pageNo && dataSource && pagination && !this._pagerIsControlled()) {
  444. const { pageSize = numbers.DEFAULT_PAGE_SIZE } = pagination;
  445. const start = (pageNo - 1) * pageSize;
  446. const end = pageNo * pageSize;
  447. pageData = slice(dataSource, start, end);
  448. }
  449. return pageData;
  450. }
  451. normalizePagination(pagination: BasePagination, dataSource: RecordType[]) {
  452. pagination = pagination == null ? this._getPagination() : pagination;
  453. dataSource = dataSource == null ? this._getDataSource() : dataSource;
  454. const propPagination = this.getProp('pagination');
  455. if (pagination) {
  456. pagination = typeof pagination === 'object' ? { ...pagination } : {};
  457. pagination = merge(
  458. {
  459. total: (dataSource && dataSource.length) || 0,
  460. pageSize: numbers.DEFAULT_PAGE_SIZE,
  461. currentPage: get(propPagination, 'defaultCurrentPage', 1),
  462. position: strings.PAGINATION_POSITIONS[0],
  463. },
  464. pagination
  465. );
  466. if (!this._pagerIsControlled()) {
  467. const total = get(propPagination, 'total', dataSource.length);
  468. const { currentPage, pageSize } = pagination;
  469. const realTotalPage = Math.ceil(total / pageSize);
  470. pagination.total = total;
  471. if (currentPage > realTotalPage) {
  472. pagination.currentPage = 1;
  473. }
  474. }
  475. }
  476. return pagination;
  477. }
  478. setAdapterPageData(pageData: BasePageData<RecordType> = {}) {
  479. const { pagination, dataSource, disabledRowKeys, allRowKeys, groups } = pageData;
  480. this._adapter.setDisabledRowKeys(disabledRowKeys);
  481. this._adapter.setAllRowKeys(allRowKeys);
  482. this._adapter.setPagination(pagination);
  483. this._adapter.setGroups(groups);
  484. this._adapter.setDataSource(dataSource);
  485. }
  486. /**
  487. * Cache related data when initializing or updating the calculated dataSource
  488. * @param {*} filteredSortedDataSource
  489. */
  490. setCachedFilteredSortedDataSource = (filteredSortedDataSource: RecordType[]) => {
  491. this._adapter.setCachedFilteredSortedDataSource(filteredSortedDataSource);
  492. const filteredSortedRowKeys = this.getAllRowKeys(filteredSortedDataSource);
  493. this._adapter.setCachedFilteredSortedRowKeys(filteredSortedRowKeys);
  494. };
  495. destroy() { }
  496. setAllDisabledRowKeys(disabledRowKeys) {
  497. this._adapter.setAllDisabledRowKeys(disabledRowKeys);
  498. }
  499. handleClick(e: any) { }
  500. handleMouseEnter(e: any) { }
  501. handleMouseLeave(e: any) { }
  502. stopPropagation(e: any) {
  503. this._adapter.stopPropagation(e);
  504. }
  505. /**
  506. * Add non-repeating elements to the array itself
  507. */
  508. _addNoDuplicatedItemsToArr(srcArr: any[] = [], ...objArrs: any[][]) {
  509. for (const objArr of objArrs) {
  510. if (Array.isArray(objArr)) {
  511. for (const item of objArr) {
  512. if (!srcArr.includes(item)) {
  513. srcArr.push(item);
  514. }
  515. }
  516. }
  517. }
  518. return srcArr;
  519. }
  520. _notifyChange(pagination: BasePagination, filters?: BaseChangeInfoFilter<RecordType>[], sorter?: BaseChangeInfoSorter<RecordType>, extra?: OnChangeExtra) {
  521. pagination = pagination == null ? this._getPagination() : pagination;
  522. filters = filters == null ? this._getAllFilters() : filters;
  523. sorter = sorter == null ? this._getAllSorters()[0] as BaseChangeInfoSorter<RecordType> : sorter;
  524. if (get(this.getProp('scroll'), 'scrollToFirstRowOnChange')) {
  525. this._adapter.resetScrollY();
  526. }
  527. this._adapter.notifyChange({
  528. pagination: { ...pagination },
  529. filters: [...filters],
  530. sorter,
  531. extra: { ...extra },
  532. });
  533. }
  534. _rowExpansionIsControlled() {
  535. return Array.isArray(this.getProp('expandedRowKeys'));
  536. }
  537. _pagerIsControlled() {
  538. return get(this.getProp('pagination'), 'currentPage') != null;
  539. }
  540. _selectionIsControlled() {
  541. return Array.isArray(get(this.getProp('rowSelection'), 'selectedRowKeys'));
  542. }
  543. /**
  544. * Determine whether the column sorting is controlled
  545. * Controlled: the column passed the sortOrder prop
  546. * @param {String} dataIndex
  547. * @returns {Boolean}
  548. */
  549. _sorterIsControlled(dataIndex: string) {
  550. // The basis for judgment should be props columns instead of cachedColumns fix#1141
  551. const query = dataIndex && this.getQuery(dataIndex, this.getState('flattenColumns'));
  552. return Boolean(query && query.sortOrder != null);
  553. }
  554. /**
  555. * Determine whether the column is filtered and controlled
  556. * Controlled: the column passed the filteredValue prop
  557. * @param {String} dataIndex
  558. * @returns {Boolean}
  559. */
  560. _filterIsControlled(dataIndex: string) {
  561. const query = dataIndex && this.getQuery(dataIndex, this.getState('flattenColumns'));
  562. return Boolean(query && Array.isArray(query.filteredValue));
  563. }
  564. _filterShowIsControlled(dataIndex?: string) {
  565. const query = dataIndex && this.getQuery(dataIndex, this.getState('flattenColumns'));
  566. return Boolean(query && (query.filterDropdownVisible === true || query.filterDropdownVisible === false));
  567. }
  568. _getSelectedRowKeys() {
  569. const rowSelection = this.getState('rowSelection');
  570. const selectedRowKeys = get(rowSelection, 'selectedRowKeys', []);
  571. return [...selectedRowKeys];
  572. }
  573. _getSelectedRowKeysSet() {
  574. const rowSelection = this.getState('rowSelection');
  575. const selectedRowKeysSet = get(rowSelection, 'selectedRowKeysSet', new Set());
  576. return selectedRowKeysSet;
  577. }
  578. _getDataSource() {
  579. return this.getProp('dataSource') || [];
  580. }
  581. _getRecord(realKey: string | number) {
  582. return find(
  583. this.getProp('dataSource'),
  584. record => realKey != null && realKey !== '' && this.getRecordKey(record) === realKey
  585. );
  586. }
  587. _getRecordChildren(record: RecordType) {
  588. return get(record, this.getProp('childrenRecordName'));
  589. }
  590. _getPagination() {
  591. return this.getState('pagination') || {};
  592. }
  593. /**
  594. * Filters are considered valid if filteredValue exists
  595. */
  596. _getAllFilters(queries?: BaseColumnProps<RecordType>[]) {
  597. queries = queries || this.getState('queries');
  598. const filters: BaseChangeInfoFilter<RecordType>[] = [];
  599. each(queries, query => {
  600. if (
  601. Array.isArray(query.filteredValue) &&
  602. (query.filteredValue.length || this._filterIsControlled(query.dataIndex))
  603. ) {
  604. filters.push(query);
  605. }
  606. });
  607. return filters;
  608. }
  609. _getAllSorters(queries?: BaseColumnProps<RecordType>[]): BaseColumnProps<RecordType>[] {
  610. queries = queries || this.getState('queries');
  611. return filter(queries, query => query.sorter && query.sortOrder) as BaseColumnProps<RecordType>[];
  612. }
  613. _filterQueries(targetQuery: BaseColumnProps<RecordType>, queries: BaseColumnProps<RecordType>[], keys = ['dataIndex']) {
  614. queries = queries == null ? this.getState('queries') : queries;
  615. const filteredQueries: BaseColumnProps<RecordType>[] = [];
  616. const filteredIndexes: number[] = [];
  617. each(queries, (itQuery, index) => {
  618. const flag = some(keys, k => k && targetQuery[k] != null && targetQuery[k] === itQuery[k]);
  619. if (flag) {
  620. filteredQueries.push(itQuery);
  621. filteredIndexes.push(index);
  622. }
  623. });
  624. return { filteredQueries, filteredIndexes };
  625. }
  626. _mergeToQueries(query: BaseColumnProps<RecordType>, queries: BaseColumnProps<RecordType>[], keys = ['dataIndex']) {
  627. queries = queries == null ? this.getState('queries') : queries;
  628. queries = [...queries];
  629. query = { ...query };
  630. const { filteredQueries, filteredIndexes } = this._filterQueries(query, queries, keys);
  631. each(filteredQueries, (curQuery, idx) => {
  632. // assign(curQuery, query);
  633. queries[filteredIndexes[idx]] = { ...query };
  634. });
  635. return queries;
  636. }
  637. /**
  638. * get record real key
  639. * @param {RecordType} record
  640. * @returns {string}
  641. */
  642. getRecordKey(record: RecordType): string {
  643. if (!record) {
  644. return undefined;
  645. }
  646. const rowKey = this.getProp('rowKey');
  647. return typeof rowKey === 'function' ? rowKey(record) : get(record, rowKey);
  648. }
  649. isEmpty(dataSource: RecordType[]) {
  650. dataSource = dataSource == null ? this.getProp('dataSource') : dataSource;
  651. return !(Array.isArray(dataSource) && dataSource.length > 0);
  652. }
  653. handleSelectRow(realKey: BaseRowKeyType, selected: boolean, e: any) {
  654. this.stopPropagation(e);
  655. if (typeof selected === 'boolean' && realKey != null) {
  656. const selectedRowKeys = this._getSelectedRowKeys();
  657. let foundIdx = -1;
  658. const selectedRow = this.getSelectedRows(null, [realKey])[0];
  659. let selectedRows: BaseIncludeGroupRecord<RecordType>[];
  660. if ((foundIdx = selectedRowKeys.indexOf(realKey)) > -1 && selected === false) {
  661. selectedRowKeys.splice(foundIdx, 1);
  662. selectedRows = this.getSelectedRows(null, selectedRowKeys);
  663. if (!this._selectionIsControlled()) {
  664. this._adapter.setSelectedRowKeys(selectedRowKeys);
  665. }
  666. this._adapter.notifySelect(selectedRow, selected, selectedRows, e);
  667. this._adapter.notifySelectionChange(selectedRowKeys, selectedRows);
  668. } else if (selectedRowKeys.indexOf(realKey) === -1 && selected === true) {
  669. selectedRowKeys.push(realKey);
  670. selectedRows = this.getSelectedRows(null, selectedRowKeys);
  671. if (!this._selectionIsControlled()) {
  672. this._adapter.setSelectedRowKeys(selectedRowKeys);
  673. }
  674. this._adapter.notifySelect(selectedRow, selected, selectedRows, e);
  675. this._adapter.notifySelectionChange(selectedRowKeys, selectedRows);
  676. }
  677. }
  678. }
  679. /**
  680. * select all rows
  681. * @param {*} selected The future state of the select all button
  682. * @param {*} e
  683. */
  684. handleSelectAllRow(selected: boolean, e: any) {
  685. this.stopPropagation(e);
  686. if (typeof selected === 'boolean') {
  687. const curSelectedRowKeys = this._getSelectedRowKeys();
  688. let selectedRowKeys = [...curSelectedRowKeys];
  689. const selectedRowKeysSet = this._getSelectedRowKeysSet();
  690. let allRowKeys = [...this._adapter.getCachedFilteredSortedRowKeys()];
  691. const disabledRowKeys = this._adapter.getAllDisabledRowKeys();
  692. const disabledRowKeysSet = this._adapter.getAllDisabledRowKeysSet();
  693. let changedRowKeys;
  694. // Select all, if not disabled && not in selectedRowKeys
  695. if (selected) {
  696. for (const key of allRowKeys) {
  697. if (!disabledRowKeysSet.has(key) && !selectedRowKeysSet.has(key)) {
  698. selectedRowKeys.push(key);
  699. }
  700. }
  701. allRowKeys = pullAll(allRowKeys, [...disabledRowKeys, ...curSelectedRowKeys]);
  702. changedRowKeys = [...allRowKeys];
  703. } else {
  704. selectedRowKeys = pullAll(selectedRowKeys, allRowKeys);
  705. changedRowKeys = [...curSelectedRowKeys];
  706. }
  707. const changedRows = this.getSelectedRows(null, changedRowKeys || []);
  708. const selectedRows = this.getSelectedRows(null, selectedRowKeys || []);
  709. if (!this._selectionIsControlled()) {
  710. this._adapter.setSelectedRowKeys(selectedRowKeys);
  711. }
  712. this._adapter.notifySelectAll(selected, selectedRows, changedRows, e);
  713. this._adapter.notifySelectionChange(selectedRowKeys, selectedRows);
  714. }
  715. }
  716. /**
  717. * row keys => rows
  718. * @param {*} dataSource
  719. * @param {*} selectedRowKeys
  720. * @param {*} selectedRowKeysSet Recursive optimization
  721. */
  722. getSelectedRows(dataSource: RecordType[], selectedRowKeys: BaseRowKeyType[], selectedRowKeysSet?: Set<BaseRowKeyType>): BaseIncludeGroupRecord<RecordType>[] {
  723. dataSource = dataSource == null ? this._getDataSource() : dataSource;
  724. selectedRowKeys = selectedRowKeys == null ? this._getSelectedRowKeys() : selectedRowKeys;
  725. if (!isSet(selectedRowKeysSet)) {
  726. selectedRowKeysSet = new Set(selectedRowKeys);
  727. }
  728. const childrenRecordName = this.getProp('childrenRecordName');
  729. const selectedRows: BaseIncludeGroupRecord<RecordType>[] = [];
  730. if (
  731. isSet(selectedRowKeysSet) &&
  732. selectedRowKeysSet.size &&
  733. Array.isArray(dataSource) &&
  734. dataSource.length
  735. ) {
  736. // Time complexity optimization, replace the includes operation of array with has of set
  737. selectedRows.push(...dataSource.filter(data => selectedRowKeysSet.has(this.getRecordKey(data))));
  738. if (selectedRows.length < selectedRowKeys.length) {
  739. for (const item of dataSource) {
  740. const children = get(item, childrenRecordName);
  741. if (Array.isArray(children) && children.length) {
  742. const rows = this.getSelectedRows(children, selectedRowKeys, selectedRowKeysSet);
  743. selectedRows.push(...rows);
  744. }
  745. }
  746. }
  747. }
  748. return selectedRows;
  749. }
  750. getAllDisabledRowKeys(dataSource?: RecordType[], getCheckboxProps?: GetCheckboxProps<RecordType>): BaseRowKeyType[] {
  751. dataSource = dataSource == null ? this._getDataSource() : dataSource;
  752. getCheckboxProps =
  753. getCheckboxProps == null ? get(this.getProp('rowSelection'), 'getCheckboxProps') : getCheckboxProps;
  754. const childrenRecordName = this.getProp('childrenRecordName');
  755. const disabledRowKeys: BaseRowKeyType[] = [];
  756. if (Array.isArray(dataSource) && dataSource.length && typeof getCheckboxProps === 'function') {
  757. for (const record of dataSource) {
  758. const props = getCheckboxProps(record);
  759. if (props && props.disabled) {
  760. disabledRowKeys.push(this.getRecordKey(record));
  761. }
  762. const children = get(record, childrenRecordName);
  763. if (Array.isArray(children) && children.length) {
  764. const keys: BaseRowKeyType[] = this.getAllDisabledRowKeys(children, getCheckboxProps);
  765. disabledRowKeys.push(...keys);
  766. }
  767. }
  768. }
  769. return disabledRowKeys;
  770. }
  771. getAllRowKeys(dataSource: RecordType[]): BaseRowKeyType[] {
  772. dataSource = dataSource == null ? this._getDataSource() : dataSource;
  773. const childrenRecordName = this.getProp('childrenRecordName');
  774. const allRowKeys = [];
  775. if (Array.isArray(dataSource) && dataSource.length) {
  776. for (const record of dataSource) {
  777. const childrenRowKeys = [];
  778. const children = get(record, childrenRecordName);
  779. if (Array.isArray(children) && children.length) {
  780. childrenRowKeys.push(...this.getAllRowKeys(children));
  781. }
  782. allRowKeys.push(this.getRecordKey(record), ...childrenRowKeys);
  783. }
  784. }
  785. return allRowKeys;
  786. }
  787. /**
  788. * Check if the selected item is in allRowKeysSet
  789. * @param {Array} selectedRowKeys
  790. * @param {Set} allRowKeysSet
  791. */
  792. hasRowSelected(selectedRowKeys: BaseRowKeyType[], allRowKeysSet: Set<BaseRowKeyType>) {
  793. return Boolean(Array.isArray(selectedRowKeys) &&
  794. selectedRowKeys.length &&
  795. isSet(allRowKeysSet) &&
  796. allRowKeysSet.size &&
  797. selectedRowKeys.filter(key => allRowKeysSet.has(key)).length);
  798. }
  799. /**
  800. * expand processing function
  801. * @param {Boolean} expanded
  802. * @param {String} realKey
  803. * @param {Event} domEvent
  804. */
  805. handleRowExpanded(expanded: boolean, realKey: string, domEvent: any) {
  806. this.stopPropagation(domEvent);
  807. const expandedRowKeys = [...this.getState('expandedRowKeys')];
  808. const index = expandedRowKeys.indexOf(realKey);
  809. const keyIsValid = typeof realKey === 'string' || typeof realKey === 'number';
  810. if (keyIsValid && expanded && index === -1) {
  811. expandedRowKeys.push(realKey);
  812. } else if (keyIsValid && !expanded && index > -1) {
  813. expandedRowKeys.splice(index, 1);
  814. }
  815. if (!this._rowExpansionIsControlled()) {
  816. this._adapter.setExpandedRowKeys(expandedRowKeys);
  817. }
  818. const expandedRows = this.getSelectedRows(null, expandedRowKeys);
  819. let expandedRow = this.getSelectedRows(null, [realKey])[0];
  820. // groups record processing
  821. const groups = this._getGroups();
  822. if (groups) {
  823. // Construct group expandRow
  824. if (groups.has(realKey)) {
  825. expandedRow = { groupKey: realKey };
  826. }
  827. // If expandedRowKeys includes groupKey, add to expandedRows
  828. for (let i = 0, len = expandedRowKeys.length; i < len; i++) {
  829. if (groups.has(realKey)) {
  830. expandedRows.push({ groupKey: expandedRowKeys[i] });
  831. }
  832. }
  833. }
  834. this._adapter.notifyExpand(expanded, expandedRow, domEvent);
  835. this._adapter.notifyExpandedRowsChange(expandedRows);
  836. }
  837. /**
  838. * get state.groups
  839. * @returns {Map|Null}
  840. */
  841. _getGroups() {
  842. const groupBy = this._adapter.getProp('groupBy');
  843. if (groupBy !== null) {
  844. const groups = this._adapter.getState('groups');
  845. return groups;
  846. }
  847. return null;
  848. }
  849. /**
  850. * Determine whether you have selected all except for disabled
  851. * @param {Set} selectedRowKeysSet
  852. * @param {Set} disabledRowKeysSet
  853. * @param {Array} allKeys keys after sorted and filtered
  854. */
  855. allIsSelected(selectedRowKeysSet: Set<BaseRowKeyType>, disabledRowKeysSet: Set<BaseRowKeyType>, allKeys: BaseRowKeyType[]) {
  856. const filteredAllKeys = filter(allKeys, key => key != null && !disabledRowKeysSet.has(key));
  857. if (filteredAllKeys && filteredAllKeys.length) {
  858. for (const key of filteredAllKeys) {
  859. if (key != null && !selectedRowKeysSet.has(key)) {
  860. return false;
  861. }
  862. }
  863. return true;
  864. } else {
  865. const isAllSelected = allKeys.length && allKeys.every(rowKey => selectedRowKeysSet.has(rowKey));
  866. return isAllSelected || false;
  867. }
  868. }
  869. /**
  870. * This function is not used yet
  871. * @param {*} selectedRowKeys
  872. * @param {*} allKeys
  873. */
  874. allIsNotSelected(selectedRowKeys: BaseRowKeyType[], allKeys: BaseRowKeyType[]) {
  875. for (const key of allKeys) {
  876. if (key != null && Array.isArray(selectedRowKeys) && selectedRowKeys.includes(key)) {
  877. return true;
  878. }
  879. }
  880. return false;
  881. }
  882. formatPaginationInfo(pagination: BasePagination = {}, defaultPageText = '') {
  883. let info = '';
  884. const formatPageText = get(this.getProp('pagination'), 'formatPageText');
  885. const { total, pageSize, currentPage } = pagination;
  886. const currentStart = Math.min((currentPage - 1) * pageSize + 1, total);
  887. const currentEnd = Math.min(currentPage * pageSize, total);
  888. if (formatPageText || (formatPageText !== false && defaultPageText && total > 0)) {
  889. info =
  890. typeof formatPageText === 'function' ?
  891. formatPageText({ currentStart, currentEnd, total }) :
  892. defaultPageText
  893. .replace('${currentStart}', currentStart as any)
  894. .replace('${currentEnd}', currentEnd as any)
  895. .replace('${total}', total as any);
  896. }
  897. return info;
  898. }
  899. toggleShowFilter(dataIndex: string, visible: boolean) {
  900. let filterObj: BaseColumnProps<RecordType> = this.getQuery(dataIndex);
  901. const filterDropdownVisible = visible;
  902. filterObj = { ...filterObj, filterDropdownVisible };
  903. if (!this._filterShowIsControlled()) {
  904. // this._adapter.setQuery({
  905. // ...filterObj,
  906. // filterDropdownVisible,
  907. // });
  908. }
  909. this._adapter.notifyFilterDropdownVisibleChange(filterDropdownVisible, dataIndex);
  910. }
  911. /**
  912. * Called when the filter changes
  913. * @param {*} dataIndex
  914. * @param {*} data
  915. */
  916. handleFilterSelect(dataIndex: string, data: { filteredValue?: string[] } = {}) {
  917. let query: BaseColumnProps<RecordType> = this.getQuery(dataIndex);
  918. let queries = [...this._adapter.getState('queries')];
  919. const { filteredValue } = data;
  920. query = {
  921. ...query,
  922. filteredValue,
  923. };
  924. queries = mergeQueries(query, queries);
  925. const mergedQueries = this._mergeToQueries(query, null);
  926. const filters = this._getAllFilters(mergedQueries);
  927. if (!this._filterIsControlled(dataIndex)) {
  928. this._adapter.setQueries(queries);
  929. this.handleClickFilterOrSorter(queries);
  930. }
  931. this._notifyChange(null, filters, undefined, { changeType: 'filter' });
  932. }
  933. /**
  934. * Click the sort button to call
  935. * @param {*} column
  936. * @param {*} e
  937. */
  938. handleSort(column: { dataIndex?: string; sortOrder?: BaseSortOrder } = {}, e: any, check = false) {
  939. this.stopPropagation(e);
  940. /* if mouse down to the resizable handle, do not trigger the sorting,fix #2802
  941. The target of the click event may be different from the target of the mousedown,
  942. e.g: Press the mouse, move to another node and then release it,
  943. So according to the event in the mousedown to determine whether to trigger the sorting
  944. */
  945. if (check) {
  946. if (this.mouseDownTarget && this.mouseDownTarget?.targetName === 'SPAN' &&
  947. this.mouseDownTarget?.className?.includes?.('react-resizable-handle')
  948. ) {
  949. return;
  950. }
  951. this.mouseDownTarget = null;
  952. }
  953. const { dataIndex } = column;
  954. let queries = this.getState('queries');
  955. let curQuery = null;
  956. queries = [...queries];
  957. each(queries, (query, idx, arr) => {
  958. if (query.sorter) {
  959. const sorterObj = { ...query };
  960. const stateSortOrder = get(sorterObj, 'sortOrder');
  961. const defaultSortOrder = get(sorterObj, 'defaultSortOrder', false);
  962. let querySortOrder = this.isSortOrderValid(stateSortOrder) ? stateSortOrder : defaultSortOrder;
  963. if (dataIndex && dataIndex === sorterObj.dataIndex) {
  964. if (querySortOrder === strings.SORT_DIRECTIONS[0]) {
  965. querySortOrder = strings.SORT_DIRECTIONS[1];
  966. } else if (querySortOrder === strings.SORT_DIRECTIONS[1]) {
  967. querySortOrder = false;
  968. } else {
  969. querySortOrder = strings.SORT_DIRECTIONS[0];
  970. }
  971. } else {
  972. // This results in the current click only supports single column sorting
  973. querySortOrder = false;
  974. }
  975. arr[idx] = { ...sorterObj, sortOrder: querySortOrder };
  976. if (dataIndex === sorterObj.dataIndex) {
  977. curQuery = arr[idx];
  978. }
  979. }
  980. });
  981. if (!this._sorterIsControlled(dataIndex)) {
  982. this._adapter.setQueries(queries);
  983. this.handleClickFilterOrSorter(queries);
  984. }
  985. // notify sort event
  986. this._notifyChange(null, null, curQuery, { changeType: 'sorter' });
  987. }
  988. handleMouseDown = (e: any) => {
  989. this.mouseDownTarget = {
  990. targetName: e.target?.tagName,
  991. className: e.target?.className,
  992. };
  993. }
  994. /**
  995. * Recalculate the cached data after clicking filter or sorter
  996. * @param {*} queries
  997. */
  998. handleClickFilterOrSorter(queries: BaseColumnProps<RecordType>[]) {
  999. const dataSource = [...this.getProp('dataSource')];
  1000. const sortedDataSource = this.getFilteredSortedDataSource(dataSource, queries);
  1001. const allDataDisabledRowKeys = this.getAllDisabledRowKeys(sortedDataSource);
  1002. this.setCachedFilteredSortedDataSource(sortedDataSource);
  1003. this.setAllDisabledRowKeys(allDataDisabledRowKeys);
  1004. const pageData = this.getCurrentPageData(sortedDataSource);
  1005. this.setAdapterPageData(pageData);
  1006. }
  1007. getQuery(dataIndex: string, queries?: BaseColumnProps<RecordType>[]) {
  1008. queries = queries || this.getState('queries');
  1009. if (dataIndex != null) {
  1010. return find(queries, query => query.dataIndex === dataIndex);
  1011. }
  1012. return undefined;
  1013. }
  1014. getCellWidths(flattenedColumns: BaseColumnProps<RecordType>[], flattenedWidths?: BaseHeadWidth[], ignoreScrollBarKey?: boolean) {
  1015. return this._adapter.getCellWidths(flattenedColumns, flattenedWidths, ignoreScrollBarKey);
  1016. }
  1017. setHeadWidths(headWidths: Array<BaseHeadWidth>, index?: number) {
  1018. return this._adapter.setHeadWidths(headWidths, index);
  1019. }
  1020. getHeadWidths(index: number) {
  1021. return this._adapter.getHeadWidths(index);
  1022. }
  1023. mergedRowExpandable(record: RecordType) {
  1024. return this._adapter.mergedRowExpandable(record);
  1025. }
  1026. setBodyHasScrollbar(bodyHasScrollbar: boolean) {
  1027. this._adapter.setBodyHasScrollbar(bodyHasScrollbar);
  1028. }
  1029. isSortOrderValid = (sortOrder: BaseSortOrder) => strings.SORT_DIRECTIONS.includes(sortOrder as any) || sortOrder === false;
  1030. }
  1031. export type BaseRowKeyType = string | number;
  1032. export interface BasePagination {
  1033. total?: number;
  1034. currentPage?: number;
  1035. pageSize?: number;
  1036. position?: ArrayElement<typeof strings.PAGINATION_POSITIONS>;
  1037. defaultCurrentPage?: number;
  1038. formatPageText?: any
  1039. }
  1040. export interface BaseHeadWidth {
  1041. width: number;
  1042. key: string
  1043. }
  1044. export interface BasePageData<RecordType> {
  1045. dataSource?: RecordType[];
  1046. groups?: Map<string, RecordType[]>;
  1047. pagination?: BasePagination;
  1048. disabledRowKeys?: BaseRowKeyType[];
  1049. allRowKeys?: BaseRowKeyType[];
  1050. queries?: BaseColumnProps<RecordType>[]
  1051. }
  1052. export type GetCheckboxProps<RecordType> = (record?: RecordType) => BaseCheckboxProps;
  1053. export type BaseGroupBy<RecordType> = string | number | BaseGroupByFn<RecordType>;
  1054. export type BaseGroupByFn<RecordType> = (record?: RecordType) => string | number;
  1055. export interface BaseSorterInfo<RecordType> {
  1056. [x: string]: any;
  1057. dataIndex?: string;
  1058. sortOrder?: BaseSortOrder;
  1059. sorter?: BaseSorter<RecordType>
  1060. }
  1061. export type BaseSortOrder = boolean | ArrayElement<typeof strings.SORT_DIRECTIONS>;
  1062. export type BaseSorter<RecordType> = boolean | ((a?: RecordType, b?: RecordType, sortOrder?: 'ascend' | 'descend') => number);
  1063. export interface BaseChangeInfoFilter<RecordType> {
  1064. dataIndex?: string;
  1065. value?: any;
  1066. text?: any;
  1067. filters?: BaseFilter[];
  1068. onFilter?: (filteredValue?: any, record?: RecordType) => boolean;
  1069. filteredValue?: any[];
  1070. defaultFilteredValue?: any[];
  1071. children?: BaseFilter[];
  1072. filterChildrenRecord?: boolean
  1073. }
  1074. export interface BaseFilter {
  1075. value?: any;
  1076. text?: any;
  1077. children?: BaseFilter[]
  1078. }
  1079. export type BaseFixed = ArrayElement<typeof strings.FIXED_SET>;
  1080. export type BaseAlign = ArrayElement<typeof strings.ALIGNS>;
  1081. export type BaseOnCell<RecordType> = (record?: RecordType, rowIndex?: number) => BaseOnCellReturnObject;
  1082. export interface BaseOnCellReturnObject {
  1083. [x: string]: any;
  1084. style?: Record<string, any>;
  1085. className?: string;
  1086. onClick?: (e: any) => void
  1087. }
  1088. export type BaseOnFilter<RecordType> = (filteredValue?: any, record?: RecordType) => boolean;
  1089. export type BaseOnFilterDropdownVisibleChange = (visible?: boolean) => void;
  1090. export type BaseOnHeaderCell<RecordType> = (record?: RecordType, columnIndex?: number) => BaseOnHeaderCellReturnObject;
  1091. export interface BaseOnHeaderCellReturnObject {
  1092. [x: string]: any;
  1093. style?: Record<string, any>;
  1094. className?: string;
  1095. onClick?: (e: any) => void
  1096. }
  1097. export interface BaseChangeInfoSorter<RecordType> {
  1098. [x: string]: any;
  1099. dataIndex: string;
  1100. sortOrder: BaseSortOrder;
  1101. sorter: BaseSorter<RecordType>
  1102. }
  1103. export type BaseIncludeGroupRecord<RecordType> = RecordType | { groupKey: string };
  1104. export type BaseEllipsis = boolean | { showTitle: boolean };
  1105. export default TableFoundation;