Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 | 220x 220x 220x 2098x 389x 389x 389x 220x 220x 4133x 4133x 4133x 220x 8892x 3917x 1320x 440x 220x 72x 24x 12x 778x | import { useEffect, useState } from 'react';
import { eventTarget, utilities as csUtilities } from '@cornerstonejs/core';
import { Enums as csToolsEnums } from '@cornerstonejs/tools';
const { DefaultHistoryMemo } = csUtilities.HistoryMemo;
// Cornerstone events after which the undo/redo history may have changed.
// `undo`/`redo` emit the HISTORY_* events, but any tool action that records a
// memo (drawing an annotation, editing a labelmap, deleting an annotation)
// pushes onto the history WITHOUT emitting a history event, so we also listen
// for the tool events that produce those memos in order to keep the enabled
// state of the buttons in sync.
const HISTORY_CHANGING_EVENTS = [
csToolsEnums.Events.HISTORY_UNDO,
csToolsEnums.Events.HISTORY_REDO,
csToolsEnums.Events.ANNOTATION_COMPLETED,
csToolsEnums.Events.ANNOTATION_MODIFIED,
csToolsEnums.Events.ANNOTATION_REMOVED,
csToolsEnums.Events.SEGMENTATION_DATA_MODIFIED,
];
// A drag of an existing annotation fires ANNOTATION_MODIFIED only while the
// pointer moves; the tool pushes the memo on mouse up and fires no event for
// it. So also re-read the state after each mouse up / touch end.
const POINTER_END_EVENTS = ['mouseup', 'touchend'];
/**
* Tracks whether an undo/redo is currently available so the header buttons can
* be disabled (greyed out) when there is nothing to undo/redo.
*/
export function useUndoRedoState(): { canUndo: boolean; canRedo: boolean } {
const [state, setState] = useState(() => ({
canUndo: DefaultHistoryMemo.canUndo,
canRedo: DefaultHistoryMemo.canRedo,
}));
useEffect(() => {
// A memo is often pushed synchronously *after* the triggering event is
// dispatched, so defer the read until the current call stack unwinds to
// make sure we observe the up-to-date availability.
let scheduled: ReturnType<typeof setTimeout> | null = null;
const readState = () => {
scheduled = null;
setState(prev => {
const { canUndo, canRedo } = DefaultHistoryMemo;
return prev.canUndo === canUndo && prev.canRedo === canRedo ? prev : { canUndo, canRedo };
});
};
const schedule = () => {
if (scheduled === null) {
scheduled = setTimeout(readState, 0);
}
};
HISTORY_CHANGING_EVENTS.forEach(evt => eventTarget.addEventListener(evt, schedule));
POINTER_END_EVENTS.forEach(evt => window.addEventListener(evt, schedule, true));
// Sync once on mount in case the history already has content.
readState();
return () => {
HISTORY_CHANGING_EVENTS.forEach(evt => eventTarget.removeEventListener(evt, schedule));
POINTER_END_EVENTS.forEach(evt => window.removeEventListener(evt, schedule, true));
Iif (scheduled !== null) {
clearTimeout(scheduled);
}
};
}, []);
return state;
}
export default useUndoRedoState;
|