import { BulkSelectKeyedItem, CollectionState, FiltersMap, Fqdn, PickerEvents, PickerState, ReportBI, SelectionEntry, selectionStatusWixPatternsPreset, TaskState, FilterEventOptions, withoutDefaults, Filter, } from '@wix/bex-core'; import { addEventListener } from '@wix/bex-core/util'; import type { PickerModalState } from '../hooks'; import { UsePickerContentParamsBase } from '../hooks/usePickerContent'; import { fromMultipleSelectionToBoolean } from '../components/MaxSelection'; import { action, makeObservable, observable, runInAction } from 'mobx'; import { cairoFilterToggled, newItemCreationStart } from '@wix/bex-core/bi'; import { EventEmitter } from 'events'; import { TypedEmitter } from '@wix/bex-core/events'; import { PickerContentStateBI } from './PickerContentStateBI'; import { AdditionalStepState } from './AdditionalStepState'; const isPickerSelectionV2 = ( e: UsePickerContentParamsBase['initialSelect'], ) => e != null && typeof e === 'object' && ('defaultStatus' in e || 'values' in e); export interface PickerContentStateParams { modalState: PickerModalState; collection: CollectionState; createBILogger:

(defaultParams: P) => ReportBI; events?: PickerEvents; initialSelect?: UsePickerContentParamsBase['initialSelect']; } export class PickerContentState { readonly picker: PickerState; readonly modalState: PickerModalState; readonly initTask = new TaskState(); readonly reportBi; readonly events = new EventEmitter() as TypedEmitter<{ beforeToggleIsSelectAll: () => { afterToggleIsSelectAll: () => unknown; }; }>; initialSelect: PickerContentStateParams['initialSelect']; additionalStep?: AdditionalStepState; isAdditionalStepShown = false; autoAdditionalStepOnInitialSelect = false; isAutoAdditionalStepPending = true; constructor(params: PickerContentStateParams) { const { collection, modalState, createBILogger, events, initialSelect } = params; this.initialSelect = initialSelect; this.modalState = modalState; this.reportBi = createBILogger({ ...collection.fqdn, type: 'modal', componentType: 'Picker Modal', artifactId: modalState.environment.artifactId, }); this.picker = new PickerState({ collection, getMaxItems: modalState.getMaxItems, shouldFilterNotAffectTotalCount: modalState.shouldFilterNotAffectTotalCount, minContentHeight: modalState.minContentHeight, events, reportBi: this.reportBi, }); makeObservable(this, { _setInitialSelection: action, isAdditionalStepShown: observable.ref, setIsAdditionalStepShown: action.bound, isAutoAdditionalStepPending: observable.ref, }); if (modalState.pickerContentRef.current == null) { this._setInitialData(); } this._setInitialSelection(); } _setInitialData() { const { modalState, picker } = this; const { collection } = picker; const { firstPageResult } = modalState.fetchFirstPage.status.value.data ?? {}; if (firstPageResult) { collection.setInitialPage(firstPageResult); } } _setInitialSelection() { const { initialSelect, picker } = this; const { collection } = picker; const { bulkSelect, result: { keyedItems, itemKey }, } = collection; if (initialSelect == null) { return; } if (typeof initialSelect === 'boolean') { const [firstItem] = keyedItems; if (firstItem) { bulkSelect.select.forceSet(firstItem); } return; } if (typeof initialSelect === 'function') { const initialSelectItems = keyedItems.filter(({ item }, index) => initialSelect(item, index), ); bulkSelect.select.syncValues( initialSelectItems, bulkSelect.select.finalStatus, ); return; } if (isPickerSelectionV2(initialSelect)) { const { defaultStatus, values } = initialSelect; if (defaultStatus) { bulkSelect.select.defaultStatus = typeof defaultStatus === 'object' ? defaultStatus : selectionStatusWixPatternsPreset[defaultStatus]; } if (values) { bulkSelect.select.forceSetMany( values.map(({ status, value }) => ({ value: { item: value, key: itemKey(value), }, status: typeof status === 'object' ? status : selectionStatusWixPatternsPreset[status], })), ); } return; } if (initialSelect.isSelectAll) { bulkSelect.selectAll(); if (initialSelect.uncheckedValues) { initialSelect.uncheckedValues.forEach((item) => { typeof item === 'string' ? bulkSelect.deselectOne(item) : bulkSelect.deselectOneItem({ key: itemKey(item), item, }); }); } return; } if (initialSelect.selectedValues) { initialSelect.selectedValues.forEach((item) => { typeof item === 'string' ? bulkSelect.selectOne(item) : bulkSelect.selectOneItem({ key: itemKey(item), item, }); }); } if (initialSelect.partiallySelectedIds) { const resolvedEntriesFromCollection = initialSelect.partiallySelectedIds.map((item) => { const collectionItem = typeof item === 'string' ? bulkSelect.getCollectionItem(item) : { key: itemKey(item), item, }; if (!collectionItem) { return null; } return { status: selectionStatusWixPatternsPreset.indeterminate, value: { key: collectionItem.key, item: collectionItem.item, }, }; }); bulkSelect.select.forceSetMany( resolvedEntriesFromCollection.filter( (e): e is SelectionEntry> => e != null, ), ); } } selectCreatedItems(items?: T[]) { const { picker, modalState: { multipleRef }, } = this; const { collection: { bulkSelect, itemKey }, } = picker; if (items?.length) { const [item] = items; const multiple = fromMultipleSelectionToBoolean(multipleRef.current); if (!multiple) { bulkSelect.resetToDefaults(); } bulkSelect.selectOne(itemKey(item)); } } onCreateNew = async (items?: T[]) => { await this.picker.onCreateNew(items); this.selectCreatedItems(items); }; onCreateNewButtonClick = ( onClick?: (param: { onClose: (arr?: T[]) => void }) => void, ): void => { const { modalState: { _isCustomContentVisible }, picker, } = this; onClick?.({ onClose: this.onCreateNew }); _isCustomContentVisible.value = true; this.reportBi( newItemCreationStart({ ...picker._commonDynamicBiParams(), numSelectedItems: picker.collection.bulkSelect.select.selectedCount, }), ); }; _resultUpdated = () => { const { initialSelect, picker } = this; const { collection } = picker; const { bulkSelect, result: { keyedItems }, } = collection; if (typeof initialSelect === 'function') { const initialSelectItems = keyedItems.filter(({ item }, index) => initialSelect(item, index), ); bulkSelect.select.syncValues( initialSelectItems, bulkSelect.select.finalStatus, ); return; } }; _reportFilterToggleBiOnNextResult( params: FilterEventOptions | undefined, filter: Filter, ) { const { actionType, clickedValueKey } = params ?? {}; const { picker: { collection }, } = this; const onFetch = () => { collection.emitter.off('fetch', onFetch); return action(() => { this.reportBi( withoutDefaults(cairoFilterToggled)({ ...this.picker._commonDynamicBiParams(), origin: 'Picker Modal', filterValue: clickedValueKey, filterName: filter.name, actionType, numFiltersActive: collection.query.activeFiltersCount, filteredListSize: collection.result.total, isCustomField: !!filter.isCustomField, }), ); }); }; this.picker.collection.emitter.on('fetch', onFetch); } init(initParams?: { autoAdditionalStepOnInitialSelect?: boolean }) { const { picker, modalState, initTask } = this; const { collection } = picker; if (initParams?.autoAdditionalStepOnInitialSelect) { this.autoAdditionalStepOnInitialSelect = true; } else { this.isAutoAdditionalStepPending = false; } const bi = new PickerContentStateBI({ state: this }); const disposers = [picker.init(), bi.init()]; this.picker.collection.query.filtersArray.forEach((filter) => { disposers.push( addEventListener( filter.events as TypedEmitter<{ change: (opt: FilterEventOptions | undefined) => unknown; }>, 'change', (params: FilterEventOptions | undefined) => { this._reportFilterToggleBiOnNextResult(params, filter); }, ), ); }); collection.emitter.on('resultUpdated', this._resultUpdated); modalState.pickerContentRef.current = { onCreateNew: this.onCreateNew, }; initTask.run(async () => { await picker.initTask.status.promise; if (!isPickerSelectionV2(this.initialSelect)) { this._setInitialSelection(); } if (this.autoAdditionalStepOnInitialSelect) { runInAction(() => { const selectedItem = this.picker.collection.bulkSelect.selectedValues?.[0]; if (selectedItem) { this.isAdditionalStepShown = true; } this.isAutoAdditionalStepPending = false; }); } }); return () => { const { current: pickerContent } = modalState.pickerContentRef; if (pickerContent) { pickerContent.onCreateNew = undefined; } disposers.forEach((d) => d()); picker.collection.emitter.off('resultUpdated', this._resultUpdated); }; } toggleIsSelectAll = action(() => { const { picker, events } = this; const { collection: { bulkSelect, query: { hasActiveFilters }, result, }, } = picker; const { allSelected, select: { isEmpty }, } = bulkSelect; const afters = events.listeners('beforeToggleIsSelectAll').map((l) => l()); if (allSelected || !isEmpty) { bulkSelect.toggleIsSelectAll(); } else if (hasActiveFilters) { for (const keyedItem of result.keyedItems) { bulkSelect.selectOneItem(keyedItem); } } else { bulkSelect.toggleIsSelectAll(); } afters.forEach(({ afterToggleIsSelectAll }) => afterToggleIsSelectAll()); }); setIsAdditionalStepShown(value: boolean) { this.isAdditionalStepShown = value; } }