jotai-state-tree 1.4.2 → 1.4.4
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/dist/{chunk-Q6QPBXHH.mjs → chunk-XA4ACZVI.mjs} +322 -225
- package/dist/index.d.mts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +321 -225
- package/dist/index.mjs +1 -1
- package/dist/react.d.mts +2 -5
- package/dist/react.d.ts +2 -5
- package/dist/react.js +356 -394
- package/dist/react.mjs +138 -158
- package/dist/{undo-DL1pyOkT.d.mts → undo-BSq-Pomp.d.mts} +12 -6
- package/dist/{undo-DL1pyOkT.d.ts → undo-BSq-Pomp.d.ts} +12 -6
- package/package.json +1 -1
- package/src/__tests__/react.react.test.tsx +112 -0
- package/src/react.ts +136 -186
- package/src/tree.ts +58 -1
- package/src/undo.ts +394 -345
|
@@ -128,6 +128,41 @@ var StateTreeNode = class {
|
|
|
128
128
|
this.$parent = parent ?? null;
|
|
129
129
|
this.$path = parent ? `${parent.$path}/${pathSegment}` : "";
|
|
130
130
|
this.valueAtom = atom(initialValue);
|
|
131
|
+
this.isAliveAtom = atom(true);
|
|
132
|
+
this.snapshotAtom = atom((get) => {
|
|
133
|
+
const value = get(this.valueAtom);
|
|
134
|
+
if (this.$type._kind === "model") {
|
|
135
|
+
const res = {};
|
|
136
|
+
for (const [key, childNode] of this.children.entries()) {
|
|
137
|
+
res[key] = get(childNode.snapshotAtom);
|
|
138
|
+
}
|
|
139
|
+
if (value && typeof value === "object") {
|
|
140
|
+
for (const key of Object.keys(value)) {
|
|
141
|
+
if (!this.children.has(key)) {
|
|
142
|
+
res[key] = value[key];
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
return this.postProcessor ? this.postProcessor(res) : res;
|
|
147
|
+
}
|
|
148
|
+
if (this.$type._kind === "array") {
|
|
149
|
+
const res = [];
|
|
150
|
+
const childrenKeys = Array.from(this.children.keys()).sort((a, b) => Number(a) - Number(b));
|
|
151
|
+
for (const key of childrenKeys) {
|
|
152
|
+
const childNode = this.children.get(key);
|
|
153
|
+
res.push(get(childNode.snapshotAtom));
|
|
154
|
+
}
|
|
155
|
+
return res;
|
|
156
|
+
}
|
|
157
|
+
if (this.$type._kind === "map") {
|
|
158
|
+
const res = {};
|
|
159
|
+
for (const [key, childNode] of this.children.entries()) {
|
|
160
|
+
res[key] = get(childNode.snapshotAtom);
|
|
161
|
+
}
|
|
162
|
+
return res;
|
|
163
|
+
}
|
|
164
|
+
return value;
|
|
165
|
+
});
|
|
131
166
|
nodeRegistry.set(this.$id, { node: new WeakRef(this), instance: null });
|
|
132
167
|
nodeFinalizationRegistry.register(this, this.$id, this);
|
|
133
168
|
}
|
|
@@ -319,6 +354,10 @@ var StateTreeNode = class {
|
|
|
319
354
|
this.children.clear();
|
|
320
355
|
this.unregisterIdentifier();
|
|
321
356
|
this.$isAlive = false;
|
|
357
|
+
try {
|
|
358
|
+
globalStore.set(this.isAliveAtom, false);
|
|
359
|
+
} catch (e) {
|
|
360
|
+
}
|
|
322
361
|
notifyLifecycleChange(this, false);
|
|
323
362
|
nodeRegistry.delete(this.$id);
|
|
324
363
|
nodeFinalizationRegistry.unregister(this);
|
|
@@ -495,6 +534,10 @@ function clearAllRegistries() {
|
|
|
495
534
|
const node = entry.node.deref();
|
|
496
535
|
if (node) {
|
|
497
536
|
node.$isAlive = false;
|
|
537
|
+
try {
|
|
538
|
+
globalStore.set(node.isAliveAtom, false);
|
|
539
|
+
} catch (e) {
|
|
540
|
+
}
|
|
498
541
|
}
|
|
499
542
|
}
|
|
500
543
|
nodeRegistry.clear();
|
|
@@ -953,29 +996,32 @@ function unfreeze(target) {
|
|
|
953
996
|
}
|
|
954
997
|
|
|
955
998
|
// src/undo.ts
|
|
956
|
-
|
|
957
|
-
var
|
|
958
|
-
var
|
|
999
|
+
import { atom as atom2 } from "jotai";
|
|
1000
|
+
var historyTrackersRegistry = /* @__PURE__ */ new WeakMap();
|
|
1001
|
+
var HistoryTracker = class {
|
|
959
1002
|
constructor(target, options = {}) {
|
|
960
|
-
|
|
961
|
-
this.
|
|
962
|
-
this.
|
|
963
|
-
this.isRedoing = false;
|
|
1003
|
+
// Transient state
|
|
1004
|
+
this.autoRecord = false;
|
|
1005
|
+
this.isApplyingHistory = false;
|
|
964
1006
|
this.skipRecording = false;
|
|
965
1007
|
this.grouping = false;
|
|
966
1008
|
this.actionGrouping = false;
|
|
967
1009
|
this.currentGroup = [];
|
|
968
1010
|
this.currentGroupInverse = [];
|
|
1011
|
+
this.lastChangeTime = 0;
|
|
969
1012
|
this.disposer = null;
|
|
970
1013
|
this.actionDisposer = null;
|
|
971
|
-
this.lastChangeTime = 0;
|
|
972
1014
|
this.target = target;
|
|
973
|
-
this.
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
1015
|
+
this.maxHistoryLength = options.maxHistoryLength ?? options.maxSnapshots ?? 100;
|
|
1016
|
+
this.groupByTime = options.groupByTime ?? false;
|
|
1017
|
+
this.groupingWindow = options.groupingWindow ?? 200;
|
|
1018
|
+
this.autoRecord = options.autoRecord ?? false;
|
|
1019
|
+
const initialSnapshot = getSnapshot(target);
|
|
1020
|
+
this.historyAtom = atom2({
|
|
1021
|
+
entries: [],
|
|
1022
|
+
currentIndex: -1,
|
|
1023
|
+
initialSnapshot
|
|
1024
|
+
});
|
|
979
1025
|
this.disposer = onPatch(target, (patch, reversePatch) => {
|
|
980
1026
|
this.recordPatch(patch, reversePatch);
|
|
981
1027
|
});
|
|
@@ -988,32 +1034,18 @@ var UndoManager = class {
|
|
|
988
1034
|
}
|
|
989
1035
|
});
|
|
990
1036
|
}
|
|
991
|
-
get canUndo() {
|
|
992
|
-
return this.currentIndex >= 0;
|
|
993
|
-
}
|
|
994
|
-
get canRedo() {
|
|
995
|
-
return this.currentIndex < this.historyEntries.length - 1;
|
|
996
|
-
}
|
|
997
|
-
get undoLevels() {
|
|
998
|
-
return this.currentIndex + 1;
|
|
999
|
-
}
|
|
1000
|
-
get redoLevels() {
|
|
1001
|
-
return this.historyEntries.length - this.currentIndex - 1;
|
|
1002
|
-
}
|
|
1003
|
-
get history() {
|
|
1004
|
-
return [...this.historyEntries];
|
|
1005
|
-
}
|
|
1006
|
-
get historyIndex() {
|
|
1007
|
-
return this.currentIndex;
|
|
1008
|
-
}
|
|
1009
1037
|
recordPatch(patch, reversePatch) {
|
|
1010
|
-
if (this.
|
|
1038
|
+
if (!this.autoRecord) {
|
|
1039
|
+
return;
|
|
1040
|
+
}
|
|
1041
|
+
if (this.isApplyingHistory || this.skipRecording) {
|
|
1011
1042
|
return;
|
|
1012
1043
|
}
|
|
1013
1044
|
const node = getStateTreeNode(this.target);
|
|
1014
1045
|
if (node.getRoot().$isApplyingHistory) {
|
|
1015
1046
|
return;
|
|
1016
1047
|
}
|
|
1048
|
+
const store = getGlobalStore();
|
|
1017
1049
|
const now = Date.now();
|
|
1018
1050
|
if (isActionRunning() && !this.grouping) {
|
|
1019
1051
|
this.grouping = true;
|
|
@@ -1031,86 +1063,172 @@ var UndoManager = class {
|
|
|
1031
1063
|
this.currentGroupInverse.push({ ...patch });
|
|
1032
1064
|
return;
|
|
1033
1065
|
}
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1066
|
+
store.set(this.historyAtom, (prev) => {
|
|
1067
|
+
let entries = prev.currentIndex < prev.entries.length - 1 ? prev.entries.slice(0, prev.currentIndex + 1) : [...prev.entries];
|
|
1068
|
+
if (this.groupByTime && entries.length > 0 && now - this.lastChangeTime < this.groupingWindow && prev.currentIndex === prev.entries.length - 1) {
|
|
1069
|
+
const lastEntry = { ...entries[entries.length - 1] };
|
|
1070
|
+
lastEntry.patches = [...lastEntry.patches, reversePatch];
|
|
1071
|
+
lastEntry.inversePatches = [...lastEntry.inversePatches, { ...patch }];
|
|
1072
|
+
lastEntry.timestamp = now;
|
|
1073
|
+
lastEntry.snapshot = getSnapshot(this.target);
|
|
1074
|
+
entries[entries.length - 1] = lastEntry;
|
|
1075
|
+
this.lastChangeTime = now;
|
|
1076
|
+
return {
|
|
1077
|
+
...prev,
|
|
1078
|
+
entries
|
|
1079
|
+
};
|
|
1080
|
+
} else {
|
|
1081
|
+
const newEntry = {
|
|
1082
|
+
patches: [reversePatch],
|
|
1083
|
+
inversePatches: [{ ...patch }],
|
|
1084
|
+
timestamp: now,
|
|
1085
|
+
snapshot: getSnapshot(this.target)
|
|
1086
|
+
};
|
|
1087
|
+
entries.push(newEntry);
|
|
1088
|
+
let newIndex = entries.length - 1;
|
|
1089
|
+
if (entries.length > this.maxHistoryLength) {
|
|
1090
|
+
const excess = entries.length - this.maxHistoryLength;
|
|
1091
|
+
entries = entries.slice(excess);
|
|
1092
|
+
newIndex -= excess;
|
|
1093
|
+
}
|
|
1094
|
+
this.lastChangeTime = now;
|
|
1095
|
+
return {
|
|
1096
|
+
...prev,
|
|
1097
|
+
entries,
|
|
1098
|
+
currentIndex: newIndex
|
|
1099
|
+
};
|
|
1053
1100
|
}
|
|
1054
|
-
}
|
|
1055
|
-
this.lastChangeTime = now;
|
|
1101
|
+
});
|
|
1056
1102
|
}
|
|
1057
1103
|
undo() {
|
|
1058
|
-
|
|
1104
|
+
const store = getGlobalStore();
|
|
1105
|
+
const state = store.get(this.historyAtom);
|
|
1106
|
+
if (state.currentIndex < 0) {
|
|
1059
1107
|
return;
|
|
1060
1108
|
}
|
|
1061
1109
|
const node = getStateTreeNode(this.target);
|
|
1062
1110
|
const rootNode = node.getRoot();
|
|
1063
1111
|
const wasApplying = rootNode.$isApplyingHistory;
|
|
1064
1112
|
rootNode.$isApplyingHistory = true;
|
|
1065
|
-
this.
|
|
1113
|
+
this.isApplyingHistory = true;
|
|
1066
1114
|
try {
|
|
1067
|
-
const entry =
|
|
1115
|
+
const entry = state.entries[state.currentIndex];
|
|
1068
1116
|
for (let i = entry.patches.length - 1; i >= 0; i--) {
|
|
1069
1117
|
applyPatch(this.target, entry.patches[i]);
|
|
1070
1118
|
}
|
|
1071
|
-
this.
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
const M = this.historyEntries.length;
|
|
1076
|
-
const ttIndex = this.currentIndex + 1 + (N - 1 - M);
|
|
1077
|
-
tt.index = Math.max(0, Math.min(N - 1, ttIndex));
|
|
1078
|
-
}
|
|
1119
|
+
store.set(this.historyAtom, (prev) => ({
|
|
1120
|
+
...prev,
|
|
1121
|
+
currentIndex: prev.currentIndex - 1
|
|
1122
|
+
}));
|
|
1079
1123
|
} finally {
|
|
1080
|
-
this.
|
|
1124
|
+
this.isApplyingHistory = false;
|
|
1081
1125
|
rootNode.$isApplyingHistory = wasApplying;
|
|
1082
1126
|
}
|
|
1083
1127
|
}
|
|
1084
1128
|
redo() {
|
|
1085
|
-
|
|
1129
|
+
const store = getGlobalStore();
|
|
1130
|
+
const state = store.get(this.historyAtom);
|
|
1131
|
+
if (state.currentIndex >= state.entries.length - 1) {
|
|
1086
1132
|
return;
|
|
1087
1133
|
}
|
|
1088
1134
|
const node = getStateTreeNode(this.target);
|
|
1089
1135
|
const rootNode = node.getRoot();
|
|
1090
1136
|
const wasApplying = rootNode.$isApplyingHistory;
|
|
1091
1137
|
rootNode.$isApplyingHistory = true;
|
|
1092
|
-
this.
|
|
1138
|
+
this.isApplyingHistory = true;
|
|
1093
1139
|
try {
|
|
1094
|
-
|
|
1095
|
-
const entry =
|
|
1140
|
+
const nextIndex = state.currentIndex + 1;
|
|
1141
|
+
const entry = state.entries[nextIndex];
|
|
1096
1142
|
for (const patch of entry.inversePatches) {
|
|
1097
1143
|
applyPatch(this.target, patch);
|
|
1098
1144
|
}
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
const ttIndex = this.currentIndex + 1 + (N - 1 - M);
|
|
1104
|
-
tt.index = Math.max(0, Math.min(N - 1, ttIndex));
|
|
1105
|
-
}
|
|
1145
|
+
store.set(this.historyAtom, (prev) => ({
|
|
1146
|
+
...prev,
|
|
1147
|
+
currentIndex: nextIndex
|
|
1148
|
+
}));
|
|
1106
1149
|
} finally {
|
|
1107
|
-
this.
|
|
1150
|
+
this.isApplyingHistory = false;
|
|
1108
1151
|
rootNode.$isApplyingHistory = wasApplying;
|
|
1109
1152
|
}
|
|
1110
1153
|
}
|
|
1154
|
+
goTo(index) {
|
|
1155
|
+
const store = getGlobalStore();
|
|
1156
|
+
const state = store.get(this.historyAtom);
|
|
1157
|
+
const maxIdx = state.entries.length;
|
|
1158
|
+
if (index < 0 || index > maxIdx) {
|
|
1159
|
+
return;
|
|
1160
|
+
}
|
|
1161
|
+
const node = getStateTreeNode(this.target);
|
|
1162
|
+
const rootNode = node.getRoot();
|
|
1163
|
+
const wasApplying = rootNode.$isApplyingHistory;
|
|
1164
|
+
rootNode.$isApplyingHistory = true;
|
|
1165
|
+
this.isApplyingHistory = true;
|
|
1166
|
+
try {
|
|
1167
|
+
const targetSnapshot = index === 0 ? state.initialSnapshot : state.entries[index - 1].snapshot;
|
|
1168
|
+
applySnapshot(this.target, targetSnapshot);
|
|
1169
|
+
store.set(this.historyAtom, (prev) => ({
|
|
1170
|
+
...prev,
|
|
1171
|
+
currentIndex: index - 1
|
|
1172
|
+
}));
|
|
1173
|
+
} finally {
|
|
1174
|
+
this.isApplyingHistory = false;
|
|
1175
|
+
rootNode.$isApplyingHistory = wasApplying;
|
|
1176
|
+
}
|
|
1177
|
+
}
|
|
1178
|
+
goBack() {
|
|
1179
|
+
const store = getGlobalStore();
|
|
1180
|
+
const state = store.get(this.historyAtom);
|
|
1181
|
+
const currentSnapshotIndex = state.currentIndex + 1;
|
|
1182
|
+
if (currentSnapshotIndex > 0) {
|
|
1183
|
+
this.goTo(currentSnapshotIndex - 1);
|
|
1184
|
+
}
|
|
1185
|
+
}
|
|
1186
|
+
goForward() {
|
|
1187
|
+
const store = getGlobalStore();
|
|
1188
|
+
const state = store.get(this.historyAtom);
|
|
1189
|
+
const currentSnapshotIndex = state.currentIndex + 1;
|
|
1190
|
+
if (currentSnapshotIndex < state.entries.length) {
|
|
1191
|
+
this.goTo(currentSnapshotIndex + 1);
|
|
1192
|
+
}
|
|
1193
|
+
}
|
|
1194
|
+
record() {
|
|
1195
|
+
const store = getGlobalStore();
|
|
1196
|
+
const state = store.get(this.historyAtom);
|
|
1197
|
+
let entries = state.currentIndex < state.entries.length - 1 ? state.entries.slice(0, state.currentIndex + 1) : [...state.entries];
|
|
1198
|
+
const newEntry = {
|
|
1199
|
+
patches: [],
|
|
1200
|
+
inversePatches: [],
|
|
1201
|
+
timestamp: Date.now(),
|
|
1202
|
+
snapshot: getSnapshot(this.target)
|
|
1203
|
+
};
|
|
1204
|
+
entries.push(newEntry);
|
|
1205
|
+
let newIndex = entries.length - 1;
|
|
1206
|
+
if (entries.length > this.maxHistoryLength) {
|
|
1207
|
+
const excess = entries.length - this.maxHistoryLength;
|
|
1208
|
+
entries = entries.slice(excess);
|
|
1209
|
+
newIndex -= excess;
|
|
1210
|
+
}
|
|
1211
|
+
store.set(this.historyAtom, {
|
|
1212
|
+
...state,
|
|
1213
|
+
entries,
|
|
1214
|
+
currentIndex: newIndex
|
|
1215
|
+
});
|
|
1216
|
+
}
|
|
1217
|
+
getSnapshot(index) {
|
|
1218
|
+
const store = getGlobalStore();
|
|
1219
|
+
const state = store.get(this.historyAtom);
|
|
1220
|
+
if (index < 0 || index > state.entries.length) {
|
|
1221
|
+
throw new Error(`[jotai-state-tree] Invalid snapshot index: ${index}`);
|
|
1222
|
+
}
|
|
1223
|
+
return index === 0 ? state.initialSnapshot : state.entries[index - 1].snapshot;
|
|
1224
|
+
}
|
|
1111
1225
|
clear() {
|
|
1112
|
-
|
|
1113
|
-
this.
|
|
1226
|
+
const store = getGlobalStore();
|
|
1227
|
+
store.set(this.historyAtom, {
|
|
1228
|
+
entries: [],
|
|
1229
|
+
currentIndex: -1,
|
|
1230
|
+
initialSnapshot: getSnapshot(this.target)
|
|
1231
|
+
});
|
|
1114
1232
|
this.currentGroup = [];
|
|
1115
1233
|
this.currentGroupInverse = [];
|
|
1116
1234
|
this.grouping = false;
|
|
@@ -1129,20 +1247,28 @@ var UndoManager = class {
|
|
|
1129
1247
|
this.grouping = false;
|
|
1130
1248
|
this.actionGrouping = false;
|
|
1131
1249
|
if (this.currentGroup.length > 0) {
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1250
|
+
const store = getGlobalStore();
|
|
1251
|
+
store.set(this.historyAtom, (prev) => {
|
|
1252
|
+
let entries = prev.currentIndex < prev.entries.length - 1 ? prev.entries.slice(0, prev.currentIndex + 1) : [...prev.entries];
|
|
1253
|
+
const newEntry = {
|
|
1254
|
+
patches: [...this.currentGroup],
|
|
1255
|
+
inversePatches: [...this.currentGroupInverse],
|
|
1256
|
+
timestamp: Date.now(),
|
|
1257
|
+
snapshot: getSnapshot(this.target)
|
|
1258
|
+
};
|
|
1259
|
+
entries.push(newEntry);
|
|
1260
|
+
let newIndex = entries.length - 1;
|
|
1261
|
+
if (entries.length > this.maxHistoryLength) {
|
|
1262
|
+
const excess = entries.length - this.maxHistoryLength;
|
|
1263
|
+
entries = entries.slice(excess);
|
|
1264
|
+
newIndex -= excess;
|
|
1265
|
+
}
|
|
1266
|
+
return {
|
|
1267
|
+
...prev,
|
|
1268
|
+
entries,
|
|
1269
|
+
currentIndex: newIndex
|
|
1270
|
+
};
|
|
1139
1271
|
});
|
|
1140
|
-
this.currentIndex++;
|
|
1141
|
-
if (this.historyEntries.length > this.options.maxHistoryLength) {
|
|
1142
|
-
const excess = this.historyEntries.length - this.options.maxHistoryLength;
|
|
1143
|
-
this.historyEntries.splice(0, excess);
|
|
1144
|
-
this.currentIndex -= excess;
|
|
1145
|
-
}
|
|
1146
1272
|
}
|
|
1147
1273
|
this.currentGroup = [];
|
|
1148
1274
|
this.currentGroupInverse = [];
|
|
@@ -1156,7 +1282,7 @@ var UndoManager = class {
|
|
|
1156
1282
|
}
|
|
1157
1283
|
}
|
|
1158
1284
|
dispose() {
|
|
1159
|
-
|
|
1285
|
+
historyTrackersRegistry.delete(this.target);
|
|
1160
1286
|
if (this.disposer) {
|
|
1161
1287
|
this.disposer();
|
|
1162
1288
|
this.disposer = null;
|
|
@@ -1165,143 +1291,113 @@ var UndoManager = class {
|
|
|
1165
1291
|
this.actionDisposer();
|
|
1166
1292
|
this.actionDisposer = null;
|
|
1167
1293
|
}
|
|
1168
|
-
this.clear();
|
|
1169
1294
|
}
|
|
1170
1295
|
};
|
|
1171
|
-
function
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
this.snapshots = [];
|
|
1177
|
-
this.index = -1;
|
|
1178
|
-
this.isApplying = false;
|
|
1179
|
-
this.disposer = null;
|
|
1180
|
-
this.actionDisposer = null;
|
|
1181
|
-
this.pendingRecord = false;
|
|
1182
|
-
this.actionGrouping = false;
|
|
1183
|
-
this.target = target;
|
|
1184
|
-
this.maxSnapshots = options.maxSnapshots ?? 50;
|
|
1185
|
-
this.autoRecord = options.autoRecord ?? false;
|
|
1186
|
-
timeTravelManagersRegistry.set(target, this);
|
|
1187
|
-
this.record();
|
|
1188
|
-
if (this.autoRecord) {
|
|
1189
|
-
this.disposer = onPatch(target, () => {
|
|
1190
|
-
if (this.isApplying) return;
|
|
1191
|
-
const node = getStateTreeNode(this.target);
|
|
1192
|
-
if (node.getRoot().$isApplyingHistory) {
|
|
1193
|
-
return;
|
|
1194
|
-
}
|
|
1195
|
-
if (isActionRunning()) {
|
|
1196
|
-
this.pendingRecord = true;
|
|
1197
|
-
if (!this.actionGrouping) {
|
|
1198
|
-
this.actionGrouping = true;
|
|
1199
|
-
Promise.resolve().then(() => {
|
|
1200
|
-
if (this.actionGrouping) {
|
|
1201
|
-
this.commitPendingRecord();
|
|
1202
|
-
}
|
|
1203
|
-
});
|
|
1204
|
-
}
|
|
1205
|
-
} else {
|
|
1206
|
-
this.record();
|
|
1207
|
-
}
|
|
1208
|
-
});
|
|
1209
|
-
this.actionDisposer = onAction(target, () => {
|
|
1210
|
-
const current = getCurrentAction();
|
|
1211
|
-
if (current && !current.parent) {
|
|
1212
|
-
if (this.actionGrouping) {
|
|
1213
|
-
this.commitPendingRecord();
|
|
1214
|
-
}
|
|
1215
|
-
}
|
|
1216
|
-
});
|
|
1217
|
-
}
|
|
1218
|
-
}
|
|
1219
|
-
commitPendingRecord() {
|
|
1220
|
-
this.actionGrouping = false;
|
|
1221
|
-
if (this.pendingRecord) {
|
|
1222
|
-
this.pendingRecord = false;
|
|
1223
|
-
this.record();
|
|
1224
|
-
}
|
|
1225
|
-
}
|
|
1226
|
-
get currentIndex() {
|
|
1227
|
-
return this.index;
|
|
1228
|
-
}
|
|
1229
|
-
get snapshotCount() {
|
|
1230
|
-
return this.snapshots.length;
|
|
1231
|
-
}
|
|
1232
|
-
get canGoBack() {
|
|
1233
|
-
return this.index > 0;
|
|
1234
|
-
}
|
|
1235
|
-
get canGoForward() {
|
|
1236
|
-
return this.index < this.snapshots.length - 1;
|
|
1237
|
-
}
|
|
1238
|
-
record() {
|
|
1239
|
-
if (this.index < this.snapshots.length - 1) {
|
|
1240
|
-
this.snapshots.splice(this.index + 1);
|
|
1241
|
-
}
|
|
1242
|
-
this.snapshots.push(getSnapshot(this.target));
|
|
1243
|
-
this.index++;
|
|
1244
|
-
if (this.snapshots.length > this.maxSnapshots) {
|
|
1245
|
-
const excess = this.snapshots.length - this.maxSnapshots;
|
|
1246
|
-
this.snapshots.splice(0, excess);
|
|
1247
|
-
this.index -= excess;
|
|
1248
|
-
}
|
|
1249
|
-
}
|
|
1250
|
-
goBack() {
|
|
1251
|
-
if (!this.canGoBack) return;
|
|
1252
|
-
this.goTo(this.index - 1);
|
|
1296
|
+
function getOrCreateHistoryTracker(target, options = {}) {
|
|
1297
|
+
let tracker = historyTrackersRegistry.get(target);
|
|
1298
|
+
if (!tracker) {
|
|
1299
|
+
tracker = new HistoryTracker(target, options);
|
|
1300
|
+
historyTrackersRegistry.set(target, tracker);
|
|
1253
1301
|
}
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1302
|
+
return tracker;
|
|
1303
|
+
}
|
|
1304
|
+
function createUndoManager(target, options) {
|
|
1305
|
+
const tracker = getOrCreateHistoryTracker(target, options);
|
|
1306
|
+
tracker.autoRecord = true;
|
|
1307
|
+
const store = getGlobalStore();
|
|
1308
|
+
return {
|
|
1309
|
+
get canUndo() {
|
|
1310
|
+
const state = store.get(tracker.historyAtom);
|
|
1311
|
+
return state.currentIndex >= 0;
|
|
1312
|
+
},
|
|
1313
|
+
get canRedo() {
|
|
1314
|
+
const state = store.get(tracker.historyAtom);
|
|
1315
|
+
return state.currentIndex < state.entries.length - 1;
|
|
1316
|
+
},
|
|
1317
|
+
get undoLevels() {
|
|
1318
|
+
const state = store.get(tracker.historyAtom);
|
|
1319
|
+
return state.currentIndex + 1;
|
|
1320
|
+
},
|
|
1321
|
+
get redoLevels() {
|
|
1322
|
+
const state = store.get(tracker.historyAtom);
|
|
1323
|
+
return state.entries.length - state.currentIndex - 1;
|
|
1324
|
+
},
|
|
1325
|
+
get history() {
|
|
1326
|
+
const state = store.get(tracker.historyAtom);
|
|
1327
|
+
return state.entries;
|
|
1328
|
+
},
|
|
1329
|
+
get historyIndex() {
|
|
1330
|
+
const state = store.get(tracker.historyAtom);
|
|
1331
|
+
return state.currentIndex;
|
|
1332
|
+
},
|
|
1333
|
+
undo() {
|
|
1334
|
+
tracker.undo();
|
|
1335
|
+
},
|
|
1336
|
+
redo() {
|
|
1337
|
+
tracker.redo();
|
|
1338
|
+
},
|
|
1339
|
+
clear() {
|
|
1340
|
+
tracker.clear();
|
|
1341
|
+
},
|
|
1342
|
+
startGroup() {
|
|
1343
|
+
tracker.startGroup();
|
|
1344
|
+
},
|
|
1345
|
+
endGroup() {
|
|
1346
|
+
tracker.endGroup();
|
|
1347
|
+
},
|
|
1348
|
+
withoutUndo(fn) {
|
|
1349
|
+
return tracker.withoutUndo(fn);
|
|
1350
|
+
},
|
|
1351
|
+
dispose() {
|
|
1352
|
+
tracker.dispose();
|
|
1283
1353
|
}
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1354
|
+
};
|
|
1355
|
+
}
|
|
1356
|
+
function createTimeTravelManager(target, options) {
|
|
1357
|
+
const tracker = getOrCreateHistoryTracker(target, options);
|
|
1358
|
+
if (options?.autoRecord) {
|
|
1359
|
+
tracker.autoRecord = true;
|
|
1290
1360
|
}
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
}
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1361
|
+
const store = getGlobalStore();
|
|
1362
|
+
return {
|
|
1363
|
+
get currentIndex() {
|
|
1364
|
+
const state = store.get(tracker.historyAtom);
|
|
1365
|
+
return state.currentIndex + 1;
|
|
1366
|
+
},
|
|
1367
|
+
get snapshotCount() {
|
|
1368
|
+
const state = store.get(tracker.historyAtom);
|
|
1369
|
+
return state.entries.length + 1;
|
|
1370
|
+
},
|
|
1371
|
+
get canGoBack() {
|
|
1372
|
+
const state = store.get(tracker.historyAtom);
|
|
1373
|
+
return state.currentIndex + 1 > 0;
|
|
1374
|
+
},
|
|
1375
|
+
get canGoForward() {
|
|
1376
|
+
const state = store.get(tracker.historyAtom);
|
|
1377
|
+
return state.currentIndex + 1 < state.entries.length + 1 - 1;
|
|
1378
|
+
},
|
|
1379
|
+
record() {
|
|
1380
|
+
tracker.record();
|
|
1381
|
+
},
|
|
1382
|
+
goBack() {
|
|
1383
|
+
tracker.goBack();
|
|
1384
|
+
},
|
|
1385
|
+
goForward() {
|
|
1386
|
+
tracker.goForward();
|
|
1387
|
+
},
|
|
1388
|
+
goTo(index) {
|
|
1389
|
+
tracker.goTo(index);
|
|
1390
|
+
},
|
|
1391
|
+
getSnapshot(index) {
|
|
1392
|
+
return tracker.getSnapshot(index);
|
|
1393
|
+
},
|
|
1394
|
+
clear() {
|
|
1395
|
+
tracker.clear();
|
|
1396
|
+
},
|
|
1397
|
+
dispose() {
|
|
1398
|
+
tracker.dispose();
|
|
1300
1399
|
}
|
|
1301
|
-
}
|
|
1302
|
-
};
|
|
1303
|
-
function createTimeTravelManager(target, options) {
|
|
1304
|
-
return new TimeTravelManager(target, options);
|
|
1400
|
+
};
|
|
1305
1401
|
}
|
|
1306
1402
|
var ActionRecorder = class {
|
|
1307
1403
|
constructor(target) {
|
|
@@ -1443,6 +1539,7 @@ export {
|
|
|
1443
1539
|
freeze,
|
|
1444
1540
|
isFrozen,
|
|
1445
1541
|
unfreeze,
|
|
1542
|
+
getOrCreateHistoryTracker,
|
|
1446
1543
|
createUndoManager,
|
|
1447
1544
|
createTimeTravelManager,
|
|
1448
1545
|
createActionRecorder
|
package/dist/index.d.mts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { I as ISimpleType, a as IType, b as IIdentifierType, c as IIdentifierNumberType, d as ILiteralType, e as IEnumerationType, f as IFrozenType, M as ModelProperties, g as IModelType, h as MixinConfig, i as IMixin, j as IAnyType, k as IArrayType, l as IMapType, m as IOptionalType, n as IMaybeType, o as IMaybeNullType, p as IUnionType, U as UnionOptions, q as ILateType, r as IAnyModelType, R as ReferenceOptions, s as IReferenceType, t as ISafeReferenceType, u as IRefinementType, v as IDisposer, S as SnapshotIn, w as Instance } from './undo-
|
|
2
|
-
export { L as CustomTypeOptions, aG as IActionRecorder, aH as IActionRecording, C as IAnyComplexType, D as IAnyMixin, aE as IHistoryEntry, F as IJsonPatch, y as IMSTArray, z as IMSTMap, G as IReversibleJsonPatch, x as IStateTreeNode, aF as ITimeTravelManager, aC as IUndoManager, aD as IUndoManagerOptions, H as IValidationContext, K as IValidationError, J as IValidationResult, A as ModelInstance, E as ModelSelf, B as SnapshotOut, T as applyPatch, O as applySnapshot, aw as cleanupStaleEntries, ax as clearAllRegistries, aa as clone, aq as cloneDeep, aB as createActionRecorder, aA as createTimeTravelManager, az as createUndoManager, a8 as destroy, a9 as detach, am as findAll, an as findFirst, as as freeze, a2 as getEnv, ag as getGlobalStore, a4 as getIdentifier, af as getMembers, ar as getOrCreatePath, Y as getParent, $ as getParentOfType, a0 as getPath, a1 as getPathParts, av as getRegistryStats, aj as getRelativePath, X as getRoot, N as getSnapshot, ap as getTreeStats, a3 as getType, _ as hasParent, al as haveSameRoot, a5 as isAlive, ak as isAncestor, at as isFrozen, a6 as isRoot, a7 as isStateTreeNode, ao as isValidReference, W as onAction, ay as onLifecycleChange, Q as onPatch, P as onSnapshot, V as recordPatches, ai as resetGlobalStore, ae as resolveIdentifier, ac as resolvePath, ah as setGlobalStore, Z as tryGetParent, ad as tryResolve, au as unfreeze, ab as walk } from './undo-
|
|
1
|
+
import { I as ISimpleType, a as IType, b as IIdentifierType, c as IIdentifierNumberType, d as ILiteralType, e as IEnumerationType, f as IFrozenType, M as ModelProperties, g as IModelType, h as MixinConfig, i as IMixin, j as IAnyType, k as IArrayType, l as IMapType, m as IOptionalType, n as IMaybeType, o as IMaybeNullType, p as IUnionType, U as UnionOptions, q as ILateType, r as IAnyModelType, R as ReferenceOptions, s as IReferenceType, t as ISafeReferenceType, u as IRefinementType, v as IDisposer, S as SnapshotIn, w as Instance } from './undo-BSq-Pomp.mjs';
|
|
2
|
+
export { L as CustomTypeOptions, aG as IActionRecorder, aH as IActionRecording, C as IAnyComplexType, D as IAnyMixin, aE as IHistoryEntry, F as IJsonPatch, y as IMSTArray, z as IMSTMap, G as IReversibleJsonPatch, x as IStateTreeNode, aF as ITimeTravelManager, aC as IUndoManager, aD as IUndoManagerOptions, H as IValidationContext, K as IValidationError, J as IValidationResult, A as ModelInstance, E as ModelSelf, B as SnapshotOut, T as applyPatch, O as applySnapshot, aw as cleanupStaleEntries, ax as clearAllRegistries, aa as clone, aq as cloneDeep, aB as createActionRecorder, aA as createTimeTravelManager, az as createUndoManager, a8 as destroy, a9 as detach, am as findAll, an as findFirst, as as freeze, a2 as getEnv, ag as getGlobalStore, a4 as getIdentifier, af as getMembers, ar as getOrCreatePath, Y as getParent, $ as getParentOfType, a0 as getPath, a1 as getPathParts, av as getRegistryStats, aj as getRelativePath, X as getRoot, N as getSnapshot, ap as getTreeStats, a3 as getType, _ as hasParent, al as haveSameRoot, a5 as isAlive, ak as isAncestor, at as isFrozen, a6 as isRoot, a7 as isStateTreeNode, ao as isValidReference, W as onAction, ay as onLifecycleChange, Q as onPatch, P as onSnapshot, V as recordPatches, ai as resetGlobalStore, ae as resolveIdentifier, ac as resolvePath, ah as setGlobalStore, Z as tryGetParent, ad as tryResolve, au as unfreeze, ab as walk } from './undo-BSq-Pomp.mjs';
|
|
3
3
|
import 'jotai/vanilla/internals';
|
|
4
4
|
import 'jotai';
|
|
5
5
|
|