123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150 |
- import {
- useCallback,
- useRef, useState,
- } from 'react'
- import { debounce } from 'lodash-es'
- import {
- useStoreApi,
- } from 'reactflow'
- import { useTranslation } from 'react-i18next'
- import { useWorkflowHistoryStore } from '../workflow-history-store'
- /**
- * All supported Events that create a new history state.
- * Current limitations:
- * - InputChange events in Node Panels do not trigger state changes.
- * - Resizing UI elements does not trigger state changes.
- */
- export enum WorkflowHistoryEvent {
- NodeTitleChange = 'NodeTitleChange',
- NodeDescriptionChange = 'NodeDescriptionChange',
- NodeDragStop = 'NodeDragStop',
- NodeChange = 'NodeChange',
- NodeConnect = 'NodeConnect',
- NodePaste = 'NodePaste',
- NodeDelete = 'NodeDelete',
- EdgeDelete = 'EdgeDelete',
- EdgeDeleteByDeleteBranch = 'EdgeDeleteByDeleteBranch',
- NodeAdd = 'NodeAdd',
- NodeResize = 'NodeResize',
- NoteAdd = 'NoteAdd',
- NoteChange = 'NoteChange',
- NoteDelete = 'NoteDelete',
- LayoutOrganize = 'LayoutOrganize',
- }
- export const useWorkflowHistory = () => {
- const store = useStoreApi()
- const { store: workflowHistoryStore } = useWorkflowHistoryStore()
- const { t } = useTranslation()
- const [undoCallbacks, setUndoCallbacks] = useState<any[]>([])
- const [redoCallbacks, setRedoCallbacks] = useState<any[]>([])
- const onUndo = useCallback((callback: unknown) => {
- setUndoCallbacks((prev: any) => [...prev, callback])
- return () => setUndoCallbacks(prev => prev.filter(cb => cb !== callback))
- }, [])
- const onRedo = useCallback((callback: unknown) => {
- setRedoCallbacks((prev: any) => [...prev, callback])
- return () => setRedoCallbacks(prev => prev.filter(cb => cb !== callback))
- }, [])
- const undo = useCallback(() => {
- workflowHistoryStore.temporal.getState().undo()
- undoCallbacks.forEach(callback => callback())
- }, [undoCallbacks, workflowHistoryStore.temporal])
- const redo = useCallback(() => {
- workflowHistoryStore.temporal.getState().redo()
- redoCallbacks.forEach(callback => callback())
- }, [redoCallbacks, workflowHistoryStore.temporal])
- // Some events may be triggered multiple times in a short period of time.
- // We debounce the history state update to avoid creating multiple history states
- // with minimal changes.
- const saveStateToHistoryRef = useRef(debounce((event: WorkflowHistoryEvent) => {
- workflowHistoryStore.setState({
- workflowHistoryEvent: event,
- nodes: store.getState().getNodes(),
- edges: store.getState().edges,
- })
- }, 500))
- const saveStateToHistory = useCallback((event: WorkflowHistoryEvent) => {
- switch (event) {
- case WorkflowHistoryEvent.NoteChange:
- // Hint: Note change does not trigger when note text changes,
- // because the note editors have their own history states.
- saveStateToHistoryRef.current(event)
- break
- case WorkflowHistoryEvent.NodeTitleChange:
- case WorkflowHistoryEvent.NodeDescriptionChange:
- case WorkflowHistoryEvent.NodeDragStop:
- case WorkflowHistoryEvent.NodeChange:
- case WorkflowHistoryEvent.NodeConnect:
- case WorkflowHistoryEvent.NodePaste:
- case WorkflowHistoryEvent.NodeDelete:
- case WorkflowHistoryEvent.EdgeDelete:
- case WorkflowHistoryEvent.EdgeDeleteByDeleteBranch:
- case WorkflowHistoryEvent.NodeAdd:
- case WorkflowHistoryEvent.NodeResize:
- case WorkflowHistoryEvent.NoteAdd:
- case WorkflowHistoryEvent.LayoutOrganize:
- case WorkflowHistoryEvent.NoteDelete:
- saveStateToHistoryRef.current(event)
- break
- default:
- // We do not create a history state for every event.
- // Some events of reactflow may change things the user would not want to undo/redo.
- // For example: UI state changes like selecting a node.
- break
- }
- }, [])
- const getHistoryLabel = useCallback((event: WorkflowHistoryEvent) => {
- switch (event) {
- case WorkflowHistoryEvent.NodeTitleChange:
- return t('workflow.changeHistory.nodeTitleChange')
- case WorkflowHistoryEvent.NodeDescriptionChange:
- return t('workflow.changeHistory.nodeDescriptionChange')
- case WorkflowHistoryEvent.LayoutOrganize:
- case WorkflowHistoryEvent.NodeDragStop:
- return t('workflow.changeHistory.nodeDragStop')
- case WorkflowHistoryEvent.NodeChange:
- return t('workflow.changeHistory.nodeChange')
- case WorkflowHistoryEvent.NodeConnect:
- return t('workflow.changeHistory.nodeConnect')
- case WorkflowHistoryEvent.NodePaste:
- return t('workflow.changeHistory.nodePaste')
- case WorkflowHistoryEvent.NodeDelete:
- return t('workflow.changeHistory.nodeDelete')
- case WorkflowHistoryEvent.NodeAdd:
- return t('workflow.changeHistory.nodeAdd')
- case WorkflowHistoryEvent.EdgeDelete:
- case WorkflowHistoryEvent.EdgeDeleteByDeleteBranch:
- return t('workflow.changeHistory.edgeDelete')
- case WorkflowHistoryEvent.NodeResize:
- return t('workflow.changeHistory.nodeResize')
- case WorkflowHistoryEvent.NoteAdd:
- return t('workflow.changeHistory.noteAdd')
- case WorkflowHistoryEvent.NoteChange:
- return t('workflow.changeHistory.noteChange')
- case WorkflowHistoryEvent.NoteDelete:
- return t('workflow.changeHistory.noteDelete')
- default:
- return 'Unknown Event'
- }
- }, [t])
- return {
- store: workflowHistoryStore,
- saveStateToHistory,
- getHistoryLabel,
- undo,
- redo,
- onUndo,
- onRedo,
- }
- }
|