@voidhash/mimic-react 0.0.2 → 0.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build.log +60 -26
- package/dist/_virtual/_@oxc-project_runtime@0.103.0/helpers/defineProperty.cjs +14 -0
- package/dist/_virtual/_@oxc-project_runtime@0.103.0/helpers/defineProperty.mjs +14 -0
- package/dist/_virtual/_@oxc-project_runtime@0.103.0/helpers/objectSpread2.cjs +27 -0
- package/dist/_virtual/_@oxc-project_runtime@0.103.0/helpers/objectSpread2.mjs +27 -0
- package/dist/_virtual/_@oxc-project_runtime@0.103.0/helpers/toPrimitive.cjs +16 -0
- package/dist/_virtual/_@oxc-project_runtime@0.103.0/helpers/toPrimitive.mjs +16 -0
- package/dist/_virtual/_@oxc-project_runtime@0.103.0/helpers/toPropertyKey.cjs +11 -0
- package/dist/_virtual/_@oxc-project_runtime@0.103.0/helpers/toPropertyKey.mjs +11 -0
- package/dist/_virtual/_@oxc-project_runtime@0.103.0/helpers/typeof.cjs +18 -0
- package/dist/_virtual/_@oxc-project_runtime@0.103.0/helpers/typeof.mjs +12 -0
- package/dist/zustand/index.cjs +2 -94
- package/dist/zustand/index.d.cts +3 -115
- package/dist/zustand/index.d.mts +3 -115
- package/dist/zustand/index.mjs +2 -95
- package/dist/zustand/middleware.cjs +95 -0
- package/dist/zustand/middleware.d.cts +47 -0
- package/dist/zustand/middleware.d.cts.map +1 -0
- package/dist/zustand/middleware.d.mts +47 -0
- package/dist/zustand/middleware.d.mts.map +1 -0
- package/dist/zustand/middleware.mjs +96 -0
- package/dist/zustand/middleware.mjs.map +1 -0
- package/dist/zustand/types.d.cts +75 -0
- package/dist/zustand/types.d.cts.map +1 -0
- package/dist/zustand/types.d.mts +75 -0
- package/dist/zustand/types.d.mts.map +1 -0
- package/dist/zustand-commander/commander.cjs +187 -0
- package/dist/zustand-commander/commander.d.cts +52 -0
- package/dist/zustand-commander/commander.d.cts.map +1 -0
- package/dist/zustand-commander/commander.d.mts +52 -0
- package/dist/zustand-commander/commander.d.mts.map +1 -0
- package/dist/zustand-commander/commander.mjs +185 -0
- package/dist/zustand-commander/commander.mjs.map +1 -0
- package/dist/zustand-commander/hooks.cjs +145 -0
- package/dist/zustand-commander/hooks.d.cts +78 -0
- package/dist/zustand-commander/hooks.d.cts.map +1 -0
- package/dist/zustand-commander/hooks.d.mts +78 -0
- package/dist/zustand-commander/hooks.d.mts.map +1 -0
- package/dist/zustand-commander/hooks.mjs +144 -0
- package/dist/zustand-commander/hooks.mjs.map +1 -0
- package/dist/zustand-commander/index.cjs +15 -354
- package/dist/zustand-commander/index.d.cts +4 -313
- package/dist/zustand-commander/index.d.mts +4 -313
- package/dist/zustand-commander/index.mjs +4 -344
- package/dist/zustand-commander/types.cjs +28 -0
- package/dist/zustand-commander/types.d.cts +195 -0
- package/dist/zustand-commander/types.d.cts.map +1 -0
- package/dist/zustand-commander/types.d.mts +195 -0
- package/dist/zustand-commander/types.d.mts.map +1 -0
- package/dist/zustand-commander/types.mjs +25 -0
- package/dist/zustand-commander/types.mjs.map +1 -0
- package/package.json +3 -3
- package/tsdown.config.ts +1 -1
- package/dist/objectSpread2-CIP_6jda.cjs +0 -73
- package/dist/objectSpread2-CxTyNSYl.mjs +0 -67
- package/dist/zustand/index.d.cts.map +0 -1
- package/dist/zustand/index.d.mts.map +0 -1
- package/dist/zustand/index.mjs.map +0 -1
- package/dist/zustand-commander/index.d.cts.map +0 -1
- package/dist/zustand-commander/index.d.mts.map +0 -1
- package/dist/zustand-commander/index.mjs.map +0 -1
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { CommandDispatch, CommanderSlice } from "./types.cjs";
|
|
2
|
+
import { StoreApi, UseBoundStore } from "zustand";
|
|
3
|
+
|
|
4
|
+
//#region src/zustand-commander/hooks.d.ts
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* React hook to get a dispatch function for commands.
|
|
8
|
+
* The dispatch function executes commands and manages undo/redo state.
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* ```tsx
|
|
12
|
+
* const dispatch = useCommander(useStore);
|
|
13
|
+
*
|
|
14
|
+
* const handleClick = () => {
|
|
15
|
+
* dispatch(addCard)({ columnId: "col-1", title: "New Card" });
|
|
16
|
+
* };
|
|
17
|
+
* ```
|
|
18
|
+
*/
|
|
19
|
+
declare function useCommander<TStore extends CommanderSlice>(store: UseBoundStore<StoreApi<TStore>>): CommandDispatch<TStore>;
|
|
20
|
+
/**
|
|
21
|
+
* State and actions for undo/redo functionality.
|
|
22
|
+
*/
|
|
23
|
+
interface UndoRedoState {
|
|
24
|
+
/** Whether there are actions that can be undone */
|
|
25
|
+
readonly canUndo: boolean;
|
|
26
|
+
/** Whether there are actions that can be redone */
|
|
27
|
+
readonly canRedo: boolean;
|
|
28
|
+
/** Number of items in the undo stack */
|
|
29
|
+
readonly undoCount: number;
|
|
30
|
+
/** Number of items in the redo stack */
|
|
31
|
+
readonly redoCount: number;
|
|
32
|
+
/** Undo the last action */
|
|
33
|
+
readonly undo: () => boolean;
|
|
34
|
+
/** Redo the last undone action */
|
|
35
|
+
readonly redo: () => boolean;
|
|
36
|
+
/** Clear the undo/redo history */
|
|
37
|
+
readonly clear: () => void;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* React hook for undo/redo functionality.
|
|
41
|
+
* Provides state (canUndo, canRedo) and actions (undo, redo, clear).
|
|
42
|
+
*
|
|
43
|
+
* @example
|
|
44
|
+
* ```tsx
|
|
45
|
+
* const { canUndo, canRedo, undo, redo } = useUndoRedo(useStore);
|
|
46
|
+
*
|
|
47
|
+
* return (
|
|
48
|
+
* <>
|
|
49
|
+
* <button onClick={undo} disabled={!canUndo}>Undo</button>
|
|
50
|
+
* <button onClick={redo} disabled={!canRedo}>Redo</button>
|
|
51
|
+
* </>
|
|
52
|
+
* );
|
|
53
|
+
* ```
|
|
54
|
+
*/
|
|
55
|
+
declare function useUndoRedo<TStore extends CommanderSlice>(store: UseBoundStore<StoreApi<TStore>>): UndoRedoState;
|
|
56
|
+
/**
|
|
57
|
+
* Options for the keyboard shortcut hook.
|
|
58
|
+
*/
|
|
59
|
+
interface UseUndoRedoKeyboardOptions {
|
|
60
|
+
/** Enable Ctrl/Cmd+Z for undo (default: true) */
|
|
61
|
+
readonly enableUndo?: boolean;
|
|
62
|
+
/** Enable Ctrl/Cmd+Shift+Z or Ctrl+Y for redo (default: true) */
|
|
63
|
+
readonly enableRedo?: boolean;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* React hook that adds keyboard shortcuts for undo/redo.
|
|
67
|
+
* Listens for Ctrl/Cmd+Z (undo) and Ctrl/Cmd+Shift+Z or Ctrl+Y (redo).
|
|
68
|
+
*
|
|
69
|
+
* @example
|
|
70
|
+
* ```tsx
|
|
71
|
+
* // In your app component
|
|
72
|
+
* useUndoRedoKeyboard(useStore);
|
|
73
|
+
* ```
|
|
74
|
+
*/
|
|
75
|
+
declare function useUndoRedoKeyboard<TStore extends CommanderSlice>(store: UseBoundStore<StoreApi<TStore>>, options?: UseUndoRedoKeyboardOptions): void;
|
|
76
|
+
//#endregion
|
|
77
|
+
export { UndoRedoState, UseUndoRedoKeyboardOptions, useCommander, useUndoRedo, useUndoRedoKeyboard };
|
|
78
|
+
//# sourceMappingURL=hooks.d.cts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hooks.d.cts","names":[],"sources":["../../src/zustand-commander/hooks.ts"],"sourcesContent":[],"mappings":";;;;;;;;AAsHA;AAiCA;;;;;;;AAiDA;AAiBA;AAAmD,iBA5HnC,YA4HmC,CAAA,eA5HP,cA4HO,CAAA,CAAA,KAAA,EA3H1C,aA2H0C,CA3H5B,QA2H4B,CA3HnB,MA2HmB,CAAA,CAAA,CAAA,EA1HhD,eA0HgD,CA1HhC,MA0HgC,CAAA;;;;AAExC,UArGM,aAAA,CAqGN;EAA+B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBApE1B,2BAA2B,uBAClC,cAAc,SAAS,WAC7B;;;;UA+Cc,0BAAA;;;;;;;;;;;;;;;;iBAiBD,mCAAmC,uBAC1C,cAAc,SAAS,oBACrB"}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { CommandDispatch, CommanderSlice } from "./types.mjs";
|
|
2
|
+
import { StoreApi, UseBoundStore } from "zustand";
|
|
3
|
+
|
|
4
|
+
//#region src/zustand-commander/hooks.d.ts
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* React hook to get a dispatch function for commands.
|
|
8
|
+
* The dispatch function executes commands and manages undo/redo state.
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* ```tsx
|
|
12
|
+
* const dispatch = useCommander(useStore);
|
|
13
|
+
*
|
|
14
|
+
* const handleClick = () => {
|
|
15
|
+
* dispatch(addCard)({ columnId: "col-1", title: "New Card" });
|
|
16
|
+
* };
|
|
17
|
+
* ```
|
|
18
|
+
*/
|
|
19
|
+
declare function useCommander<TStore extends CommanderSlice>(store: UseBoundStore<StoreApi<TStore>>): CommandDispatch<TStore>;
|
|
20
|
+
/**
|
|
21
|
+
* State and actions for undo/redo functionality.
|
|
22
|
+
*/
|
|
23
|
+
interface UndoRedoState {
|
|
24
|
+
/** Whether there are actions that can be undone */
|
|
25
|
+
readonly canUndo: boolean;
|
|
26
|
+
/** Whether there are actions that can be redone */
|
|
27
|
+
readonly canRedo: boolean;
|
|
28
|
+
/** Number of items in the undo stack */
|
|
29
|
+
readonly undoCount: number;
|
|
30
|
+
/** Number of items in the redo stack */
|
|
31
|
+
readonly redoCount: number;
|
|
32
|
+
/** Undo the last action */
|
|
33
|
+
readonly undo: () => boolean;
|
|
34
|
+
/** Redo the last undone action */
|
|
35
|
+
readonly redo: () => boolean;
|
|
36
|
+
/** Clear the undo/redo history */
|
|
37
|
+
readonly clear: () => void;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* React hook for undo/redo functionality.
|
|
41
|
+
* Provides state (canUndo, canRedo) and actions (undo, redo, clear).
|
|
42
|
+
*
|
|
43
|
+
* @example
|
|
44
|
+
* ```tsx
|
|
45
|
+
* const { canUndo, canRedo, undo, redo } = useUndoRedo(useStore);
|
|
46
|
+
*
|
|
47
|
+
* return (
|
|
48
|
+
* <>
|
|
49
|
+
* <button onClick={undo} disabled={!canUndo}>Undo</button>
|
|
50
|
+
* <button onClick={redo} disabled={!canRedo}>Redo</button>
|
|
51
|
+
* </>
|
|
52
|
+
* );
|
|
53
|
+
* ```
|
|
54
|
+
*/
|
|
55
|
+
declare function useUndoRedo<TStore extends CommanderSlice>(store: UseBoundStore<StoreApi<TStore>>): UndoRedoState;
|
|
56
|
+
/**
|
|
57
|
+
* Options for the keyboard shortcut hook.
|
|
58
|
+
*/
|
|
59
|
+
interface UseUndoRedoKeyboardOptions {
|
|
60
|
+
/** Enable Ctrl/Cmd+Z for undo (default: true) */
|
|
61
|
+
readonly enableUndo?: boolean;
|
|
62
|
+
/** Enable Ctrl/Cmd+Shift+Z or Ctrl+Y for redo (default: true) */
|
|
63
|
+
readonly enableRedo?: boolean;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* React hook that adds keyboard shortcuts for undo/redo.
|
|
67
|
+
* Listens for Ctrl/Cmd+Z (undo) and Ctrl/Cmd+Shift+Z or Ctrl+Y (redo).
|
|
68
|
+
*
|
|
69
|
+
* @example
|
|
70
|
+
* ```tsx
|
|
71
|
+
* // In your app component
|
|
72
|
+
* useUndoRedoKeyboard(useStore);
|
|
73
|
+
* ```
|
|
74
|
+
*/
|
|
75
|
+
declare function useUndoRedoKeyboard<TStore extends CommanderSlice>(store: UseBoundStore<StoreApi<TStore>>, options?: UseUndoRedoKeyboardOptions): void;
|
|
76
|
+
//#endregion
|
|
77
|
+
export { UndoRedoState, UseUndoRedoKeyboardOptions, useCommander, useUndoRedo, useUndoRedoKeyboard };
|
|
78
|
+
//# sourceMappingURL=hooks.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hooks.d.mts","names":[],"sources":["../../src/zustand-commander/hooks.ts"],"sourcesContent":[],"mappings":";;;;;;;;AAsHA;AAiCA;;;;;;;AAiDA;AAiBA;AAAmD,iBA5HnC,YA4HmC,CAAA,eA5HP,cA4HO,CAAA,CAAA,KAAA,EA3H1C,aA2H0C,CA3H5B,QA2H4B,CA3HnB,MA2HmB,CAAA,CAAA,CAAA,EA1HhD,eA0HgD,CA1HhC,MA0HgC,CAAA;;;;AAExC,UArGM,aAAA,CAqGN;EAA+B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBApE1B,2BAA2B,uBAClC,cAAc,SAAS,WAC7B;;;;UA+Cc,0BAAA;;;;;;;;;;;;;;;;iBAiBD,mCAAmC,uBAC1C,cAAc,SAAS,oBACrB"}
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
import { _objectSpread2 } from "../_virtual/_@oxc-project_runtime@0.103.0/helpers/objectSpread2.mjs";
|
|
2
|
+
import { isUndoableCommand } from "./types.mjs";
|
|
3
|
+
import { clearUndoHistory, performRedo, performUndo } from "./commander.mjs";
|
|
4
|
+
import { useCallback, useEffect, useMemo } from "react";
|
|
5
|
+
import { useStore } from "zustand";
|
|
6
|
+
|
|
7
|
+
//#region src/zustand-commander/hooks.ts
|
|
8
|
+
/**
|
|
9
|
+
* @voidhash/mimic-react/zustand-commander
|
|
10
|
+
*
|
|
11
|
+
* React hooks for zustand-commander.
|
|
12
|
+
*
|
|
13
|
+
* @since 0.0.1
|
|
14
|
+
*/
|
|
15
|
+
/**
|
|
16
|
+
* Creates a dispatch function for commands.
|
|
17
|
+
* This is for use outside of React components (e.g., in command handlers).
|
|
18
|
+
*/
|
|
19
|
+
function createDispatchFromApi(storeApi, maxUndoStackSize = 100) {
|
|
20
|
+
const dispatch = (command) => {
|
|
21
|
+
return (params) => {
|
|
22
|
+
const ctx = {
|
|
23
|
+
getState: () => storeApi.getState(),
|
|
24
|
+
setState: (partial) => storeApi.setState(partial),
|
|
25
|
+
dispatch
|
|
26
|
+
};
|
|
27
|
+
const result = command.fn(ctx, params);
|
|
28
|
+
if (isUndoableCommand(command)) storeApi.setState((state) => {
|
|
29
|
+
const { undoStack } = state._commander;
|
|
30
|
+
const newUndoStack = [...undoStack, {
|
|
31
|
+
command,
|
|
32
|
+
params,
|
|
33
|
+
result,
|
|
34
|
+
timestamp: Date.now()
|
|
35
|
+
}].slice(-maxUndoStackSize);
|
|
36
|
+
return _objectSpread2(_objectSpread2({}, state), {}, { _commander: {
|
|
37
|
+
undoStack: newUndoStack,
|
|
38
|
+
redoStack: []
|
|
39
|
+
} });
|
|
40
|
+
});
|
|
41
|
+
return result;
|
|
42
|
+
};
|
|
43
|
+
};
|
|
44
|
+
return dispatch;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* React hook to get a dispatch function for commands.
|
|
48
|
+
* The dispatch function executes commands and manages undo/redo state.
|
|
49
|
+
*
|
|
50
|
+
* @example
|
|
51
|
+
* ```tsx
|
|
52
|
+
* const dispatch = useCommander(useStore);
|
|
53
|
+
*
|
|
54
|
+
* const handleClick = () => {
|
|
55
|
+
* dispatch(addCard)({ columnId: "col-1", title: "New Card" });
|
|
56
|
+
* };
|
|
57
|
+
* ```
|
|
58
|
+
*/
|
|
59
|
+
function useCommander(store) {
|
|
60
|
+
const storeApi = useMemo(() => {
|
|
61
|
+
return store;
|
|
62
|
+
}, [store]);
|
|
63
|
+
return useMemo(() => createDispatchFromApi(storeApi), [storeApi]);
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* React hook for undo/redo functionality.
|
|
67
|
+
* Provides state (canUndo, canRedo) and actions (undo, redo, clear).
|
|
68
|
+
*
|
|
69
|
+
* @example
|
|
70
|
+
* ```tsx
|
|
71
|
+
* const { canUndo, canRedo, undo, redo } = useUndoRedo(useStore);
|
|
72
|
+
*
|
|
73
|
+
* return (
|
|
74
|
+
* <>
|
|
75
|
+
* <button onClick={undo} disabled={!canUndo}>Undo</button>
|
|
76
|
+
* <button onClick={redo} disabled={!canRedo}>Redo</button>
|
|
77
|
+
* </>
|
|
78
|
+
* );
|
|
79
|
+
* ```
|
|
80
|
+
*/
|
|
81
|
+
function useUndoRedo(store) {
|
|
82
|
+
const storeApi = useMemo(() => {
|
|
83
|
+
return store;
|
|
84
|
+
}, [store]);
|
|
85
|
+
const commanderState = useStore(store, (state) => state._commander);
|
|
86
|
+
return {
|
|
87
|
+
canUndo: commanderState.undoStack.length > 0,
|
|
88
|
+
canRedo: commanderState.redoStack.length > 0,
|
|
89
|
+
undoCount: commanderState.undoStack.length,
|
|
90
|
+
redoCount: commanderState.redoStack.length,
|
|
91
|
+
undo: useCallback(() => {
|
|
92
|
+
return performUndo(storeApi);
|
|
93
|
+
}, [storeApi]),
|
|
94
|
+
redo: useCallback(() => {
|
|
95
|
+
return performRedo(storeApi);
|
|
96
|
+
}, [storeApi]),
|
|
97
|
+
clear: useCallback(() => {
|
|
98
|
+
clearUndoHistory(storeApi);
|
|
99
|
+
}, [storeApi])
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* React hook that adds keyboard shortcuts for undo/redo.
|
|
104
|
+
* Listens for Ctrl/Cmd+Z (undo) and Ctrl/Cmd+Shift+Z or Ctrl+Y (redo).
|
|
105
|
+
*
|
|
106
|
+
* @example
|
|
107
|
+
* ```tsx
|
|
108
|
+
* // In your app component
|
|
109
|
+
* useUndoRedoKeyboard(useStore);
|
|
110
|
+
* ```
|
|
111
|
+
*/
|
|
112
|
+
function useUndoRedoKeyboard(store, options = {}) {
|
|
113
|
+
const { enableUndo = true, enableRedo = true } = options;
|
|
114
|
+
const storeApi = useMemo(() => {
|
|
115
|
+
return store;
|
|
116
|
+
}, [store]);
|
|
117
|
+
useEffect(() => {
|
|
118
|
+
if (typeof window === "undefined") return;
|
|
119
|
+
const handleKeyDown = (event) => {
|
|
120
|
+
if (!(navigator.platform.toUpperCase().indexOf("MAC") >= 0 ? event.metaKey : event.ctrlKey)) return;
|
|
121
|
+
if (enableUndo && event.key === "z" && !event.shiftKey) {
|
|
122
|
+
event.preventDefault();
|
|
123
|
+
performUndo(storeApi);
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
if (enableRedo) {
|
|
127
|
+
if (event.key === "z" && event.shiftKey || event.key === "y") {
|
|
128
|
+
event.preventDefault();
|
|
129
|
+
performRedo(storeApi);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
};
|
|
133
|
+
window.addEventListener("keydown", handleKeyDown);
|
|
134
|
+
return () => window.removeEventListener("keydown", handleKeyDown);
|
|
135
|
+
}, [
|
|
136
|
+
storeApi,
|
|
137
|
+
enableUndo,
|
|
138
|
+
enableRedo
|
|
139
|
+
]);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
//#endregion
|
|
143
|
+
export { useCommander, useUndoRedo, useUndoRedoKeyboard };
|
|
144
|
+
//# sourceMappingURL=hooks.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hooks.mjs","names":["dispatch: CommandDispatch<TStore>","ctx: CommandContext<TStore>"],"sources":["../../src/zustand-commander/hooks.ts"],"sourcesContent":["/**\n * @voidhash/mimic-react/zustand-commander\n *\n * React hooks for zustand-commander.\n *\n * @since 0.0.1\n */\n\nimport { useCallback, useEffect, useMemo } from \"react\";\nimport { useStore, type StoreApi, type UseBoundStore } from \"zustand\";\nimport { performRedo, performUndo, clearUndoHistory } from \"./commander\";\nimport {\n isUndoableCommand,\n type Command,\n type CommandContext,\n type CommandDispatch,\n type CommanderSlice,\n type ExtractState,\n} from \"./types.js\";\n\n// =============================================================================\n// useCommander Hook\n// =============================================================================\n\n/**\n * Creates a dispatch function for commands.\n * This is for use outside of React components (e.g., in command handlers).\n */\nfunction createDispatchFromApi<TStore extends CommanderSlice>(\n storeApi: StoreApi<TStore>,\n maxUndoStackSize = 100\n): CommandDispatch<TStore> {\n const dispatch: CommandDispatch<TStore> = <TParams, TReturn>(\n command: Command<TStore, TParams, TReturn>\n ) => {\n return (params: TParams): TReturn => {\n // Create context for the command\n const ctx: CommandContext<TStore> = {\n getState: () => storeApi.getState(),\n setState: (partial) => storeApi.setState(partial as Partial<TStore>),\n dispatch,\n };\n\n // Execute the command\n const result = command.fn(ctx, params);\n\n // If it's an undoable command, add to undo stack\n if (isUndoableCommand(command)) {\n storeApi.setState((state: TStore) => {\n const { undoStack } = state._commander;\n\n // Add to undo stack, respecting max size\n const newUndoStack = [\n ...undoStack,\n {\n command,\n params,\n result,\n timestamp: Date.now(),\n },\n ].slice(-maxUndoStackSize);\n\n // Clear redo stack when a new command is executed\n return {\n ...state,\n _commander: {\n undoStack: newUndoStack,\n redoStack: [],\n },\n } as TStore;\n });\n }\n\n return result;\n };\n };\n\n return dispatch;\n}\n\n/**\n * React hook to get a dispatch function for commands.\n * The dispatch function executes commands and manages undo/redo state.\n *\n * @example\n * ```tsx\n * const dispatch = useCommander(useStore);\n *\n * const handleClick = () => {\n * dispatch(addCard)({ columnId: \"col-1\", title: \"New Card\" });\n * };\n * ```\n */\nexport function useCommander<TStore extends CommanderSlice>(\n store: UseBoundStore<StoreApi<TStore>>\n): CommandDispatch<TStore> {\n // Get the store API\n const storeApi = useMemo(() => {\n // UseBoundStore has the StoreApi attached\n return store as unknown as StoreApi<TStore>;\n }, [store]);\n\n // Create a stable dispatch function\n const dispatch = useMemo(\n () => createDispatchFromApi<TStore>(storeApi),\n [storeApi]\n );\n\n return dispatch as CommandDispatch<TStore>;\n}\n\n// =============================================================================\n// useUndoRedo Hook\n// =============================================================================\n\n/**\n * State and actions for undo/redo functionality.\n */\nexport interface UndoRedoState {\n /** Whether there are actions that can be undone */\n readonly canUndo: boolean;\n /** Whether there are actions that can be redone */\n readonly canRedo: boolean;\n /** Number of items in the undo stack */\n readonly undoCount: number;\n /** Number of items in the redo stack */\n readonly redoCount: number;\n /** Undo the last action */\n readonly undo: () => boolean;\n /** Redo the last undone action */\n readonly redo: () => boolean;\n /** Clear the undo/redo history */\n readonly clear: () => void;\n}\n\n/**\n * React hook for undo/redo functionality.\n * Provides state (canUndo, canRedo) and actions (undo, redo, clear).\n *\n * @example\n * ```tsx\n * const { canUndo, canRedo, undo, redo } = useUndoRedo(useStore);\n *\n * return (\n * <>\n * <button onClick={undo} disabled={!canUndo}>Undo</button>\n * <button onClick={redo} disabled={!canRedo}>Redo</button>\n * </>\n * );\n * ```\n */\nexport function useUndoRedo<TStore extends CommanderSlice>(\n store: UseBoundStore<StoreApi<TStore>>\n): UndoRedoState {\n // Get the store API\n const storeApi = useMemo(() => {\n return store as unknown as StoreApi<TStore>;\n }, [store]);\n\n // Subscribe to commander state\n const commanderState = useStore(\n store,\n (state: TStore) => state._commander\n );\n\n const canUndo = commanderState.undoStack.length > 0;\n const canRedo = commanderState.redoStack.length > 0;\n const undoCount = commanderState.undoStack.length;\n const redoCount = commanderState.redoStack.length;\n\n const undo = useCallback(() => {\n return performUndo(storeApi);\n }, [storeApi]);\n\n const redo = useCallback(() => {\n return performRedo(storeApi);\n }, [storeApi]);\n\n const clear = useCallback(() => {\n clearUndoHistory(storeApi);\n }, [storeApi]);\n\n return {\n canUndo,\n canRedo,\n undoCount,\n redoCount,\n undo,\n redo,\n clear,\n };\n}\n\n// =============================================================================\n// Keyboard Shortcut Hook\n// =============================================================================\n\n/**\n * Options for the keyboard shortcut hook.\n */\nexport interface UseUndoRedoKeyboardOptions {\n /** Enable Ctrl/Cmd+Z for undo (default: true) */\n readonly enableUndo?: boolean;\n /** Enable Ctrl/Cmd+Shift+Z or Ctrl+Y for redo (default: true) */\n readonly enableRedo?: boolean;\n}\n\n/**\n * React hook that adds keyboard shortcuts for undo/redo.\n * Listens for Ctrl/Cmd+Z (undo) and Ctrl/Cmd+Shift+Z or Ctrl+Y (redo).\n *\n * @example\n * ```tsx\n * // In your app component\n * useUndoRedoKeyboard(useStore);\n * ```\n */\nexport function useUndoRedoKeyboard<TStore extends CommanderSlice>(\n store: UseBoundStore<StoreApi<TStore>>,\n options: UseUndoRedoKeyboardOptions = {}\n): void {\n const { enableUndo = true, enableRedo = true } = options;\n\n const storeApi = useMemo(() => {\n return store as unknown as StoreApi<TStore>;\n }, [store]);\n\n // Set up keyboard listener\n useEffect(() => {\n if (typeof window === \"undefined\") {\n return;\n }\n\n const handleKeyDown = (event: KeyboardEvent) => {\n const isMac = navigator.platform.toUpperCase().indexOf(\"MAC\") >= 0;\n const modKey = isMac ? event.metaKey : event.ctrlKey;\n\n if (!modKey) return;\n\n // Undo: Ctrl/Cmd + Z (without Shift)\n if (enableUndo && event.key === \"z\" && !event.shiftKey) {\n event.preventDefault();\n performUndo(storeApi);\n return;\n }\n\n // Redo: Ctrl/Cmd + Shift + Z or Ctrl + Y\n if (enableRedo) {\n if ((event.key === \"z\" && event.shiftKey) || event.key === \"y\") {\n event.preventDefault();\n performRedo(storeApi);\n }\n }\n };\n\n window.addEventListener(\"keydown\", handleKeyDown);\n return () => window.removeEventListener(\"keydown\", handleKeyDown);\n }, [storeApi, enableUndo, enableRedo]);\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;AA4BA,SAAS,sBACP,UACA,mBAAmB,KACM;CACzB,MAAMA,YACJ,YACG;AACH,UAAQ,WAA6B;GAEnC,MAAMC,MAA8B;IAClC,gBAAgB,SAAS,UAAU;IACnC,WAAW,YAAY,SAAS,SAAS,QAA2B;IACpE;IACD;GAGD,MAAM,SAAS,QAAQ,GAAG,KAAK,OAAO;AAGtC,OAAI,kBAAkB,QAAQ,CAC5B,UAAS,UAAU,UAAkB;IACnC,MAAM,EAAE,cAAc,MAAM;IAG5B,MAAM,eAAe,CACnB,GAAG,WACH;KACE;KACA;KACA;KACA,WAAW,KAAK,KAAK;KACtB,CACF,CAAC,MAAM,CAAC,iBAAiB;AAG1B,6CACK,cACH,YAAY;KACV,WAAW;KACX,WAAW,EAAE;KACd;KAEH;AAGJ,UAAO;;;AAIX,QAAO;;;;;;;;;;;;;;;AAgBT,SAAgB,aACd,OACyB;CAEzB,MAAM,WAAW,cAAc;AAE7B,SAAO;IACN,CAAC,MAAM,CAAC;AAQX,QALiB,cACT,sBAA8B,SAAS,EAC7C,CAAC,SAAS,CACX;;;;;;;;;;;;;;;;;;AA6CH,SAAgB,YACd,OACe;CAEf,MAAM,WAAW,cAAc;AAC7B,SAAO;IACN,CAAC,MAAM,CAAC;CAGX,MAAM,iBAAiB,SACrB,QACC,UAAkB,MAAM,WAC1B;AAmBD,QAAO;EACL,SAlBc,eAAe,UAAU,SAAS;EAmBhD,SAlBc,eAAe,UAAU,SAAS;EAmBhD,WAlBgB,eAAe,UAAU;EAmBzC,WAlBgB,eAAe,UAAU;EAmBzC,MAjBW,kBAAkB;AAC7B,UAAO,YAAY,SAAS;KAC3B,CAAC,SAAS,CAAC;EAgBZ,MAdW,kBAAkB;AAC7B,UAAO,YAAY,SAAS;KAC3B,CAAC,SAAS,CAAC;EAaZ,OAXY,kBAAkB;AAC9B,oBAAiB,SAAS;KACzB,CAAC,SAAS,CAAC;EAUb;;;;;;;;;;;;AA2BH,SAAgB,oBACd,OACA,UAAsC,EAAE,EAClC;CACN,MAAM,EAAE,aAAa,MAAM,aAAa,SAAS;CAEjD,MAAM,WAAW,cAAc;AAC7B,SAAO;IACN,CAAC,MAAM,CAAC;AAGX,iBAAgB;AACd,MAAI,OAAO,WAAW,YACpB;EAGF,MAAM,iBAAiB,UAAyB;AAI9C,OAAI,EAHU,UAAU,SAAS,aAAa,CAAC,QAAQ,MAAM,IAAI,IAC1C,MAAM,UAAU,MAAM,SAEhC;AAGb,OAAI,cAAc,MAAM,QAAQ,OAAO,CAAC,MAAM,UAAU;AACtD,UAAM,gBAAgB;AACtB,gBAAY,SAAS;AACrB;;AAIF,OAAI,YACF;QAAK,MAAM,QAAQ,OAAO,MAAM,YAAa,MAAM,QAAQ,KAAK;AAC9D,WAAM,gBAAgB;AACtB,iBAAY,SAAS;;;;AAK3B,SAAO,iBAAiB,WAAW,cAAc;AACjD,eAAa,OAAO,oBAAoB,WAAW,cAAc;IAChE;EAAC;EAAU;EAAY;EAAW,CAAC"}
|
|
@@ -1,354 +1,15 @@
|
|
|
1
|
-
const
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
*/
|
|
17
|
-
function isCommand(value) {
|
|
18
|
-
return typeof value === "object" && value !== null && COMMAND_SYMBOL in value && value[COMMAND_SYMBOL] === true;
|
|
19
|
-
}
|
|
20
|
-
/**
|
|
21
|
-
* Type guard to check if a command is undoable.
|
|
22
|
-
*/
|
|
23
|
-
function isUndoableCommand(value) {
|
|
24
|
-
return isCommand(value) && UNDOABLE_COMMAND_SYMBOL in value && value[UNDOABLE_COMMAND_SYMBOL] === true;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
//#endregion
|
|
28
|
-
//#region src/zustand-commander/commander.ts
|
|
29
|
-
const DEFAULT_OPTIONS = { maxUndoStackSize: 100 };
|
|
30
|
-
/**
|
|
31
|
-
* Creates a commander instance bound to a specific store type.
|
|
32
|
-
*
|
|
33
|
-
* @example
|
|
34
|
-
* ```ts
|
|
35
|
-
* // Create commander for your store type
|
|
36
|
-
* const commander = createCommander<StoreState>();
|
|
37
|
-
*
|
|
38
|
-
* // Define commands
|
|
39
|
-
* const addItem = commander.action(
|
|
40
|
-
* Schema.Struct({ name: Schema.String }),
|
|
41
|
-
* (ctx, params) => {
|
|
42
|
-
* const { mimic } = ctx.getState();
|
|
43
|
-
* mimic.document.transaction(root => {
|
|
44
|
-
* // add item
|
|
45
|
-
* });
|
|
46
|
-
* }
|
|
47
|
-
* );
|
|
48
|
-
*
|
|
49
|
-
* // Create store with middleware
|
|
50
|
-
* const useStore = create(
|
|
51
|
-
* commander.middleware(
|
|
52
|
-
* mimic(document, (set, get) => ({
|
|
53
|
-
* // your state
|
|
54
|
-
* }))
|
|
55
|
-
* )
|
|
56
|
-
* );
|
|
57
|
-
* ```
|
|
58
|
-
*/
|
|
59
|
-
function createCommander(options = {}) {
|
|
60
|
-
const { maxUndoStackSize } = require_objectSpread2._objectSpread2(require_objectSpread2._objectSpread2({}, DEFAULT_OPTIONS), options);
|
|
61
|
-
let _storeApi = null;
|
|
62
|
-
/**
|
|
63
|
-
* Creates the dispatch function for use within command handlers.
|
|
64
|
-
*/
|
|
65
|
-
const createDispatch = () => {
|
|
66
|
-
return (command) => {
|
|
67
|
-
return (params) => {
|
|
68
|
-
if (!_storeApi) throw new Error("Commander: Store not initialized. Make sure to use the commander middleware.");
|
|
69
|
-
const ctx = {
|
|
70
|
-
getState: () => _storeApi.getState(),
|
|
71
|
-
setState: (partial) => _storeApi.setState(partial),
|
|
72
|
-
dispatch: createDispatch()
|
|
73
|
-
};
|
|
74
|
-
const result = command.fn(ctx, params);
|
|
75
|
-
if (isUndoableCommand(command)) {
|
|
76
|
-
const entry = {
|
|
77
|
-
command,
|
|
78
|
-
params,
|
|
79
|
-
result,
|
|
80
|
-
timestamp: Date.now()
|
|
81
|
-
};
|
|
82
|
-
_storeApi.setState((state) => {
|
|
83
|
-
const { undoStack, redoStack } = state._commander;
|
|
84
|
-
const newUndoStack = [...undoStack, entry].slice(-maxUndoStackSize);
|
|
85
|
-
return require_objectSpread2._objectSpread2(require_objectSpread2._objectSpread2({}, state), {}, { _commander: {
|
|
86
|
-
undoStack: newUndoStack,
|
|
87
|
-
redoStack: []
|
|
88
|
-
} });
|
|
89
|
-
});
|
|
90
|
-
}
|
|
91
|
-
return result;
|
|
92
|
-
};
|
|
93
|
-
};
|
|
94
|
-
};
|
|
95
|
-
/**
|
|
96
|
-
* Create a regular command (no undo support).
|
|
97
|
-
*/
|
|
98
|
-
function action(fn) {
|
|
99
|
-
return {
|
|
100
|
-
[COMMAND_SYMBOL]: true,
|
|
101
|
-
fn
|
|
102
|
-
};
|
|
103
|
-
}
|
|
104
|
-
/**
|
|
105
|
-
* Create an undoable command with undo/redo support.
|
|
106
|
-
*/
|
|
107
|
-
function undoableAction(fn, revert) {
|
|
108
|
-
return {
|
|
109
|
-
[COMMAND_SYMBOL]: true,
|
|
110
|
-
[UNDOABLE_COMMAND_SYMBOL]: true,
|
|
111
|
-
fn,
|
|
112
|
-
revert
|
|
113
|
-
};
|
|
114
|
-
}
|
|
115
|
-
/**
|
|
116
|
-
* Zustand middleware that adds commander functionality.
|
|
117
|
-
*/
|
|
118
|
-
const middleware = (config) => {
|
|
119
|
-
return (set, get, api) => {
|
|
120
|
-
_storeApi = api;
|
|
121
|
-
return require_objectSpread2._objectSpread2(require_objectSpread2._objectSpread2({}, config(set, get, api)), {}, { _commander: {
|
|
122
|
-
undoStack: [],
|
|
123
|
-
redoStack: []
|
|
124
|
-
} });
|
|
125
|
-
};
|
|
126
|
-
};
|
|
127
|
-
return {
|
|
128
|
-
action,
|
|
129
|
-
undoableAction,
|
|
130
|
-
middleware
|
|
131
|
-
};
|
|
132
|
-
}
|
|
133
|
-
/**
|
|
134
|
-
* Perform an undo operation on the store.
|
|
135
|
-
* Returns true if an undo was performed, false if undo stack was empty.
|
|
136
|
-
*/
|
|
137
|
-
function performUndo(storeApi) {
|
|
138
|
-
const { undoStack, redoStack } = storeApi.getState()._commander;
|
|
139
|
-
const entry = undoStack[undoStack.length - 1];
|
|
140
|
-
if (!entry) return false;
|
|
141
|
-
const newUndoStack = undoStack.slice(0, -1);
|
|
142
|
-
const ctx = {
|
|
143
|
-
getState: () => storeApi.getState(),
|
|
144
|
-
setState: (partial) => storeApi.setState(partial),
|
|
145
|
-
dispatch: createDispatchForUndo(storeApi)
|
|
146
|
-
};
|
|
147
|
-
entry.command.revert(ctx, entry.params, entry.result);
|
|
148
|
-
storeApi.setState((state) => require_objectSpread2._objectSpread2(require_objectSpread2._objectSpread2({}, state), {}, { _commander: {
|
|
149
|
-
undoStack: newUndoStack,
|
|
150
|
-
redoStack: [...redoStack, entry]
|
|
151
|
-
} }));
|
|
152
|
-
return true;
|
|
153
|
-
}
|
|
154
|
-
/**
|
|
155
|
-
* Perform a redo operation on the store.
|
|
156
|
-
* Returns true if a redo was performed, false if redo stack was empty.
|
|
157
|
-
*/
|
|
158
|
-
function performRedo(storeApi) {
|
|
159
|
-
const { undoStack, redoStack } = storeApi.getState()._commander;
|
|
160
|
-
const entry = redoStack[redoStack.length - 1];
|
|
161
|
-
if (!entry) return false;
|
|
162
|
-
const newRedoStack = redoStack.slice(0, -1);
|
|
163
|
-
const ctx = {
|
|
164
|
-
getState: () => storeApi.getState(),
|
|
165
|
-
setState: (partial) => storeApi.setState(partial),
|
|
166
|
-
dispatch: createDispatchForUndo(storeApi)
|
|
167
|
-
};
|
|
168
|
-
const result = entry.command.fn(ctx, entry.params);
|
|
169
|
-
const newEntry = {
|
|
170
|
-
command: entry.command,
|
|
171
|
-
params: entry.params,
|
|
172
|
-
result,
|
|
173
|
-
timestamp: Date.now()
|
|
174
|
-
};
|
|
175
|
-
storeApi.setState((state) => require_objectSpread2._objectSpread2(require_objectSpread2._objectSpread2({}, state), {}, { _commander: {
|
|
176
|
-
undoStack: [...undoStack, newEntry],
|
|
177
|
-
redoStack: newRedoStack
|
|
178
|
-
} }));
|
|
179
|
-
return true;
|
|
180
|
-
}
|
|
181
|
-
/**
|
|
182
|
-
* Creates a dispatch function for use during undo/redo operations.
|
|
183
|
-
* This dispatch does NOT add to undo stack (to avoid infinite loops).
|
|
184
|
-
*/
|
|
185
|
-
function createDispatchForUndo(storeApi) {
|
|
186
|
-
return (command) => {
|
|
187
|
-
return (params) => {
|
|
188
|
-
const ctx = {
|
|
189
|
-
getState: () => storeApi.getState(),
|
|
190
|
-
setState: (partial) => storeApi.setState(partial),
|
|
191
|
-
dispatch: createDispatchForUndo(storeApi)
|
|
192
|
-
};
|
|
193
|
-
return command.fn(ctx, params);
|
|
194
|
-
};
|
|
195
|
-
};
|
|
196
|
-
}
|
|
197
|
-
/**
|
|
198
|
-
* Clear the undo and redo stacks.
|
|
199
|
-
*/
|
|
200
|
-
function clearUndoHistory(storeApi) {
|
|
201
|
-
storeApi.setState((state) => require_objectSpread2._objectSpread2(require_objectSpread2._objectSpread2({}, state), {}, { _commander: {
|
|
202
|
-
undoStack: [],
|
|
203
|
-
redoStack: []
|
|
204
|
-
} }));
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
//#endregion
|
|
208
|
-
//#region src/zustand-commander/hooks.ts
|
|
209
|
-
/**
|
|
210
|
-
* @voidhash/mimic-react/zustand-commander
|
|
211
|
-
*
|
|
212
|
-
* React hooks for zustand-commander.
|
|
213
|
-
*
|
|
214
|
-
* @since 0.0.1
|
|
215
|
-
*/
|
|
216
|
-
/**
|
|
217
|
-
* Creates a dispatch function for commands.
|
|
218
|
-
* This is for use outside of React components (e.g., in command handlers).
|
|
219
|
-
*/
|
|
220
|
-
function createDispatchFromApi(storeApi, maxUndoStackSize = 100) {
|
|
221
|
-
const dispatch = (command) => {
|
|
222
|
-
return (params) => {
|
|
223
|
-
const ctx = {
|
|
224
|
-
getState: () => storeApi.getState(),
|
|
225
|
-
setState: (partial) => storeApi.setState(partial),
|
|
226
|
-
dispatch
|
|
227
|
-
};
|
|
228
|
-
const result = command.fn(ctx, params);
|
|
229
|
-
if (isUndoableCommand(command)) storeApi.setState((state) => {
|
|
230
|
-
const { undoStack } = state._commander;
|
|
231
|
-
const newUndoStack = [...undoStack, {
|
|
232
|
-
command,
|
|
233
|
-
params,
|
|
234
|
-
result,
|
|
235
|
-
timestamp: Date.now()
|
|
236
|
-
}].slice(-maxUndoStackSize);
|
|
237
|
-
return require_objectSpread2._objectSpread2(require_objectSpread2._objectSpread2({}, state), {}, { _commander: {
|
|
238
|
-
undoStack: newUndoStack,
|
|
239
|
-
redoStack: []
|
|
240
|
-
} });
|
|
241
|
-
});
|
|
242
|
-
return result;
|
|
243
|
-
};
|
|
244
|
-
};
|
|
245
|
-
return dispatch;
|
|
246
|
-
}
|
|
247
|
-
/**
|
|
248
|
-
* React hook to get a dispatch function for commands.
|
|
249
|
-
* The dispatch function executes commands and manages undo/redo state.
|
|
250
|
-
*
|
|
251
|
-
* @example
|
|
252
|
-
* ```tsx
|
|
253
|
-
* const dispatch = useCommander(useStore);
|
|
254
|
-
*
|
|
255
|
-
* const handleClick = () => {
|
|
256
|
-
* dispatch(addCard)({ columnId: "col-1", title: "New Card" });
|
|
257
|
-
* };
|
|
258
|
-
* ```
|
|
259
|
-
*/
|
|
260
|
-
function useCommander(store) {
|
|
261
|
-
const storeApi = (0, react.useMemo)(() => {
|
|
262
|
-
return store;
|
|
263
|
-
}, [store]);
|
|
264
|
-
return (0, react.useMemo)(() => createDispatchFromApi(storeApi), [storeApi]);
|
|
265
|
-
}
|
|
266
|
-
/**
|
|
267
|
-
* React hook for undo/redo functionality.
|
|
268
|
-
* Provides state (canUndo, canRedo) and actions (undo, redo, clear).
|
|
269
|
-
*
|
|
270
|
-
* @example
|
|
271
|
-
* ```tsx
|
|
272
|
-
* const { canUndo, canRedo, undo, redo } = useUndoRedo(useStore);
|
|
273
|
-
*
|
|
274
|
-
* return (
|
|
275
|
-
* <>
|
|
276
|
-
* <button onClick={undo} disabled={!canUndo}>Undo</button>
|
|
277
|
-
* <button onClick={redo} disabled={!canRedo}>Redo</button>
|
|
278
|
-
* </>
|
|
279
|
-
* );
|
|
280
|
-
* ```
|
|
281
|
-
*/
|
|
282
|
-
function useUndoRedo(store) {
|
|
283
|
-
const storeApi = (0, react.useMemo)(() => {
|
|
284
|
-
return store;
|
|
285
|
-
}, [store]);
|
|
286
|
-
const commanderState = (0, zustand.useStore)(store, (state) => state._commander);
|
|
287
|
-
return {
|
|
288
|
-
canUndo: commanderState.undoStack.length > 0,
|
|
289
|
-
canRedo: commanderState.redoStack.length > 0,
|
|
290
|
-
undoCount: commanderState.undoStack.length,
|
|
291
|
-
redoCount: commanderState.redoStack.length,
|
|
292
|
-
undo: (0, react.useCallback)(() => {
|
|
293
|
-
return performUndo(storeApi);
|
|
294
|
-
}, [storeApi]),
|
|
295
|
-
redo: (0, react.useCallback)(() => {
|
|
296
|
-
return performRedo(storeApi);
|
|
297
|
-
}, [storeApi]),
|
|
298
|
-
clear: (0, react.useCallback)(() => {
|
|
299
|
-
clearUndoHistory(storeApi);
|
|
300
|
-
}, [storeApi])
|
|
301
|
-
};
|
|
302
|
-
}
|
|
303
|
-
/**
|
|
304
|
-
* React hook that adds keyboard shortcuts for undo/redo.
|
|
305
|
-
* Listens for Ctrl/Cmd+Z (undo) and Ctrl/Cmd+Shift+Z or Ctrl+Y (redo).
|
|
306
|
-
*
|
|
307
|
-
* @example
|
|
308
|
-
* ```tsx
|
|
309
|
-
* // In your app component
|
|
310
|
-
* useUndoRedoKeyboard(useStore);
|
|
311
|
-
* ```
|
|
312
|
-
*/
|
|
313
|
-
function useUndoRedoKeyboard(store, options = {}) {
|
|
314
|
-
const { enableUndo = true, enableRedo = true } = options;
|
|
315
|
-
const storeApi = (0, react.useMemo)(() => {
|
|
316
|
-
return store;
|
|
317
|
-
}, [store]);
|
|
318
|
-
(0, react.useEffect)(() => {
|
|
319
|
-
if (typeof window === "undefined") return;
|
|
320
|
-
const handleKeyDown = (event) => {
|
|
321
|
-
if (!(navigator.platform.toUpperCase().indexOf("MAC") >= 0 ? event.metaKey : event.ctrlKey)) return;
|
|
322
|
-
if (enableUndo && event.key === "z" && !event.shiftKey) {
|
|
323
|
-
event.preventDefault();
|
|
324
|
-
performUndo(storeApi);
|
|
325
|
-
return;
|
|
326
|
-
}
|
|
327
|
-
if (enableRedo) {
|
|
328
|
-
if (event.key === "z" && event.shiftKey || event.key === "y") {
|
|
329
|
-
event.preventDefault();
|
|
330
|
-
performRedo(storeApi);
|
|
331
|
-
}
|
|
332
|
-
}
|
|
333
|
-
};
|
|
334
|
-
window.addEventListener("keydown", handleKeyDown);
|
|
335
|
-
return () => window.removeEventListener("keydown", handleKeyDown);
|
|
336
|
-
}, [
|
|
337
|
-
storeApi,
|
|
338
|
-
enableUndo,
|
|
339
|
-
enableRedo
|
|
340
|
-
]);
|
|
341
|
-
}
|
|
342
|
-
|
|
343
|
-
//#endregion
|
|
344
|
-
exports.COMMAND_SYMBOL = COMMAND_SYMBOL;
|
|
345
|
-
exports.UNDOABLE_COMMAND_SYMBOL = UNDOABLE_COMMAND_SYMBOL;
|
|
346
|
-
exports.clearUndoHistory = clearUndoHistory;
|
|
347
|
-
exports.createCommander = createCommander;
|
|
348
|
-
exports.isCommand = isCommand;
|
|
349
|
-
exports.isUndoableCommand = isUndoableCommand;
|
|
350
|
-
exports.performRedo = performRedo;
|
|
351
|
-
exports.performUndo = performUndo;
|
|
352
|
-
exports.useCommander = useCommander;
|
|
353
|
-
exports.useUndoRedo = useUndoRedo;
|
|
354
|
-
exports.useUndoRedoKeyboard = useUndoRedoKeyboard;
|
|
1
|
+
const require_types = require('./types.cjs');
|
|
2
|
+
const require_commander = require('./commander.cjs');
|
|
3
|
+
const require_hooks = require('./hooks.cjs');
|
|
4
|
+
|
|
5
|
+
exports.COMMAND_SYMBOL = require_types.COMMAND_SYMBOL;
|
|
6
|
+
exports.UNDOABLE_COMMAND_SYMBOL = require_types.UNDOABLE_COMMAND_SYMBOL;
|
|
7
|
+
exports.clearUndoHistory = require_commander.clearUndoHistory;
|
|
8
|
+
exports.createCommander = require_commander.createCommander;
|
|
9
|
+
exports.isCommand = require_types.isCommand;
|
|
10
|
+
exports.isUndoableCommand = require_types.isUndoableCommand;
|
|
11
|
+
exports.performRedo = require_commander.performRedo;
|
|
12
|
+
exports.performUndo = require_commander.performUndo;
|
|
13
|
+
exports.useCommander = require_hooks.useCommander;
|
|
14
|
+
exports.useUndoRedo = require_hooks.useUndoRedo;
|
|
15
|
+
exports.useUndoRedoKeyboard = require_hooks.useUndoRedoKeyboard;
|