import { addEventListener, WixPatternsContainer, CollectionState, FiltersMap, } from '@wix/bex-core'; import { action, makeObservable } from 'mobx'; import { DragAndDropParentState, DragAndDropState } from './DragAndDropState'; import { CollectionDragAndDropBI } from './CollectionDragAndDropBI'; import { createOptimisticActionsOrderByModeReaction } from '../../state/DragAndDrop/createOptimisticActionsOrderByModeReaction'; import { DragAndDropCancel } from './DragAndDrop'; export type CollectionDragAndDropParentState< T, F extends FiltersMap, > = DragAndDropParentState & { collection: CollectionState; getCollectionSnapshot?: () => { [key: string]: string | number | undefined; }; }; interface CollectionDragAndDropStateParams { container: WixPatternsContainer; state: CollectionDragAndDropParentState; a11yContainer: { current: HTMLElement | null | undefined; }; } export class CollectionDragAndDropState { dnd: DragAndDropState; state; bi; dragAndDropCancel?: DragAndDropCancel; constructor(params: CollectionDragAndDropStateParams) { const { state } = params; const { collection } = state; this.state = state; this.bi = new CollectionDragAndDropBI({ state, collection, getCollectionSnapshot: state.getCollectionSnapshot, getPendingMovePatchesCount: () => collection._optimisticActions?.pendingMovePatches.length, getBulkSelectCount: () => collection.bulkSelect.selectedCount, }); this.dnd = new DragAndDropState({ state: this.state, bi: this.bi, collection: params.state.collection, container: params.container, move: () => this._collectionMove(), announcements: { dragHandleLabel: (id) => { const { translate: t } = params.container; const { itemName } = this.collection; const item = this.collection.getKeyedItem(id as string)?.item; return t('cairo.dragAndDrop.button-role-label.sr', { itemName: item ? itemName(item) : '', }); }, itemDescription: (id) => { const { itemName } = this.collection; const item = this.collection.getKeyedItem(id as string)?.item; return item ? itemName(item) : ''; }, }, }); this.dnd.onCancel = this._onCancel; makeObservable(this, { _collectionMove: action, }); } get collection() { return this.state.collection; } get optimisticActions() { return this.collection._optimisticActions; } init() { const { optimisticActions, dnd, bi, collection } = this; const { container } = dnd; const { translate: t } = container; const disposers = [ dnd.init(), createOptimisticActionsOrderByModeReaction(dnd, collection), addEventListener(container.events, 'navigation', () => { if (optimisticActions?.hasRunningSequence) { const patches = optimisticActions.pendingMovePatches; return { title: t('cairo.dragAndDrop.leaveSite.modal.title'), content: t('cairo.dragAndDrop.leaveSite.modal.description'), onResume: action(() => { bi.onMoveFailure({ reason: 'Left page', patches, }); }), }; } return null; }), ...(optimisticActions ? [ addEventListener( optimisticActions.events, 'dragAndDropSequenceError', ({ patches }) => { bi.onMoveFailure({ reason: 'Update failed', patches, }); }, ), addEventListener( optimisticActions.events, 'dragAndDropSequenceRetry', ({ patches }) => { bi.onMoveRetry({ patches }); }, ), ] : []), ]; const onWindowClosedDuringSequence = () => { if (optimisticActions?.hasRunningSequence) { bi.onMoveFailure({ reason: 'Window closed', patches: optimisticActions.pendingMovePatches, }); } }; window.addEventListener('unload', onWindowClosedDuringSequence); return action(() => { disposers.forEach((d) => d()); window.removeEventListener('unload', onWindowClosedDuringSequence); }); } _onCancel = async () => { const { dnd } = this; const dragEndEvent = dnd._createDragEndEvent(); if (dragEndEvent == null) { return false; } const shouldCancel = await this.dragAndDropCancel?.( dnd._createSimpleDragEndEvent(dragEndEvent), ); if (!shouldCancel) { return false; } dnd._maybeShowErrorToast(shouldCancel, dragEndEvent); return true; }; _collectionMove() { const { onSubmit, reorderModeState } = this.dnd; const { optimisticActions } = this; if (optimisticActions == null) { return; } const dragEndEvent = this.dnd._createDragEndEvent(); if (dragEndEvent == null) { return; } optimisticActions.move(dragEndEvent, { onTryAgain: async () => { await reorderModeState?.onBulkSubmit(); }, submit: reorderModeState ? () => reorderModeState?.onSubmit : onSubmit ? (event) => onSubmit(this.dnd._createSimpleDragEndEvent(event)) : undefined, collectionSnapshot: this.state.getCollectionSnapshot?.() ?? {}, }); } }