選択できるのは25トピックまでです。 トピックは、先頭が英数字で、英数字とダッシュ('-')を使用した35文字以内のものにしてください。

use-workflow-history.ts 5.6KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152
  1. import {
  2. useCallback,
  3. useRef, useState,
  4. } from 'react'
  5. import { debounce } from 'lodash-es'
  6. import {
  7. useStoreApi,
  8. } from 'reactflow'
  9. import { useTranslation } from 'react-i18next'
  10. import { useWorkflowHistoryStore } from '../workflow-history-store'
  11. import type { WorkflowHistoryEventMeta } from '../workflow-history-store'
  12. /**
  13. * All supported Events that create a new history state.
  14. * Current limitations:
  15. * - InputChange events in Node Panels do not trigger state changes.
  16. * - Resizing UI elements does not trigger state changes.
  17. */
  18. export enum WorkflowHistoryEvent {
  19. NodeTitleChange = 'NodeTitleChange',
  20. NodeDescriptionChange = 'NodeDescriptionChange',
  21. NodeDragStop = 'NodeDragStop',
  22. NodeChange = 'NodeChange',
  23. NodeConnect = 'NodeConnect',
  24. NodePaste = 'NodePaste',
  25. NodeDelete = 'NodeDelete',
  26. EdgeDelete = 'EdgeDelete',
  27. EdgeDeleteByDeleteBranch = 'EdgeDeleteByDeleteBranch',
  28. NodeAdd = 'NodeAdd',
  29. NodeResize = 'NodeResize',
  30. NoteAdd = 'NoteAdd',
  31. NoteChange = 'NoteChange',
  32. NoteDelete = 'NoteDelete',
  33. LayoutOrganize = 'LayoutOrganize',
  34. }
  35. export const useWorkflowHistory = () => {
  36. const store = useStoreApi()
  37. const { store: workflowHistoryStore } = useWorkflowHistoryStore()
  38. const { t } = useTranslation()
  39. const [undoCallbacks, setUndoCallbacks] = useState<any[]>([])
  40. const [redoCallbacks, setRedoCallbacks] = useState<any[]>([])
  41. const onUndo = useCallback((callback: unknown) => {
  42. setUndoCallbacks((prev: any) => [...prev, callback])
  43. return () => setUndoCallbacks(prev => prev.filter(cb => cb !== callback))
  44. }, [])
  45. const onRedo = useCallback((callback: unknown) => {
  46. setRedoCallbacks((prev: any) => [...prev, callback])
  47. return () => setRedoCallbacks(prev => prev.filter(cb => cb !== callback))
  48. }, [])
  49. const undo = useCallback(() => {
  50. workflowHistoryStore.temporal.getState().undo()
  51. undoCallbacks.forEach(callback => callback())
  52. }, [undoCallbacks, workflowHistoryStore.temporal])
  53. const redo = useCallback(() => {
  54. workflowHistoryStore.temporal.getState().redo()
  55. redoCallbacks.forEach(callback => callback())
  56. }, [redoCallbacks, workflowHistoryStore.temporal])
  57. // Some events may be triggered multiple times in a short period of time.
  58. // We debounce the history state update to avoid creating multiple history states
  59. // with minimal changes.
  60. const saveStateToHistoryRef = useRef(debounce((event: WorkflowHistoryEvent, meta?: WorkflowHistoryEventMeta) => {
  61. workflowHistoryStore.setState({
  62. workflowHistoryEvent: event,
  63. workflowHistoryEventMeta: meta,
  64. nodes: store.getState().getNodes(),
  65. edges: store.getState().edges,
  66. })
  67. }, 500))
  68. const saveStateToHistory = useCallback((event: WorkflowHistoryEvent, meta?: WorkflowHistoryEventMeta) => {
  69. switch (event) {
  70. case WorkflowHistoryEvent.NoteChange:
  71. // Hint: Note change does not trigger when note text changes,
  72. // because the note editors have their own history states.
  73. saveStateToHistoryRef.current(event, meta)
  74. break
  75. case WorkflowHistoryEvent.NodeTitleChange:
  76. case WorkflowHistoryEvent.NodeDescriptionChange:
  77. case WorkflowHistoryEvent.NodeDragStop:
  78. case WorkflowHistoryEvent.NodeChange:
  79. case WorkflowHistoryEvent.NodeConnect:
  80. case WorkflowHistoryEvent.NodePaste:
  81. case WorkflowHistoryEvent.NodeDelete:
  82. case WorkflowHistoryEvent.EdgeDelete:
  83. case WorkflowHistoryEvent.EdgeDeleteByDeleteBranch:
  84. case WorkflowHistoryEvent.NodeAdd:
  85. case WorkflowHistoryEvent.NodeResize:
  86. case WorkflowHistoryEvent.NoteAdd:
  87. case WorkflowHistoryEvent.LayoutOrganize:
  88. case WorkflowHistoryEvent.NoteDelete:
  89. saveStateToHistoryRef.current(event, meta)
  90. break
  91. default:
  92. // We do not create a history state for every event.
  93. // Some events of reactflow may change things the user would not want to undo/redo.
  94. // For example: UI state changes like selecting a node.
  95. break
  96. }
  97. }, [])
  98. const getHistoryLabel = useCallback((event: WorkflowHistoryEvent) => {
  99. switch (event) {
  100. case WorkflowHistoryEvent.NodeTitleChange:
  101. return t('workflow.changeHistory.nodeTitleChange')
  102. case WorkflowHistoryEvent.NodeDescriptionChange:
  103. return t('workflow.changeHistory.nodeDescriptionChange')
  104. case WorkflowHistoryEvent.LayoutOrganize:
  105. case WorkflowHistoryEvent.NodeDragStop:
  106. return t('workflow.changeHistory.nodeDragStop')
  107. case WorkflowHistoryEvent.NodeChange:
  108. return t('workflow.changeHistory.nodeChange')
  109. case WorkflowHistoryEvent.NodeConnect:
  110. return t('workflow.changeHistory.nodeConnect')
  111. case WorkflowHistoryEvent.NodePaste:
  112. return t('workflow.changeHistory.nodePaste')
  113. case WorkflowHistoryEvent.NodeDelete:
  114. return t('workflow.changeHistory.nodeDelete')
  115. case WorkflowHistoryEvent.NodeAdd:
  116. return t('workflow.changeHistory.nodeAdd')
  117. case WorkflowHistoryEvent.EdgeDelete:
  118. case WorkflowHistoryEvent.EdgeDeleteByDeleteBranch:
  119. return t('workflow.changeHistory.edgeDelete')
  120. case WorkflowHistoryEvent.NodeResize:
  121. return t('workflow.changeHistory.nodeResize')
  122. case WorkflowHistoryEvent.NoteAdd:
  123. return t('workflow.changeHistory.noteAdd')
  124. case WorkflowHistoryEvent.NoteChange:
  125. return t('workflow.changeHistory.noteChange')
  126. case WorkflowHistoryEvent.NoteDelete:
  127. return t('workflow.changeHistory.noteDelete')
  128. default:
  129. return 'Unknown Event'
  130. }
  131. }, [t])
  132. return {
  133. store: workflowHistoryStore,
  134. saveStateToHistory,
  135. getHistoryLabel,
  136. undo,
  137. redo,
  138. onUndo,
  139. onRedo,
  140. }
  141. }