elementdrawing 1.0.0
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/LICENSE +21 -0
- package/dist/elementdrawing.min.js +3 -0
- package/dist/elementdrawing.min.js.LICENSE.txt +8 -0
- package/dist/elementdrawing.min.js.map +1 -0
- package/dist/index.html +1 -0
- package/package.json +127 -0
- package/src/core/bridge.h +855 -0
- package/src/core/diff.c +900 -0
- package/src/core/element.c +1078 -0
- package/src/core/event.c +813 -0
- package/src/core/fiber.c +1027 -0
- package/src/core/hooks.c +919 -0
- package/src/core/renderer.c +963 -0
- package/src/core/scheduler.c +702 -0
- package/src/core/state.c +803 -0
- package/src/css/animations.css +779 -0
- package/src/css/base.css +615 -0
- package/src/css/components.css +1311 -0
- package/src/css/tailwind.css +370 -0
- package/src/css/themes.css +517 -0
- package/src/css/utilities.css +475 -0
- package/src/index.js +746 -0
- package/src/js/animation.js +655 -0
- package/src/js/dom.js +665 -0
- package/src/js/events.js +585 -0
- package/src/js/http.js +446 -0
- package/src/js/index.js +26 -0
- package/src/js/router.js +483 -0
- package/src/js/store.js +539 -0
- package/src/js/utils.js +593 -0
- package/src/js/validator.js +529 -0
- package/src/jsx/components/Accordion.jsx +210 -0
- package/src/jsx/components/Alert.jsx +169 -0
- package/src/jsx/components/Avatar.jsx +214 -0
- package/src/jsx/components/Badge.jsx +136 -0
- package/src/jsx/components/Breadcrumb.jsx +200 -0
- package/src/jsx/components/Button.jsx +188 -0
- package/src/jsx/components/Card.jsx +192 -0
- package/src/jsx/components/Carousel.jsx +278 -0
- package/src/jsx/components/Checkbox.jsx +215 -0
- package/src/jsx/components/Dialog.jsx +242 -0
- package/src/jsx/components/Drawer.jsx +190 -0
- package/src/jsx/components/Dropdown.jsx +268 -0
- package/src/jsx/components/Form.jsx +274 -0
- package/src/jsx/components/Input.jsx +285 -0
- package/src/jsx/components/Menu.jsx +276 -0
- package/src/jsx/components/Modal.jsx +274 -0
- package/src/jsx/components/Navbar.jsx +292 -0
- package/src/jsx/components/Pagination.jsx +268 -0
- package/src/jsx/components/Progress.jsx +252 -0
- package/src/jsx/components/Radio.jsx +208 -0
- package/src/jsx/components/Select.jsx +397 -0
- package/src/jsx/components/Sidebar.jsx +250 -0
- package/src/jsx/components/Slider.jsx +310 -0
- package/src/jsx/components/Spinner.jsx +198 -0
- package/src/jsx/components/Switch.jsx +201 -0
- package/src/jsx/components/Table.jsx +332 -0
- package/src/jsx/components/Tabs.jsx +227 -0
- package/src/jsx/components/Textarea.jsx +212 -0
- package/src/jsx/components/Toast.jsx +270 -0
- package/src/jsx/components/Tooltip.jsx +178 -0
- package/src/jsx/components/Typography.jsx +299 -0
- package/src/jsx/components/index.jsx +70 -0
- package/src/jsx/core/element.js +3 -0
- package/src/jsx/hooks/index.js +356 -0
- package/src/jsx/hooks/useCallback.js +472 -0
- package/src/jsx/hooks/useContext.js +586 -0
- package/src/jsx/hooks/useEffect.js +704 -0
- package/src/jsx/hooks/useLayoutEffect.js +508 -0
- package/src/jsx/hooks/useMemo.js +689 -0
- package/src/jsx/hooks/useReducer.js +729 -0
- package/src/jsx/hooks/useRef.js +542 -0
- package/src/jsx/hooks/useState.js +854 -0
- package/src/jsx/runtime/commit.js +903 -0
- package/src/jsx/runtime/createElement.js +860 -0
- package/src/jsx/runtime/index.js +356 -0
- package/src/jsx/runtime/reconcile.js +687 -0
- package/src/jsx/runtime/render.js +914 -0
package/src/js/store.js
ADDED
|
@@ -0,0 +1,539 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* State Management Store
|
|
3
|
+
* ElementDrawing Framework - Store with state, getters, mutations, actions,
|
|
4
|
+
* subscriptions, middleware, persistence, snapshots, modules, and undo/redo.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
'use strict';
|
|
8
|
+
|
|
9
|
+
// ─── Store Class ──────────────────────────────────────────────────────────────
|
|
10
|
+
|
|
11
|
+
function Store(options) {
|
|
12
|
+
options = options || {};
|
|
13
|
+
|
|
14
|
+
// ─── Core State ────────────────────────────────────────────────────────
|
|
15
|
+
this._state = options.state ? deepClone(options.state) : {};
|
|
16
|
+
this._getters = {};
|
|
17
|
+
this._mutations = {};
|
|
18
|
+
this._actions = {};
|
|
19
|
+
this._modules = {};
|
|
20
|
+
this._subscribers = [];
|
|
21
|
+
this._middleware = [];
|
|
22
|
+
this._watchers = {};
|
|
23
|
+
this._strict = options.strict || false;
|
|
24
|
+
this._history = [];
|
|
25
|
+
this._historyIndex = -1;
|
|
26
|
+
this._maxHistory = options.maxHistory || 50;
|
|
27
|
+
this._isCommitting = false;
|
|
28
|
+
|
|
29
|
+
// ─── Persistence Config ────────────────────────────────────────────────
|
|
30
|
+
this._persistence = options.persistence || null;
|
|
31
|
+
|
|
32
|
+
// ─── Register Getters ──────────────────────────────────────────────────
|
|
33
|
+
if (options.getters) {
|
|
34
|
+
Object.keys(options.getters).forEach((key) => {
|
|
35
|
+
this._getters[key] = options.getters[key];
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// ─── Register Mutations ────────────────────────────────────────────────
|
|
40
|
+
if (options.mutations) {
|
|
41
|
+
Object.keys(options.mutations).forEach((key) => {
|
|
42
|
+
this._mutations[key] = options.mutations[key];
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// ─── Register Actions ──────────────────────────────────────────────────
|
|
47
|
+
if (options.actions) {
|
|
48
|
+
Object.keys(options.actions).forEach((key) => {
|
|
49
|
+
this._actions[key] = options.actions[key];
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// ─── Register Modules ──────────────────────────────────────────────────
|
|
54
|
+
if (options.modules) {
|
|
55
|
+
Object.keys(options.modules).forEach((key) => {
|
|
56
|
+
this.registerModule(key, options.modules[key]);
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// ─── Register Middleware ───────────────────────────────────────────────
|
|
61
|
+
if (options.middleware) {
|
|
62
|
+
options.middleware.forEach((m) => this.use(m));
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// ─── Load Persisted State ──────────────────────────────────────────────
|
|
66
|
+
if (this._persistence) {
|
|
67
|
+
this._loadPersistedState();
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// ─── Save Initial Snapshot ─────────────────────────────────────────────
|
|
71
|
+
this._saveSnapshot();
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// ─── State Access ─────────────────────────────────────────────────────────────
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Get the current state (read-only in strict mode).
|
|
78
|
+
* @returns {Object}
|
|
79
|
+
*/
|
|
80
|
+
Store.prototype.getState = function () {
|
|
81
|
+
return this._state;
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Get a nested state value by path.
|
|
86
|
+
* @param {string} path - Dot-notation path (e.g., 'user.profile.name')
|
|
87
|
+
* @returns {*}
|
|
88
|
+
*/
|
|
89
|
+
Store.prototype.get = function (path) {
|
|
90
|
+
return path.split('.').reduce((obj, key) => obj && obj[key], this._state);
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
// ─── Getters ──────────────────────────────────────────────────────────────────
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Get a computed value from a getter.
|
|
97
|
+
* @param {string} name
|
|
98
|
+
* @returns {*}
|
|
99
|
+
*/
|
|
100
|
+
Store.prototype.getGetter = function (name) {
|
|
101
|
+
const getter = this._getters[name];
|
|
102
|
+
if (!getter) {
|
|
103
|
+
console.warn('[Store] Getter not found: ' + name);
|
|
104
|
+
return undefined;
|
|
105
|
+
}
|
|
106
|
+
return getter(this._state, this._getters);
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Register a getter.
|
|
111
|
+
* @param {string} name
|
|
112
|
+
* @param {Function} getter - (state, getters) => value
|
|
113
|
+
*/
|
|
114
|
+
Store.prototype.registerGetter = function (name, getter) {
|
|
115
|
+
this._getters[name] = getter;
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
// ─── Mutations ────────────────────────────────────────────────────────────────
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Commit a mutation to synchronously change state.
|
|
122
|
+
* @param {string} type - Mutation type
|
|
123
|
+
* @param {*} [payload] - Mutation payload
|
|
124
|
+
*/
|
|
125
|
+
Store.prototype.commit = function (type, payload) {
|
|
126
|
+
if (this._strict && !this._isCommitting) {
|
|
127
|
+
console.warn('[Store] Mutations must be committed inside commit() in strict mode');
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
const mutation = this._mutations[type];
|
|
131
|
+
if (!mutation) {
|
|
132
|
+
console.warn('[Store] Unknown mutation type: ' + type);
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// Apply middleware
|
|
137
|
+
for (const mw of this._middleware) {
|
|
138
|
+
if (mw.beforeMutation) {
|
|
139
|
+
const result = mw.beforeMutation(type, payload, this._state);
|
|
140
|
+
if (result === false) return; // Middleware cancelled the mutation
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// Execute mutation
|
|
145
|
+
this._isCommitting = true;
|
|
146
|
+
const prevState = deepClone(this._state);
|
|
147
|
+
try {
|
|
148
|
+
mutation(this._state, payload);
|
|
149
|
+
} catch (error) {
|
|
150
|
+
console.error('[Store] Mutation error (' + type + '):', error);
|
|
151
|
+
this._state = prevState;
|
|
152
|
+
this._isCommitting = false;
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
this._isCommitting = false;
|
|
156
|
+
|
|
157
|
+
// Notify subscribers
|
|
158
|
+
this._subscribers.forEach((sub) => {
|
|
159
|
+
try { sub({ type, payload }, this._state, prevState); }
|
|
160
|
+
catch (e) { console.error('[Store] Subscriber error:', e); }
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
// Apply middleware after
|
|
164
|
+
for (const mw of this._middleware) {
|
|
165
|
+
if (mw.afterMutation) mw.afterMutation(type, payload, this._state, prevState);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// Save snapshot for undo/redo
|
|
169
|
+
this._saveSnapshot();
|
|
170
|
+
|
|
171
|
+
// Persist state
|
|
172
|
+
if (this._persistence) this._persistState();
|
|
173
|
+
|
|
174
|
+
// Notify watchers
|
|
175
|
+
this._notifyWatchers(prevState, this._state);
|
|
176
|
+
};
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* Register a mutation.
|
|
180
|
+
* @param {string} type
|
|
181
|
+
* @param {Function} mutation - (state, payload) => void
|
|
182
|
+
*/
|
|
183
|
+
Store.prototype.registerMutation = function (type, mutation) {
|
|
184
|
+
this._mutations[type] = mutation;
|
|
185
|
+
};
|
|
186
|
+
|
|
187
|
+
// ─── Actions ──────────────────────────────────────────────────────────────────
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* Dispatch an action (can be asynchronous).
|
|
191
|
+
* @param {string} type - Action type
|
|
192
|
+
* @param {*} [payload] - Action payload
|
|
193
|
+
* @returns {Promise}
|
|
194
|
+
*/
|
|
195
|
+
Store.prototype.dispatch = function (type, payload) {
|
|
196
|
+
const action = this._actions[type];
|
|
197
|
+
if (!action) {
|
|
198
|
+
console.warn('[Store] Unknown action type: ' + type);
|
|
199
|
+
return Promise.reject(new Error('Unknown action: ' + type));
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
const context = {
|
|
203
|
+
state: this._state,
|
|
204
|
+
getters: this._getters,
|
|
205
|
+
commit: this.commit.bind(this),
|
|
206
|
+
dispatch: this.dispatch.bind(this),
|
|
207
|
+
get: this.get.bind(this),
|
|
208
|
+
getGetter: this.getGetter.bind(this),
|
|
209
|
+
};
|
|
210
|
+
|
|
211
|
+
// Apply middleware
|
|
212
|
+
for (const mw of this._middleware) {
|
|
213
|
+
if (mw.beforeAction) {
|
|
214
|
+
const result = mw.beforeAction(type, payload, this._state);
|
|
215
|
+
if (result === false) return Promise.resolve(false);
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
try {
|
|
220
|
+
const result = action(context, payload);
|
|
221
|
+
|
|
222
|
+
if (result && typeof result.then === 'function') {
|
|
223
|
+
return result.then((res) => {
|
|
224
|
+
for (const mw of this._middleware) {
|
|
225
|
+
if (mw.afterAction) mw.afterAction(type, payload, this._state);
|
|
226
|
+
}
|
|
227
|
+
return res;
|
|
228
|
+
}).catch((error) => {
|
|
229
|
+
for (const mw of this._middleware) {
|
|
230
|
+
if (mw.actionError) mw.actionError(error, type, payload);
|
|
231
|
+
}
|
|
232
|
+
throw error;
|
|
233
|
+
});
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
for (const mw of this._middleware) {
|
|
237
|
+
if (mw.afterAction) mw.afterAction(type, payload, this._state);
|
|
238
|
+
}
|
|
239
|
+
return Promise.resolve(result);
|
|
240
|
+
} catch (error) {
|
|
241
|
+
for (const mw of this._middleware) {
|
|
242
|
+
if (mw.actionError) mw.actionError(error, type, payload);
|
|
243
|
+
}
|
|
244
|
+
return Promise.reject(error);
|
|
245
|
+
}
|
|
246
|
+
};
|
|
247
|
+
|
|
248
|
+
/**
|
|
249
|
+
* Register an action.
|
|
250
|
+
* @param {string} type
|
|
251
|
+
* @param {Function} action - (context, payload) => result | Promise
|
|
252
|
+
*/
|
|
253
|
+
Store.prototype.registerAction = function (type, action) {
|
|
254
|
+
this._actions[type] = action;
|
|
255
|
+
};
|
|
256
|
+
|
|
257
|
+
// ─── Subscriptions ────────────────────────────────────────────────────────────
|
|
258
|
+
|
|
259
|
+
/**
|
|
260
|
+
* Subscribe to state changes.
|
|
261
|
+
* @param {Function} subscriber - (mutation, state, prevState) => void
|
|
262
|
+
* @returns {Function} Unsubscribe function
|
|
263
|
+
*/
|
|
264
|
+
Store.prototype.subscribe = function (subscriber) {
|
|
265
|
+
this._subscribers.push(subscriber);
|
|
266
|
+
return () => {
|
|
267
|
+
const idx = this._subscribers.indexOf(subscriber);
|
|
268
|
+
if (idx !== -1) this._subscribers.splice(idx, 1);
|
|
269
|
+
};
|
|
270
|
+
};
|
|
271
|
+
|
|
272
|
+
/**
|
|
273
|
+
* Watch a specific state path for changes.
|
|
274
|
+
* @param {string} path - Dot-notation path
|
|
275
|
+
* @param {Function} callback - (newValue, oldValue) => void
|
|
276
|
+
* @returns {Function} Unwatch function
|
|
277
|
+
*/
|
|
278
|
+
Store.prototype.watch = function (path, callback) {
|
|
279
|
+
if (!this._watchers[path]) this._watchers[path] = [];
|
|
280
|
+
this._watchers[path].push(callback);
|
|
281
|
+
return () => {
|
|
282
|
+
const watchers = this._watchers[path];
|
|
283
|
+
if (watchers) {
|
|
284
|
+
const idx = watchers.indexOf(callback);
|
|
285
|
+
if (idx !== -1) watchers.splice(idx, 1);
|
|
286
|
+
}
|
|
287
|
+
};
|
|
288
|
+
};
|
|
289
|
+
|
|
290
|
+
Store.prototype._notifyWatchers = function (prevState, newState) {
|
|
291
|
+
Object.keys(this._watchers).forEach((path) => {
|
|
292
|
+
const oldVal = path.split('.').reduce((obj, key) => obj && obj[key], prevState);
|
|
293
|
+
const newVal = path.split('.').reduce((obj, key) => obj && obj[key], newState);
|
|
294
|
+
if (!Object.is(oldVal, newVal)) {
|
|
295
|
+
this._watchers[path].forEach((cb) => {
|
|
296
|
+
try { cb(newVal, oldVal); } catch (e) { console.error('[Store] Watcher error:', e); }
|
|
297
|
+
});
|
|
298
|
+
}
|
|
299
|
+
});
|
|
300
|
+
};
|
|
301
|
+
|
|
302
|
+
// ─── Middleware ────────────────────────────────────────────────────────────────
|
|
303
|
+
|
|
304
|
+
/**
|
|
305
|
+
* Add middleware to the store.
|
|
306
|
+
* @param {Object} middleware
|
|
307
|
+
* @param {Function} [middleware.beforeMutation]
|
|
308
|
+
* @param {Function} [middleware.afterMutation]
|
|
309
|
+
* @param {Function} [middleware.beforeAction]
|
|
310
|
+
* @param {Function} [middleware.afterAction]
|
|
311
|
+
* @param {Function} [middleware.actionError]
|
|
312
|
+
* @returns {Function} Remove middleware function
|
|
313
|
+
*/
|
|
314
|
+
Store.prototype.use = function (middleware) {
|
|
315
|
+
this._middleware.push(middleware);
|
|
316
|
+
return () => {
|
|
317
|
+
const idx = this._middleware.indexOf(middleware);
|
|
318
|
+
if (idx !== -1) this._middleware.splice(idx, 1);
|
|
319
|
+
};
|
|
320
|
+
};
|
|
321
|
+
|
|
322
|
+
// ─── State Persistence ────────────────────────────────────────────────────────
|
|
323
|
+
|
|
324
|
+
Store.prototype._persistState = function () {
|
|
325
|
+
if (!this._persistence) return;
|
|
326
|
+
try {
|
|
327
|
+
const storage = window[this._persistence.storage || 'localStorage'];
|
|
328
|
+
const key = this._persistence.key || 'ed_store';
|
|
329
|
+
const serialized = (this._persistence.serialize || JSON.stringify)(this._state);
|
|
330
|
+
storage.setItem(key, serialized);
|
|
331
|
+
} catch (error) {
|
|
332
|
+
console.error('[Store] Persistence error:', error);
|
|
333
|
+
}
|
|
334
|
+
};
|
|
335
|
+
|
|
336
|
+
Store.prototype._loadPersistedState = function () {
|
|
337
|
+
if (!this._persistence) return;
|
|
338
|
+
try {
|
|
339
|
+
const storage = window[this._persistence.storage || 'localStorage'];
|
|
340
|
+
const key = this._persistence.key || 'ed_store';
|
|
341
|
+
const serialized = storage.getItem(key);
|
|
342
|
+
if (serialized !== null) {
|
|
343
|
+
const saved = (this._persistence.deserialize || JSON.parse)(serialized);
|
|
344
|
+
Object.assign(this._state, saved);
|
|
345
|
+
}
|
|
346
|
+
} catch (error) {
|
|
347
|
+
console.error('[Store] Load persistence error:', error);
|
|
348
|
+
}
|
|
349
|
+
};
|
|
350
|
+
|
|
351
|
+
// ─── State Snapshot / Restore ─────────────────────────────────────────────────
|
|
352
|
+
|
|
353
|
+
Store.prototype._saveSnapshot = function () {
|
|
354
|
+
// Truncate future history if we're not at the end
|
|
355
|
+
if (this._historyIndex < this._history.length - 1) {
|
|
356
|
+
this._history = this._history.slice(0, this._historyIndex + 1);
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
this._history.push(deepClone(this._state));
|
|
360
|
+
|
|
361
|
+
// Limit history size
|
|
362
|
+
if (this._history.length > this._maxHistory) {
|
|
363
|
+
this._history.shift();
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
this._historyIndex = this._history.length - 1;
|
|
367
|
+
};
|
|
368
|
+
|
|
369
|
+
/**
|
|
370
|
+
* Create a snapshot of the current state.
|
|
371
|
+
* @returns {Object} Snapshot object
|
|
372
|
+
*/
|
|
373
|
+
Store.prototype.snapshot = function () {
|
|
374
|
+
return {
|
|
375
|
+
state: deepClone(this._state),
|
|
376
|
+
timestamp: Date.now(),
|
|
377
|
+
};
|
|
378
|
+
};
|
|
379
|
+
|
|
380
|
+
/**
|
|
381
|
+
* Restore state from a snapshot.
|
|
382
|
+
* @param {Object} snapshot
|
|
383
|
+
*/
|
|
384
|
+
Store.prototype.restore = function (snapshot) {
|
|
385
|
+
if (!snapshot || !snapshot.state) {
|
|
386
|
+
console.warn('[Store] Invalid snapshot');
|
|
387
|
+
return;
|
|
388
|
+
}
|
|
389
|
+
this._state = deepClone(snapshot.state);
|
|
390
|
+
if (this._persistence) this._persistState();
|
|
391
|
+
this._notifyWatchers({}, this._state);
|
|
392
|
+
};
|
|
393
|
+
|
|
394
|
+
// ─── Undo/Redo ────────────────────────────────────────────────────────────────
|
|
395
|
+
|
|
396
|
+
/**
|
|
397
|
+
* Undo the last state change.
|
|
398
|
+
* @returns {boolean} True if undo was possible
|
|
399
|
+
*/
|
|
400
|
+
Store.prototype.undo = function () {
|
|
401
|
+
if (this._historyIndex <= 0) return false;
|
|
402
|
+
this._historyIndex--;
|
|
403
|
+
this._state = deepClone(this._history[this._historyIndex]);
|
|
404
|
+
this._notifyWatchers({}, this._state);
|
|
405
|
+
if (this._persistence) this._persistState();
|
|
406
|
+
return true;
|
|
407
|
+
};
|
|
408
|
+
|
|
409
|
+
/**
|
|
410
|
+
* Redo the last undone state change.
|
|
411
|
+
* @returns {boolean} True if redo was possible
|
|
412
|
+
*/
|
|
413
|
+
Store.prototype.redo = function () {
|
|
414
|
+
if (this._historyIndex >= this._history.length - 1) return false;
|
|
415
|
+
this._historyIndex++;
|
|
416
|
+
this._state = deepClone(this._history[this._historyIndex]);
|
|
417
|
+
this._notifyWatchers({}, this._state);
|
|
418
|
+
if (this._persistence) this._persistState();
|
|
419
|
+
return true;
|
|
420
|
+
};
|
|
421
|
+
|
|
422
|
+
/**
|
|
423
|
+
* Check if undo is available.
|
|
424
|
+
* @returns {boolean}
|
|
425
|
+
*/
|
|
426
|
+
Store.prototype.canUndo = function () { return this._historyIndex > 0; };
|
|
427
|
+
|
|
428
|
+
/**
|
|
429
|
+
* Check if redo is available.
|
|
430
|
+
* @returns {boolean}
|
|
431
|
+
*/
|
|
432
|
+
Store.prototype.canRedo = function () { return this._historyIndex < this._history.length - 1; };
|
|
433
|
+
|
|
434
|
+
// ─── Module System ────────────────────────────────────────────────────────────
|
|
435
|
+
|
|
436
|
+
/**
|
|
437
|
+
* Register a namespaced module.
|
|
438
|
+
* @param {string} name - Module namespace
|
|
439
|
+
* @param {Object} module - Module with state, getters, mutations, actions
|
|
440
|
+
*/
|
|
441
|
+
Store.prototype.registerModule = function (name, module) {
|
|
442
|
+
const namespacedState = module.state ? deepClone(module.state) : {};
|
|
443
|
+
this._state[name] = namespacedState;
|
|
444
|
+
this._modules[name] = module;
|
|
445
|
+
|
|
446
|
+
// Register namespaced getters
|
|
447
|
+
if (module.getters) {
|
|
448
|
+
Object.keys(module.getters).forEach((key) => {
|
|
449
|
+
this._getters[name + '/' + key] = module.getters[key];
|
|
450
|
+
});
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
// Register namespaced mutations
|
|
454
|
+
if (module.mutations) {
|
|
455
|
+
Object.keys(module.mutations).forEach((key) => {
|
|
456
|
+
this._mutations[name + '/' + key] = (state, payload) => {
|
|
457
|
+
module.mutations[key](state[name], payload);
|
|
458
|
+
};
|
|
459
|
+
});
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
// Register namespaced actions
|
|
463
|
+
if (module.actions) {
|
|
464
|
+
Object.keys(module.actions).forEach((key) => {
|
|
465
|
+
this._actions[name + '/' + key] = (context, payload) => {
|
|
466
|
+
const moduleContext = Object.assign({}, context, {
|
|
467
|
+
state: context.state[name],
|
|
468
|
+
commit: (type, p) => context.commit(name + '/' + type, p),
|
|
469
|
+
dispatch: (type, p) => context.dispatch(name + '/' + type, p),
|
|
470
|
+
});
|
|
471
|
+
return module.actions[key](moduleContext, payload);
|
|
472
|
+
};
|
|
473
|
+
});
|
|
474
|
+
}
|
|
475
|
+
};
|
|
476
|
+
|
|
477
|
+
/**
|
|
478
|
+
* Unregister a namespaced module.
|
|
479
|
+
* @param {string} name
|
|
480
|
+
*/
|
|
481
|
+
Store.prototype.unregisterModule = function (name) {
|
|
482
|
+
delete this._state[name];
|
|
483
|
+
delete this._modules[name];
|
|
484
|
+
|
|
485
|
+
// Clean up namespaced getters, mutations, actions
|
|
486
|
+
const prefix = name + '/';
|
|
487
|
+
Object.keys(this._getters).forEach((k) => { if (k.startsWith(prefix)) delete this._getters[k]; });
|
|
488
|
+
Object.keys(this._mutations).forEach((k) => { if (k.startsWith(prefix)) delete this._mutations[k]; });
|
|
489
|
+
Object.keys(this._actions).forEach((k) => { if (k.startsWith(prefix)) delete this._actions[k]; });
|
|
490
|
+
};
|
|
491
|
+
|
|
492
|
+
// ─── DevTools Integration ─────────────────────────────────────────────────────
|
|
493
|
+
|
|
494
|
+
/**
|
|
495
|
+
* Get store information for DevTools.
|
|
496
|
+
* @returns {Object}
|
|
497
|
+
*/
|
|
498
|
+
Store.prototype.getDevToolsInfo = function () {
|
|
499
|
+
return {
|
|
500
|
+
state: deepClone(this._state),
|
|
501
|
+
getters: Object.keys(this._getters),
|
|
502
|
+
mutations: Object.keys(this._mutations),
|
|
503
|
+
actions: Object.keys(this._actions),
|
|
504
|
+
modules: Object.keys(this._modules),
|
|
505
|
+
history: { index: this._historyIndex, length: this._history.length },
|
|
506
|
+
};
|
|
507
|
+
};
|
|
508
|
+
|
|
509
|
+
// ─── Utility ──────────────────────────────────────────────────────────────────
|
|
510
|
+
|
|
511
|
+
function deepClone(obj, cache) {
|
|
512
|
+
if (obj === null || typeof obj !== 'object') return obj;
|
|
513
|
+
if (obj instanceof Date) return new Date(obj.getTime());
|
|
514
|
+
if (obj instanceof RegExp) return new RegExp(obj.source, obj.flags);
|
|
515
|
+
cache = cache || new WeakMap();
|
|
516
|
+
if (cache.has(obj)) return cache.get(obj);
|
|
517
|
+
const clone = Array.isArray(obj) ? [] : {};
|
|
518
|
+
cache.set(obj, clone);
|
|
519
|
+
Object.keys(obj).forEach((k) => { clone[k] = deepClone(obj[k], cache); });
|
|
520
|
+
return clone;
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
// ─── Factory ──────────────────────────────────────────────────────────────────
|
|
524
|
+
|
|
525
|
+
/**
|
|
526
|
+
* Create a new store instance.
|
|
527
|
+
* @param {Object} [options]
|
|
528
|
+
* @returns {Store}
|
|
529
|
+
*/
|
|
530
|
+
function createStore(options) {
|
|
531
|
+
return new Store(options);
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
// ─── Exports ──────────────────────────────────────────────────────────────────
|
|
535
|
+
|
|
536
|
+
module.exports = {
|
|
537
|
+
Store,
|
|
538
|
+
createStore,
|
|
539
|
+
};
|