import { action, computed, makeObservable, reaction } from 'mobx'; import { ToolbarBIReporter } from './ToolbarBIReporter'; import { BulkActionModalState, FiltersMap, KeyedItem, MultiCollectionSupport, Translate, } from '@wix/bex-core'; import { SidePanelsState } from './SidePanelsState'; import { createRef, RefObject } from 'react'; interface SelectAllLocationParams { location: 'TableToolbar CTA' | 'Checkbox'; } export interface BulkActionToolbarStateParams { readonly toolbarBIReporter: ToolbarBIReporter; readonly panels: SidePanelsState; readonly multi: MultiCollectionSupport; readonly translate: Translate; readonly scrollToTop: () => unknown; } export type SelectionDisabledProp = | ((rowData: KeyedItem) => boolean | void) | boolean; export type CheckboxTooltipContentProp = ( rowData: KeyedItem, ) => string | undefined; export class BulkActionToolbarState { readonly toolbarBIReporter; readonly panels; readonly multi; readonly modals; readonly bulkSelectionCheckboxRef: RefObject = createRef(); selectionDisabledProp: SelectionDisabledProp = () => false; checkboxTooltipContentProp?: CheckboxTooltipContentProp; shouldIncludeCollection?: (collection: any) => boolean; selectionDisabledFn = ({ key }: { key: string }) => { const { selectionDisabledProp } = this; if (typeof selectionDisabledProp === 'function') { const keyedItem = this._getCollectionOf(key)?.result.get(key); if (keyedItem == null) { return false; } return Boolean(selectionDisabledProp(keyedItem)); } return () => !!selectionDisabledProp; }; getCheckboxTooltipContent = ({ key, }: { key: string; }): string | undefined => { const { checkboxTooltipContentProp } = this; if (typeof checkboxTooltipContentProp === 'function') { const keyedItem = this._getCollectionOf(key)?.result.get(key); if (keyedItem != null) { return checkboxTooltipContentProp(keyedItem); } } return undefined; }; constructor(params: BulkActionToolbarStateParams) { this.toolbarBIReporter = params.toolbarBIReporter; this.panels = params.panels; this.multi = params.multi; this.modals = new BulkActionModalState({ translate: params.translate, scrollToTop: params.scrollToTop, }); makeObservable(this, { hasClickableRows: computed, reachedMaxSelection: computed, total: computed, selectedCountOrTotal: computed, selectedIds: computed, }); } maxSelection?: number; _selectionStartTime = 0; private get filteredCollections() { return this.shouldIncludeCollection ? this.multi.collections.filter(this.shouldIncludeCollection) : this.multi.collections; } get selectedIds() { return this.multi.collections.reduce( (a, { bulkSelect }) => [...a, ...bulkSelect.selectedIds], [] as string[], ); } get selectedValues() { return this.multi.collections.reduce( (a, { bulkSelect }) => [...a, ...bulkSelect.selectedValues], [] as unknown[], ); } get uncheckedValues() { return this.multi.collections.reduce( (a, { bulkSelect }) => [...a, ...bulkSelect.uncheckedValues], [] as unknown[], ); } get selectedCount() { return this.filteredCollections.reduce( (s, { bulkSelect }) => s + bulkSelect.selectedCount, 0, ); } get selectedCountOrTotal() { if (this.allSelected) { return this.filteredCollections.reduce( (s, { bulkSelect }) => s + bulkSelect.selectedCountOrTotal, 0, ); } return this.selectedCount; } get hasClickableRows() { return this.filteredCollections.some(({ bulkSelect, ...collection }) => collection.result.keyedItems.some( (row) => !bulkSelect.select.isChecked(row.key) && !this.isRowCheckboxDisabled(row), ), ); } get allSelected() { return this.filteredCollections.every( ({ bulkSelect }) => bulkSelect.allSelected, ); } get reachedMaxSelection(): boolean { const { maxSelection, selectedCount } = this; if (maxSelection != null) { return selectedCount >= maxSelection; } return false; } get total() { return this.filteredCollections.reduce( (s, { result: { total } }) => s + total, 0, ); } isChecked(key: string) { return this.multi.collections.some(({ bulkSelect }) => bulkSelect.isChecked(key), ); } clearSelection = () => { this.multi.collections.forEach(({ bulkSelect }) => { bulkSelect.resetToDefaults(); }); }; isRowCheckboxDisabled = action((row: { key: string }) => { return ( (this.reachedMaxSelection && !this.isChecked(row.key)) || this.selectionDisabledFn(row) ); }); toggleSelectAll = action((params: SelectAllLocationParams) => { if (!this.hasClickableRows) { this.deselectAll(params); } else if (this.maxSelection != null) { this.selectAllUpToMaxSelection(); } else { this.selectAll(params); } }); selectAllUpToMaxSelection() { for (const collection of this.filteredCollections) { if (this.reachedMaxSelection) { break; } for (const item of collection.result.keyedItems) { if (this.reachedMaxSelection) { break; } if (!this.isRowCheckboxDisabled(item)) { collection.bulkSelect.select.set(item); } } } } selectAll({ location }: SelectAllLocationParams) { const onSelectAllToggledEnd = this.toolbarBIReporter.selectAllToggledStart({ location, }); this.filteredCollections.forEach((collection) => collection.bulkSelect.selectAll(), ); onSelectAllToggledEnd(); } deselectAll({ location }: SelectAllLocationParams) { const onSelectAllToggledEnd = this.toolbarBIReporter.selectAllToggledStart({ location, }); this.filteredCollections.forEach((collection) => collection.bulkSelect.deselectAll(), ); onSelectAllToggledEnd(); this.bulkSelectionCheckboxRef.current?.focus(); } _getCollectionOf(id: string) { return this.multi.collections.find((collection) => collection.result.has(id), ); } selectOne(id: string) { const onSelectionToggledEnd = this.toolbarBIReporter.itemSelectionToggledStarted({ itemId: id, clickType: 'Selection', }); this._getCollectionOf(id)?.bulkSelect.selectOne(id); onSelectionToggledEnd(); } deselectOne(id: string) { const onSelectionToggledEnd = this.toolbarBIReporter.itemSelectionToggledStarted({ itemId: id, clickType: 'Deselection', }); this._getCollectionOf(id)?.bulkSelect.deselectOne(id); onSelectionToggledEnd(); } init() { const disposers = [ ...this.multi.collections.map(({ bulkSelect }) => reaction( () => ({ selectedValues: bulkSelect.select.selectedValues, }), ({ selectedValues }) => { if (this.selectionDisabledFn == null) { return; } for (const keyedItem of selectedValues) { const collectionItem = bulkSelect.getCollectionItem( keyedItem.key, ); if (collectionItem && this.selectionDisabledFn(collectionItem)) { bulkSelect.deselectOneItem(keyedItem); } } }, ), ), ]; return () => { disposers.forEach((d) => d()); }; } }