import { useContext, useRef, useMemo, useCallback } from 'react'; import { action, computed } from 'mobx'; import { observer } from 'mobx-react'; import { EventListener } from '../types'; import ElementContext from '../utils/ElementContext'; export const SELECTION_EVENT = 'selection'; export const SELECTION_STATE = 'selectedIds'; export type SelectionEventListener = EventListener<[string[]]>; interface SelectionHandlerState { [SELECTION_STATE]?: string[]; } export type OnSelect = (e: React.MouseEvent) => void; interface Options { multiSelect?: boolean; controlled?: boolean; raiseOnSelect?: boolean; } export const useSelection = ({ multiSelect, controlled, raiseOnSelect = true }: Options = {}): [boolean, OnSelect] => { const element = useContext(ElementContext); const elementRef = useRef(element); elementRef.current = element; const selected = useMemo( () => computed(() => { const { selectedIds } = element.getController().getState(); return !!selectedIds && selectedIds.includes(element.getId()); }), [element] ); const onSelect = useCallback( (e: React.MouseEvent): void => { const actionFn = action((e: React.MouseEvent): void => { e.stopPropagation(); const id = elementRef.current.getId(); const state = elementRef.current.getController().getState(); const idx = state.selectedIds ? state.selectedIds.indexOf(id) : -1; let selectedIds: string[]; let raise = false; if (multiSelect && (e.ctrlKey || e.metaKey)) { if (!state.selectedIds) { raise = true; selectedIds = [id]; } else { selectedIds = [...state.selectedIds]; if (idx === -1) { raise = true; selectedIds.push(id); } else { selectedIds.splice(idx, 1); } } } else if (idx === -1 || multiSelect) { raise = true; selectedIds = [id]; } else { selectedIds = []; } if (!controlled) { state.selectedIds = selectedIds; } elementRef.current.getController().fireEvent(SELECTION_EVENT, selectedIds); if (raiseOnSelect && raise) { elementRef.current.raise(); } }); actionFn(e); }, [multiSelect, controlled, raiseOnSelect] ); return [selected.get(), onSelect]; }; export interface WithSelectionProps { selected?: boolean; onSelect?: OnSelect; } export const withSelection = (options?: Options) =>

(WrappedComponent: React.ComponentType

) => { const Component: React.FunctionComponent> = (props) => { const [selected, onSelect] = useSelection(options); return ; }; Component.displayName = `withSelection(${WrappedComponent.displayName || WrappedComponent.name})`; return observer(Component); };