diff --git a/packages/leyden-react/src/hooks/useAreCoordinatesSelected.ts b/packages/leyden-react/src/hooks/useAreCoordinatesSelected.ts new file mode 100644 index 0000000..445bd07 --- /dev/null +++ b/packages/leyden-react/src/hooks/useAreCoordinatesSelected.ts @@ -0,0 +1,29 @@ +import { Coordinates, LeydenEditor } from 'leyden'; +import { useEffect, useState } from 'react'; + +import { useLeydenStatic } from './useLeydenStatic'; + +export const useAreCoordinatesSelected = (coords: Coordinates|null): boolean => { + const editor = useLeydenStatic(); + const currentSelectedCoords = LeydenEditor.selectedCoords(editor); + const [coordinatesSelected, setCoordinatesSelected] = useState( + (coords && currentSelectedCoords) ? + Coordinates.equals(currentSelectedCoords, coords) : + false + ); + + useEffect(() => { + const unsubscribe = LeydenEditor.subscribeToSelectedCoordinatesByCoordinates( + editor, + setCoordinatesSelected, + { + at: coords + }, + ); + return () => { + unsubscribe(); + }; + }, []); + + return coordinatesSelected; +}; diff --git a/packages/leyden-react/src/hooks/useCellIsSelected.ts b/packages/leyden-react/src/hooks/useCellIsSelected.ts index 304f8e2..6dff9d4 100644 --- a/packages/leyden-react/src/hooks/useCellIsSelected.ts +++ b/packages/leyden-react/src/hooks/useCellIsSelected.ts @@ -1,21 +1,10 @@ -import { Coordinates } from 'leyden'; -import { useMemo } from 'react'; import { Descendant } from 'slate'; import { useCoordinates } from './useCoordinates'; -import { useSelectedCoordinates } from './useSelectedCoordinates'; +import { useAreCoordinatesSelected } from './useAreCoordinatesSelected'; export const useCellIsSelected = (node: Descendant): boolean => { const ownCoords = useCoordinates(node); - const selectedCoords = useSelectedCoordinates(); - const cellSelected = useMemo(() => { - return ( - ownCoords !== null - && selectedCoords !== null - && Coordinates.equals(ownCoords, selectedCoords) - ); - }, [ownCoords, selectedCoords]); - - return cellSelected; + return useAreCoordinatesSelected(ownCoords); }; diff --git a/packages/leyden-react/src/hooks/useCoordinates.ts b/packages/leyden-react/src/hooks/useCoordinates.ts index 682dab6..7c673c4 100644 --- a/packages/leyden-react/src/hooks/useCoordinates.ts +++ b/packages/leyden-react/src/hooks/useCoordinates.ts @@ -23,15 +23,13 @@ export const useCoordinates = (node: Descendant): Coordinates|null => { return; } // If not wrapped in `setTimeout`, this runs before the cell paths are updated - // and coordinate movement is never detected. - setTimeout(() => { - const newCoords = ReactEditor.cellCoords(editor, node); - if ((newCoords === null || !Coordinates.equals(coordinates, newCoords)) - && !canceled - ) { - setCoordinates(newCoords); - } - }); + // and coordinate movement is never detected. + const newCoords = ReactEditor.cellCoords(editor, node); + if ((newCoords === null || !Coordinates.equals(coordinates, newCoords)) + && !canceled + ) { + setCoordinates(newCoords); + } }); return () => { canceled = true; diff --git a/packages/leyden/src/interfaces/LeydenEditor.ts b/packages/leyden/src/interfaces/LeydenEditor.ts index e0c56b8..bc92c28 100644 --- a/packages/leyden/src/interfaces/LeydenEditor.ts +++ b/packages/leyden/src/interfaces/LeydenEditor.ts @@ -11,10 +11,7 @@ import { Coordinates } from './Coordinates'; import { Table } from './Table'; import { ValidationFunc, Validator } from './Validator'; import { - CellSubscriber, - OperationSubscriber, - SelectedCoordinatesSubscriber, - Unsubscriber, + CellSubscriber, CoordinateSelectedSubscriber, OperationSubscriber, SelectedCoordinatesSubscriber, Unsubscriber, } from '../utils/types'; import { OPERATION_SUBSCRIBERS } from '../utils/weakMaps'; @@ -79,6 +76,13 @@ export interface LeydenEditorInterface { editor: Editor, subscriber: SelectedCoordinatesSubscriber ) => Unsubscriber; + subscribeToSelectedCoordinatesByCoordinates: ( + editor: Editor, + subscriber: CoordinateSelectedSubscriber, + options: { + at: Coordinates | null, + } + ) => Unsubscriber; table: (editor: Editor) => Table; tablePath: () => Path; } @@ -148,7 +152,7 @@ export const LeydenEditor: LeydenEditorInterface = { }, /** - * Get the path to the nth cell in an editor. + * Get the path to the nth cell in an editor. */ nthCellPath(n: number): CellPath { @@ -333,6 +337,34 @@ export const LeydenEditor: LeydenEditorInterface = { }); }, + /** + * Subscribe to the selected status of a cell at the specified coordinates. + */ + + subscribeToSelectedCoordinatesByCoordinates( + editor: Editor, + subscriber: CoordinateSelectedSubscriber, + options: { + at: Coordinates | null, + } + ): Unsubscriber { + const { at } = options; + if (!at) { + return () => subscriber(false); + } + const cellPath = LeydenEditor.cellPath(editor, { at }); + return LeydenEditor.subscribeToOperations(editor, op => { + if (Operation.isSelectionOperation(op) && + op.newProperties?.focus?.path && + Path.equals(op.newProperties?.focus?.path.slice(0, 2), cellPath) + ) { + subscriber(true); + } else if (Operation.isSelectionOperation(op)) { + subscriber(false); + } + }); + }, + /** * Get an editor's table. */ diff --git a/packages/leyden/src/utils/types.ts b/packages/leyden/src/utils/types.ts index 4695b3c..40aa483 100644 --- a/packages/leyden/src/utils/types.ts +++ b/packages/leyden/src/utils/types.ts @@ -39,13 +39,19 @@ export type CellSubscriber = (cell: Cell) => void; export type SelectedCoordinatesSubscriber = (coords: Coordinates|null) => void; /** - * A function which will end a subscription + * A function fired when the current cell's coordinates become selected. + */ + +export type CoordinateSelectedSubscriber = (selected: boolean) => void; + +/** + * A function which will end a subscription */ export type Unsubscriber = () => void; /** - * An option representing a leyden editor passed during editor initialization + * An option representing a leyden editor passed during editor initialization */ export interface EditorOption { @@ -53,7 +59,7 @@ export interface EditorOption { } /** - * An option representing a validator set passed during editor initialization + * An option representing a validator set passed during editor initialization */ export interface ValidatorsOption { diff --git a/packages/leyden/src/withLeyden.ts b/packages/leyden/src/withLeyden.ts index edba23d..14ab485 100644 --- a/packages/leyden/src/withLeyden.ts +++ b/packages/leyden/src/withLeyden.ts @@ -35,13 +35,16 @@ export const withLeyden = ({ editor, ...rest }: WithLeydenOpti } } apply(op); + // Notify subscribers of operations after application - const opSubscribers = OPERATION_SUBSCRIBERS.get(e); - if (opSubscribers !== undefined) { - for (const opSubscriber of opSubscribers) { - opSubscriber(op); + setTimeout(() => { + const opSubscribers = OPERATION_SUBSCRIBERS.get(e); + if (opSubscribers !== undefined) { + for (const opSubscriber of opSubscribers) { + opSubscriber(op); + } } - } + }); }; e.getValidationFunc = validator => (