eleva 1.0.0-rc.6 → 1.0.0-rc.7
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/README.md +133 -4
- package/dist/eleva-plugins.cjs.js +645 -43
- package/dist/eleva-plugins.cjs.js.map +1 -1
- package/dist/eleva-plugins.esm.js +645 -44
- package/dist/eleva-plugins.esm.js.map +1 -1
- package/dist/eleva-plugins.umd.js +645 -43
- package/dist/eleva-plugins.umd.js.map +1 -1
- package/dist/eleva-plugins.umd.min.js +2 -2
- package/dist/eleva-plugins.umd.min.js.map +1 -1
- package/dist/eleva.cjs.js +31 -25
- package/dist/eleva.cjs.js.map +1 -1
- package/dist/eleva.esm.js +31 -25
- package/dist/eleva.esm.js.map +1 -1
- package/dist/eleva.umd.js +31 -25
- package/dist/eleva.umd.js.map +1 -1
- package/dist/eleva.umd.min.js +2 -2
- package/dist/eleva.umd.min.js.map +1 -1
- package/dist/plugins/attr.umd.js +2 -2
- package/dist/plugins/attr.umd.js.map +1 -1
- package/dist/plugins/attr.umd.min.js +1 -1
- package/dist/plugins/attr.umd.min.js.map +1 -1
- package/dist/plugins/props.umd.js +18 -15
- package/dist/plugins/props.umd.js.map +1 -1
- package/dist/plugins/props.umd.min.js +1 -1
- package/dist/plugins/props.umd.min.js.map +1 -1
- package/dist/plugins/router.umd.js +22 -25
- package/dist/plugins/router.umd.js.map +1 -1
- package/dist/plugins/router.umd.min.js +1 -1
- package/dist/plugins/router.umd.min.js.map +1 -1
- package/dist/plugins/store.umd.js +632 -0
- package/dist/plugins/store.umd.js.map +1 -0
- package/dist/plugins/store.umd.min.js +3 -0
- package/dist/plugins/store.umd.min.js.map +1 -0
- package/package.json +4 -4
- package/src/plugins/Store.js +741 -0
- package/src/plugins/index.js +6 -1
- package/types/plugins/Store.d.ts +86 -0
- package/types/plugins/Store.d.ts.map +1 -0
- package/types/plugins/index.d.ts +1 -0
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*! Eleva Plugins v1.0.0-rc.
|
|
1
|
+
/*! Eleva Plugins v1.0.0-rc.7 | MIT License | https://elevajs.com */
|
|
2
2
|
(function (global, factory) {
|
|
3
3
|
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
|
|
4
4
|
typeof define === 'function' && define.amd ? define(['exports'], factory) :
|
|
@@ -133,7 +133,7 @@
|
|
|
133
133
|
if (hasProperty) {
|
|
134
134
|
// Boolean attribute handling
|
|
135
135
|
if (enableBoolean) {
|
|
136
|
-
const isBoolean = typeof oldEl[prop] === "boolean" || descriptor
|
|
136
|
+
const isBoolean = typeof oldEl[prop] === "boolean" || (descriptor == null ? void 0 : descriptor.get) && typeof descriptor.get.call(oldEl) === "boolean";
|
|
137
137
|
if (isBoolean) {
|
|
138
138
|
const boolValue = value !== "false" && (value === "" || value === prop || value === "true");
|
|
139
139
|
oldEl[prop] = boolValue;
|
|
@@ -175,7 +175,7 @@
|
|
|
175
175
|
|
|
176
176
|
// Override the _patchNode method to use our attribute handler
|
|
177
177
|
eleva.renderer._patchNode = function (oldNode, newNode) {
|
|
178
|
-
if (oldNode
|
|
178
|
+
if (oldNode != null && oldNode._eleva_instance) return;
|
|
179
179
|
if (!this._isSameNode(oldNode, newNode)) {
|
|
180
180
|
oldNode.replaceWith(newNode.cloneNode(true));
|
|
181
181
|
return;
|
|
@@ -225,18 +225,16 @@
|
|
|
225
225
|
}
|
|
226
226
|
};
|
|
227
227
|
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
228
|
+
function _extends() {
|
|
229
|
+
return _extends = Object.assign ? Object.assign.bind() : function (n) {
|
|
230
|
+
for (var e = 1; e < arguments.length; e++) {
|
|
231
|
+
var t = arguments[e];
|
|
232
|
+
for (var r in t) ({}).hasOwnProperty.call(t, r) && (n[r] = t[r]);
|
|
233
|
+
}
|
|
234
|
+
return n;
|
|
235
|
+
}, _extends.apply(null, arguments);
|
|
236
|
+
}
|
|
233
237
|
|
|
234
|
-
/**
|
|
235
|
-
* Simple error handler for the core router.
|
|
236
|
-
* Can be overridden by error handling plugins.
|
|
237
|
-
* Provides consistent error formatting and logging for router operations.
|
|
238
|
-
* @private
|
|
239
|
-
*/
|
|
240
238
|
const CoreErrorHandler = {
|
|
241
239
|
/**
|
|
242
240
|
* Handles router errors with basic formatting.
|
|
@@ -345,12 +343,11 @@
|
|
|
345
343
|
this.eleva = eleva;
|
|
346
344
|
|
|
347
345
|
/** @type {RouterOptions} The merged router options. */
|
|
348
|
-
this.options = {
|
|
346
|
+
this.options = _extends({
|
|
349
347
|
mode: "hash",
|
|
350
348
|
queryParam: "view",
|
|
351
|
-
viewSelector: "root"
|
|
352
|
-
|
|
353
|
-
};
|
|
349
|
+
viewSelector: "root"
|
|
350
|
+
}, options);
|
|
354
351
|
|
|
355
352
|
/** @private @type {RouteDefinition[]} The processed list of route definitions. */
|
|
356
353
|
this.routes = this._processRoutes(options.routes || []);
|
|
@@ -414,10 +411,9 @@
|
|
|
414
411
|
const processedRoutes = [];
|
|
415
412
|
for (const route of routes) {
|
|
416
413
|
try {
|
|
417
|
-
processedRoutes.push({
|
|
418
|
-
...route,
|
|
414
|
+
processedRoutes.push(_extends({}, route, {
|
|
419
415
|
segments: this._parsePathIntoSegments(route.path)
|
|
420
|
-
});
|
|
416
|
+
}));
|
|
421
417
|
} catch (error) {
|
|
422
418
|
this.errorHandler.warn(`Invalid path in route definition "${route.path || "undefined"}": ${error.message}`, {
|
|
423
419
|
route,
|
|
@@ -672,13 +668,12 @@
|
|
|
672
668
|
return false;
|
|
673
669
|
}
|
|
674
670
|
}
|
|
675
|
-
const to = {
|
|
676
|
-
...toLocation,
|
|
671
|
+
const to = _extends({}, toLocation, {
|
|
677
672
|
params: toMatch.params,
|
|
678
673
|
meta: toMatch.route.meta || {},
|
|
679
674
|
name: toMatch.route.name,
|
|
680
675
|
matched: toMatch.route
|
|
681
|
-
};
|
|
676
|
+
});
|
|
682
677
|
try {
|
|
683
678
|
// 1. Run all *pre-navigation* guards.
|
|
684
679
|
const canNavigate = await this._runGuards(to, from, toMatch.route);
|
|
@@ -909,7 +904,10 @@
|
|
|
909
904
|
* @returns {Function} A getter function.
|
|
910
905
|
*/
|
|
911
906
|
_createRouteGetter(property, defaultValue) {
|
|
912
|
-
return () =>
|
|
907
|
+
return () => {
|
|
908
|
+
var _this$currentRoute$va, _this$currentRoute$va2;
|
|
909
|
+
return (_this$currentRoute$va = (_this$currentRoute$va2 = this.currentRoute.value) == null ? void 0 : _this$currentRoute$va2[property]) != null ? _this$currentRoute$va : defaultValue;
|
|
910
|
+
};
|
|
913
911
|
}
|
|
914
912
|
|
|
915
913
|
/**
|
|
@@ -921,8 +919,7 @@
|
|
|
921
919
|
_wrapComponent(component) {
|
|
922
920
|
const originalSetup = component.setup;
|
|
923
921
|
const self = this;
|
|
924
|
-
return {
|
|
925
|
-
...component,
|
|
922
|
+
return _extends({}, component, {
|
|
926
923
|
async setup(ctx) {
|
|
927
924
|
ctx.router = {
|
|
928
925
|
navigate: self.navigate.bind(self),
|
|
@@ -947,7 +944,7 @@
|
|
|
947
944
|
};
|
|
948
945
|
return originalSetup ? await originalSetup(ctx) : {};
|
|
949
946
|
}
|
|
950
|
-
};
|
|
947
|
+
});
|
|
951
948
|
}
|
|
952
949
|
|
|
953
950
|
/**
|
|
@@ -1341,12 +1338,6 @@
|
|
|
1341
1338
|
* const result = TemplateEngine.parse(template, data); // Returns: "Hello, World!"
|
|
1342
1339
|
*/
|
|
1343
1340
|
class TemplateEngine {
|
|
1344
|
-
/**
|
|
1345
|
-
* @private {RegExp} Regular expression for matching template expressions in the format {{ expression }}
|
|
1346
|
-
* @type {RegExp}
|
|
1347
|
-
*/
|
|
1348
|
-
static expressionPattern = /\{\{\s*(.*?)\s*\}\}/g;
|
|
1349
|
-
|
|
1350
1341
|
/**
|
|
1351
1342
|
* Parses a template string, replacing expressions with their evaluated values.
|
|
1352
1343
|
* Expressions are evaluated in the provided data context.
|
|
@@ -1385,11 +1376,16 @@
|
|
|
1385
1376
|
if (typeof expression !== "string") return expression;
|
|
1386
1377
|
try {
|
|
1387
1378
|
return new Function("data", `with(data) { return ${expression}; }`)(data);
|
|
1388
|
-
} catch {
|
|
1379
|
+
} catch (_unused) {
|
|
1389
1380
|
return "";
|
|
1390
1381
|
}
|
|
1391
1382
|
}
|
|
1392
1383
|
}
|
|
1384
|
+
/**
|
|
1385
|
+
* @private {RegExp} Regular expression for matching template expressions in the format {{ expression }}
|
|
1386
|
+
* @type {RegExp}
|
|
1387
|
+
*/
|
|
1388
|
+
TemplateEngine.expressionPattern = /\{\{\s*(.*?)\s*\}\}/g;
|
|
1393
1389
|
|
|
1394
1390
|
/**
|
|
1395
1391
|
* @class 🎯 PropsPlugin
|
|
@@ -1725,10 +1721,7 @@
|
|
|
1725
1721
|
});
|
|
1726
1722
|
|
|
1727
1723
|
// Merge signal props with regular props (signal props take precedence)
|
|
1728
|
-
enhancedProps = {
|
|
1729
|
-
...extractedProps,
|
|
1730
|
-
...signalProps
|
|
1731
|
-
};
|
|
1724
|
+
enhancedProps = _extends({}, extractedProps, signalProps);
|
|
1732
1725
|
}
|
|
1733
1726
|
|
|
1734
1727
|
// Create reactive props for non-signal props only
|
|
@@ -1747,10 +1740,7 @@
|
|
|
1747
1740
|
const reactiveNonSignalProps = createReactiveProps(nonSignalProps);
|
|
1748
1741
|
|
|
1749
1742
|
// Merge signal props with reactive non-signal props
|
|
1750
|
-
finalProps = {
|
|
1751
|
-
...reactiveNonSignalProps,
|
|
1752
|
-
...enhancedProps // Signal props take precedence
|
|
1753
|
-
};
|
|
1743
|
+
finalProps = _extends({}, reactiveNonSignalProps, enhancedProps);
|
|
1754
1744
|
}
|
|
1755
1745
|
|
|
1756
1746
|
/** @type {MountResult} */
|
|
@@ -1921,9 +1911,621 @@
|
|
|
1921
1911
|
}
|
|
1922
1912
|
};
|
|
1923
1913
|
|
|
1914
|
+
const StorePlugin = {
|
|
1915
|
+
/**
|
|
1916
|
+
* Unique identifier for the plugin
|
|
1917
|
+
* @type {string}
|
|
1918
|
+
*/
|
|
1919
|
+
name: "store",
|
|
1920
|
+
/**
|
|
1921
|
+
* Plugin version
|
|
1922
|
+
* @type {string}
|
|
1923
|
+
*/
|
|
1924
|
+
version: "1.0.0-rc.1",
|
|
1925
|
+
/**
|
|
1926
|
+
* Plugin description
|
|
1927
|
+
* @type {string}
|
|
1928
|
+
*/
|
|
1929
|
+
description: "Reactive state management for sharing data across the entire Eleva application",
|
|
1930
|
+
/**
|
|
1931
|
+
* Installs the plugin into the Eleva instance
|
|
1932
|
+
*
|
|
1933
|
+
* @param {Object} eleva - The Eleva instance
|
|
1934
|
+
* @param {Object} options - Plugin configuration options
|
|
1935
|
+
* @param {Object} [options.state={}] - Initial state object
|
|
1936
|
+
* @param {Object} [options.actions={}] - Action functions for state mutations
|
|
1937
|
+
* @param {Object} [options.namespaces={}] - Namespaced modules for organizing store
|
|
1938
|
+
* @param {Object} [options.persistence] - Persistence configuration
|
|
1939
|
+
* @param {boolean} [options.persistence.enabled=false] - Enable state persistence
|
|
1940
|
+
* @param {string} [options.persistence.key="eleva-store"] - Storage key
|
|
1941
|
+
* @param {"localStorage" | "sessionStorage"} [options.persistence.storage="localStorage"] - Storage type
|
|
1942
|
+
* @param {Array<string>} [options.persistence.include] - State keys to persist (if not provided, all state is persisted)
|
|
1943
|
+
* @param {Array<string>} [options.persistence.exclude] - State keys to exclude from persistence
|
|
1944
|
+
* @param {boolean} [options.devTools=false] - Enable development tools integration
|
|
1945
|
+
* @param {Function} [options.onError=null] - Error handler function
|
|
1946
|
+
*
|
|
1947
|
+
* @example
|
|
1948
|
+
* // Basic installation
|
|
1949
|
+
* app.use(StorePlugin, {
|
|
1950
|
+
* state: { count: 0, user: null },
|
|
1951
|
+
* actions: {
|
|
1952
|
+
* increment: (state) => state.count.value++,
|
|
1953
|
+
* setUser: (state, user) => state.user.value = user
|
|
1954
|
+
* }
|
|
1955
|
+
* });
|
|
1956
|
+
*
|
|
1957
|
+
* // Advanced installation with persistence and namespaces
|
|
1958
|
+
* app.use(StorePlugin, {
|
|
1959
|
+
* state: { theme: "light" },
|
|
1960
|
+
* namespaces: {
|
|
1961
|
+
* auth: {
|
|
1962
|
+
* state: { user: null, token: null },
|
|
1963
|
+
* actions: {
|
|
1964
|
+
* login: (state, { user, token }) => {
|
|
1965
|
+
* state.user.value = user;
|
|
1966
|
+
* state.token.value = token;
|
|
1967
|
+
* },
|
|
1968
|
+
* logout: (state) => {
|
|
1969
|
+
* state.user.value = null;
|
|
1970
|
+
* state.token.value = null;
|
|
1971
|
+
* }
|
|
1972
|
+
* }
|
|
1973
|
+
* }
|
|
1974
|
+
* },
|
|
1975
|
+
* persistence: {
|
|
1976
|
+
* enabled: true,
|
|
1977
|
+
* include: ["theme", "auth.user"]
|
|
1978
|
+
* }
|
|
1979
|
+
* });
|
|
1980
|
+
*/
|
|
1981
|
+
install(eleva, options = {}) {
|
|
1982
|
+
const {
|
|
1983
|
+
state = {},
|
|
1984
|
+
actions = {},
|
|
1985
|
+
namespaces = {},
|
|
1986
|
+
persistence = {},
|
|
1987
|
+
devTools = false,
|
|
1988
|
+
onError = null
|
|
1989
|
+
} = options;
|
|
1990
|
+
|
|
1991
|
+
/**
|
|
1992
|
+
* Store instance that manages all state and provides the API
|
|
1993
|
+
* @private
|
|
1994
|
+
*/
|
|
1995
|
+
class Store {
|
|
1996
|
+
constructor() {
|
|
1997
|
+
this.state = {};
|
|
1998
|
+
this.actions = {};
|
|
1999
|
+
this.subscribers = new Set();
|
|
2000
|
+
this.mutations = [];
|
|
2001
|
+
this.persistence = _extends({
|
|
2002
|
+
enabled: false,
|
|
2003
|
+
key: "eleva-store",
|
|
2004
|
+
storage: "localStorage",
|
|
2005
|
+
include: null,
|
|
2006
|
+
exclude: null
|
|
2007
|
+
}, persistence);
|
|
2008
|
+
this.devTools = devTools;
|
|
2009
|
+
this.onError = onError;
|
|
2010
|
+
this._initializeState(state, actions);
|
|
2011
|
+
this._initializeNamespaces(namespaces);
|
|
2012
|
+
this._loadPersistedState();
|
|
2013
|
+
this._setupDevTools();
|
|
2014
|
+
}
|
|
2015
|
+
|
|
2016
|
+
/**
|
|
2017
|
+
* Initializes the root state and actions
|
|
2018
|
+
* @private
|
|
2019
|
+
*/
|
|
2020
|
+
_initializeState(initialState, initialActions) {
|
|
2021
|
+
// Create reactive signals for each state property
|
|
2022
|
+
Object.entries(initialState).forEach(([key, value]) => {
|
|
2023
|
+
this.state[key] = new eleva.signal(value);
|
|
2024
|
+
});
|
|
2025
|
+
|
|
2026
|
+
// Set up actions
|
|
2027
|
+
this.actions = _extends({}, initialActions);
|
|
2028
|
+
}
|
|
2029
|
+
|
|
2030
|
+
/**
|
|
2031
|
+
* Initializes namespaced modules
|
|
2032
|
+
* @private
|
|
2033
|
+
*/
|
|
2034
|
+
_initializeNamespaces(namespaces) {
|
|
2035
|
+
Object.entries(namespaces).forEach(([namespace, module]) => {
|
|
2036
|
+
const {
|
|
2037
|
+
state: moduleState = {},
|
|
2038
|
+
actions: moduleActions = {}
|
|
2039
|
+
} = module;
|
|
2040
|
+
|
|
2041
|
+
// Create namespace object if it doesn't exist
|
|
2042
|
+
if (!this.state[namespace]) {
|
|
2043
|
+
this.state[namespace] = {};
|
|
2044
|
+
}
|
|
2045
|
+
if (!this.actions[namespace]) {
|
|
2046
|
+
this.actions[namespace] = {};
|
|
2047
|
+
}
|
|
2048
|
+
|
|
2049
|
+
// Initialize namespaced state
|
|
2050
|
+
Object.entries(moduleState).forEach(([key, value]) => {
|
|
2051
|
+
this.state[namespace][key] = new eleva.signal(value);
|
|
2052
|
+
});
|
|
2053
|
+
|
|
2054
|
+
// Set up namespaced actions
|
|
2055
|
+
this.actions[namespace] = _extends({}, moduleActions);
|
|
2056
|
+
});
|
|
2057
|
+
}
|
|
2058
|
+
|
|
2059
|
+
/**
|
|
2060
|
+
* Loads persisted state from storage
|
|
2061
|
+
* @private
|
|
2062
|
+
*/
|
|
2063
|
+
_loadPersistedState() {
|
|
2064
|
+
if (!this.persistence.enabled || typeof window === "undefined") {
|
|
2065
|
+
return;
|
|
2066
|
+
}
|
|
2067
|
+
try {
|
|
2068
|
+
const storage = window[this.persistence.storage];
|
|
2069
|
+
const persistedData = storage.getItem(this.persistence.key);
|
|
2070
|
+
if (persistedData) {
|
|
2071
|
+
const data = JSON.parse(persistedData);
|
|
2072
|
+
this._applyPersistedData(data);
|
|
2073
|
+
}
|
|
2074
|
+
} catch (error) {
|
|
2075
|
+
if (this.onError) {
|
|
2076
|
+
this.onError(error, "Failed to load persisted state");
|
|
2077
|
+
} else {
|
|
2078
|
+
console.warn("[StorePlugin] Failed to load persisted state:", error);
|
|
2079
|
+
}
|
|
2080
|
+
}
|
|
2081
|
+
}
|
|
2082
|
+
|
|
2083
|
+
/**
|
|
2084
|
+
* Applies persisted data to the current state
|
|
2085
|
+
* @private
|
|
2086
|
+
*/
|
|
2087
|
+
_applyPersistedData(data, currentState = this.state, path = "") {
|
|
2088
|
+
Object.entries(data).forEach(([key, value]) => {
|
|
2089
|
+
const fullPath = path ? `${path}.${key}` : key;
|
|
2090
|
+
if (this._shouldPersist(fullPath)) {
|
|
2091
|
+
if (currentState[key] && typeof currentState[key] === "object" && "value" in currentState[key]) {
|
|
2092
|
+
// This is a signal, update its value
|
|
2093
|
+
currentState[key].value = value;
|
|
2094
|
+
} else if (typeof value === "object" && value !== null && currentState[key]) {
|
|
2095
|
+
// This is a nested object, recurse
|
|
2096
|
+
this._applyPersistedData(value, currentState[key], fullPath);
|
|
2097
|
+
}
|
|
2098
|
+
}
|
|
2099
|
+
});
|
|
2100
|
+
}
|
|
2101
|
+
|
|
2102
|
+
/**
|
|
2103
|
+
* Determines if a state path should be persisted
|
|
2104
|
+
* @private
|
|
2105
|
+
*/
|
|
2106
|
+
_shouldPersist(path) {
|
|
2107
|
+
const {
|
|
2108
|
+
include,
|
|
2109
|
+
exclude
|
|
2110
|
+
} = this.persistence;
|
|
2111
|
+
if (include && include.length > 0) {
|
|
2112
|
+
return include.some(includePath => path.startsWith(includePath));
|
|
2113
|
+
}
|
|
2114
|
+
if (exclude && exclude.length > 0) {
|
|
2115
|
+
return !exclude.some(excludePath => path.startsWith(excludePath));
|
|
2116
|
+
}
|
|
2117
|
+
return true;
|
|
2118
|
+
}
|
|
2119
|
+
|
|
2120
|
+
/**
|
|
2121
|
+
* Saves current state to storage
|
|
2122
|
+
* @private
|
|
2123
|
+
*/
|
|
2124
|
+
_saveState() {
|
|
2125
|
+
if (!this.persistence.enabled || typeof window === "undefined") {
|
|
2126
|
+
return;
|
|
2127
|
+
}
|
|
2128
|
+
try {
|
|
2129
|
+
const storage = window[this.persistence.storage];
|
|
2130
|
+
const dataToSave = this._extractPersistedData();
|
|
2131
|
+
storage.setItem(this.persistence.key, JSON.stringify(dataToSave));
|
|
2132
|
+
} catch (error) {
|
|
2133
|
+
if (this.onError) {
|
|
2134
|
+
this.onError(error, "Failed to save state");
|
|
2135
|
+
} else {
|
|
2136
|
+
console.warn("[StorePlugin] Failed to save state:", error);
|
|
2137
|
+
}
|
|
2138
|
+
}
|
|
2139
|
+
}
|
|
2140
|
+
|
|
2141
|
+
/**
|
|
2142
|
+
* Extracts data that should be persisted
|
|
2143
|
+
* @private
|
|
2144
|
+
*/
|
|
2145
|
+
_extractPersistedData(currentState = this.state, path = "") {
|
|
2146
|
+
const result = {};
|
|
2147
|
+
Object.entries(currentState).forEach(([key, value]) => {
|
|
2148
|
+
const fullPath = path ? `${path}.${key}` : key;
|
|
2149
|
+
if (this._shouldPersist(fullPath)) {
|
|
2150
|
+
if (value && typeof value === "object" && "value" in value) {
|
|
2151
|
+
// This is a signal, extract its value
|
|
2152
|
+
result[key] = value.value;
|
|
2153
|
+
} else if (typeof value === "object" && value !== null) {
|
|
2154
|
+
// This is a nested object, recurse
|
|
2155
|
+
const nestedData = this._extractPersistedData(value, fullPath);
|
|
2156
|
+
if (Object.keys(nestedData).length > 0) {
|
|
2157
|
+
result[key] = nestedData;
|
|
2158
|
+
}
|
|
2159
|
+
}
|
|
2160
|
+
}
|
|
2161
|
+
});
|
|
2162
|
+
return result;
|
|
2163
|
+
}
|
|
2164
|
+
|
|
2165
|
+
/**
|
|
2166
|
+
* Sets up development tools integration
|
|
2167
|
+
* @private
|
|
2168
|
+
*/
|
|
2169
|
+
_setupDevTools() {
|
|
2170
|
+
if (!this.devTools || typeof window === "undefined" || !window.__ELEVA_DEVTOOLS__) {
|
|
2171
|
+
return;
|
|
2172
|
+
}
|
|
2173
|
+
window.__ELEVA_DEVTOOLS__.registerStore(this);
|
|
2174
|
+
}
|
|
2175
|
+
|
|
2176
|
+
/**
|
|
2177
|
+
* Dispatches an action to mutate the state
|
|
2178
|
+
* @param {string} actionName - The name of the action to dispatch (supports namespaced actions like "auth.login")
|
|
2179
|
+
* @param {any} payload - The payload to pass to the action
|
|
2180
|
+
* @returns {Promise<any>} The result of the action
|
|
2181
|
+
*/
|
|
2182
|
+
async dispatch(actionName, payload) {
|
|
2183
|
+
try {
|
|
2184
|
+
const action = this._getAction(actionName);
|
|
2185
|
+
if (!action) {
|
|
2186
|
+
const error = new Error(`Action "${actionName}" not found`);
|
|
2187
|
+
if (this.onError) {
|
|
2188
|
+
this.onError(error, actionName);
|
|
2189
|
+
}
|
|
2190
|
+
throw error;
|
|
2191
|
+
}
|
|
2192
|
+
const mutation = {
|
|
2193
|
+
type: actionName,
|
|
2194
|
+
payload,
|
|
2195
|
+
timestamp: Date.now()
|
|
2196
|
+
};
|
|
2197
|
+
|
|
2198
|
+
// Record mutation for devtools
|
|
2199
|
+
this.mutations.push(mutation);
|
|
2200
|
+
if (this.mutations.length > 100) {
|
|
2201
|
+
this.mutations.shift(); // Keep only last 100 mutations
|
|
2202
|
+
}
|
|
2203
|
+
|
|
2204
|
+
// Execute the action
|
|
2205
|
+
const result = await action.call(null, this.state, payload);
|
|
2206
|
+
|
|
2207
|
+
// Save state if persistence is enabled
|
|
2208
|
+
this._saveState();
|
|
2209
|
+
|
|
2210
|
+
// Notify subscribers
|
|
2211
|
+
this.subscribers.forEach(callback => {
|
|
2212
|
+
try {
|
|
2213
|
+
callback(mutation, this.state);
|
|
2214
|
+
} catch (error) {
|
|
2215
|
+
if (this.onError) {
|
|
2216
|
+
this.onError(error, "Subscriber callback failed");
|
|
2217
|
+
}
|
|
2218
|
+
}
|
|
2219
|
+
});
|
|
2220
|
+
|
|
2221
|
+
// Notify devtools
|
|
2222
|
+
if (this.devTools && typeof window !== "undefined" && window.__ELEVA_DEVTOOLS__) {
|
|
2223
|
+
window.__ELEVA_DEVTOOLS__.notifyMutation(mutation, this.state);
|
|
2224
|
+
}
|
|
2225
|
+
return result;
|
|
2226
|
+
} catch (error) {
|
|
2227
|
+
if (this.onError) {
|
|
2228
|
+
this.onError(error, `Action dispatch failed: ${actionName}`);
|
|
2229
|
+
}
|
|
2230
|
+
throw error;
|
|
2231
|
+
}
|
|
2232
|
+
}
|
|
2233
|
+
|
|
2234
|
+
/**
|
|
2235
|
+
* Gets an action by name (supports namespaced actions)
|
|
2236
|
+
* @private
|
|
2237
|
+
*/
|
|
2238
|
+
_getAction(actionName) {
|
|
2239
|
+
const parts = actionName.split(".");
|
|
2240
|
+
let current = this.actions;
|
|
2241
|
+
for (const part of parts) {
|
|
2242
|
+
if (current[part] === undefined) {
|
|
2243
|
+
return null;
|
|
2244
|
+
}
|
|
2245
|
+
current = current[part];
|
|
2246
|
+
}
|
|
2247
|
+
return typeof current === "function" ? current : null;
|
|
2248
|
+
}
|
|
2249
|
+
|
|
2250
|
+
/**
|
|
2251
|
+
* Subscribes to store mutations
|
|
2252
|
+
* @param {Function} callback - Callback function to call on mutations
|
|
2253
|
+
* @returns {Function} Unsubscribe function
|
|
2254
|
+
*/
|
|
2255
|
+
subscribe(callback) {
|
|
2256
|
+
if (typeof callback !== "function") {
|
|
2257
|
+
throw new Error("Subscribe callback must be a function");
|
|
2258
|
+
}
|
|
2259
|
+
this.subscribers.add(callback);
|
|
2260
|
+
|
|
2261
|
+
// Return unsubscribe function
|
|
2262
|
+
return () => {
|
|
2263
|
+
this.subscribers.delete(callback);
|
|
2264
|
+
};
|
|
2265
|
+
}
|
|
2266
|
+
|
|
2267
|
+
/**
|
|
2268
|
+
* Gets a deep copy of the current state values (not signals)
|
|
2269
|
+
* @returns {Object} The current state values
|
|
2270
|
+
*/
|
|
2271
|
+
getState() {
|
|
2272
|
+
return this._extractPersistedData();
|
|
2273
|
+
}
|
|
2274
|
+
|
|
2275
|
+
/**
|
|
2276
|
+
* Replaces the entire state (useful for testing or state hydration)
|
|
2277
|
+
* @param {Object} newState - The new state object
|
|
2278
|
+
*/
|
|
2279
|
+
replaceState(newState) {
|
|
2280
|
+
this._applyPersistedData(newState);
|
|
2281
|
+
this._saveState();
|
|
2282
|
+
}
|
|
2283
|
+
|
|
2284
|
+
/**
|
|
2285
|
+
* Clears persisted state from storage
|
|
2286
|
+
*/
|
|
2287
|
+
clearPersistedState() {
|
|
2288
|
+
if (!this.persistence.enabled || typeof window === "undefined") {
|
|
2289
|
+
return;
|
|
2290
|
+
}
|
|
2291
|
+
try {
|
|
2292
|
+
const storage = window[this.persistence.storage];
|
|
2293
|
+
storage.removeItem(this.persistence.key);
|
|
2294
|
+
} catch (error) {
|
|
2295
|
+
if (this.onError) {
|
|
2296
|
+
this.onError(error, "Failed to clear persisted state");
|
|
2297
|
+
}
|
|
2298
|
+
}
|
|
2299
|
+
}
|
|
2300
|
+
|
|
2301
|
+
/**
|
|
2302
|
+
* Registers a new namespaced module at runtime
|
|
2303
|
+
* @param {string} namespace - The namespace for the module
|
|
2304
|
+
* @param {Object} module - The module definition
|
|
2305
|
+
* @param {Object} module.state - The module's initial state
|
|
2306
|
+
* @param {Object} module.actions - The module's actions
|
|
2307
|
+
*/
|
|
2308
|
+
registerModule(namespace, module) {
|
|
2309
|
+
if (this.state[namespace] || this.actions[namespace]) {
|
|
2310
|
+
console.warn(`[StorePlugin] Module "${namespace}" already exists`);
|
|
2311
|
+
return;
|
|
2312
|
+
}
|
|
2313
|
+
|
|
2314
|
+
// Initialize the module
|
|
2315
|
+
this.state[namespace] = {};
|
|
2316
|
+
this.actions[namespace] = {};
|
|
2317
|
+
const namespaces = {
|
|
2318
|
+
[namespace]: module
|
|
2319
|
+
};
|
|
2320
|
+
this._initializeNamespaces(namespaces);
|
|
2321
|
+
this._saveState();
|
|
2322
|
+
}
|
|
2323
|
+
|
|
2324
|
+
/**
|
|
2325
|
+
* Unregisters a namespaced module
|
|
2326
|
+
* @param {string} namespace - The namespace to unregister
|
|
2327
|
+
*/
|
|
2328
|
+
unregisterModule(namespace) {
|
|
2329
|
+
if (!this.state[namespace] && !this.actions[namespace]) {
|
|
2330
|
+
console.warn(`[StorePlugin] Module "${namespace}" does not exist`);
|
|
2331
|
+
return;
|
|
2332
|
+
}
|
|
2333
|
+
delete this.state[namespace];
|
|
2334
|
+
delete this.actions[namespace];
|
|
2335
|
+
this._saveState();
|
|
2336
|
+
}
|
|
2337
|
+
|
|
2338
|
+
/**
|
|
2339
|
+
* Creates a new reactive state property at runtime
|
|
2340
|
+
* @param {string} key - The state key
|
|
2341
|
+
* @param {*} initialValue - The initial value
|
|
2342
|
+
* @returns {Object} The created signal
|
|
2343
|
+
*/
|
|
2344
|
+
createState(key, initialValue) {
|
|
2345
|
+
if (this.state[key]) {
|
|
2346
|
+
return this.state[key]; // Return existing state
|
|
2347
|
+
}
|
|
2348
|
+
this.state[key] = new eleva.signal(initialValue);
|
|
2349
|
+
this._saveState();
|
|
2350
|
+
return this.state[key];
|
|
2351
|
+
}
|
|
2352
|
+
|
|
2353
|
+
/**
|
|
2354
|
+
* Creates a new action at runtime
|
|
2355
|
+
* @param {string} name - The action name
|
|
2356
|
+
* @param {Function} actionFn - The action function
|
|
2357
|
+
*/
|
|
2358
|
+
createAction(name, actionFn) {
|
|
2359
|
+
if (typeof actionFn !== "function") {
|
|
2360
|
+
throw new Error("Action must be a function");
|
|
2361
|
+
}
|
|
2362
|
+
this.actions[name] = actionFn;
|
|
2363
|
+
}
|
|
2364
|
+
}
|
|
2365
|
+
|
|
2366
|
+
// Create the store instance
|
|
2367
|
+
const store = new Store();
|
|
2368
|
+
|
|
2369
|
+
// Store the original mount method to override it
|
|
2370
|
+
const originalMount = eleva.mount;
|
|
2371
|
+
|
|
2372
|
+
/**
|
|
2373
|
+
* Override the mount method to inject store context into components
|
|
2374
|
+
*/
|
|
2375
|
+
eleva.mount = async (container, compName, props = {}) => {
|
|
2376
|
+
// Get the component definition
|
|
2377
|
+
const componentDef = typeof compName === "string" ? eleva._components.get(compName) || compName : compName;
|
|
2378
|
+
if (!componentDef) {
|
|
2379
|
+
return await originalMount.call(eleva, container, compName, props);
|
|
2380
|
+
}
|
|
2381
|
+
|
|
2382
|
+
// Create a wrapped component that injects store into setup
|
|
2383
|
+
const wrappedComponent = _extends({}, componentDef, {
|
|
2384
|
+
async setup(ctx) {
|
|
2385
|
+
// Inject store into the context with enhanced API
|
|
2386
|
+
ctx.store = {
|
|
2387
|
+
// Core store functionality
|
|
2388
|
+
state: store.state,
|
|
2389
|
+
dispatch: store.dispatch.bind(store),
|
|
2390
|
+
subscribe: store.subscribe.bind(store),
|
|
2391
|
+
getState: store.getState.bind(store),
|
|
2392
|
+
// Module management
|
|
2393
|
+
registerModule: store.registerModule.bind(store),
|
|
2394
|
+
unregisterModule: store.unregisterModule.bind(store),
|
|
2395
|
+
// Utilities for dynamic state/action creation
|
|
2396
|
+
createState: store.createState.bind(store),
|
|
2397
|
+
createAction: store.createAction.bind(store),
|
|
2398
|
+
// Access to signal constructor for manual state creation
|
|
2399
|
+
signal: eleva.signal
|
|
2400
|
+
};
|
|
2401
|
+
|
|
2402
|
+
// Call original setup if it exists
|
|
2403
|
+
const originalSetup = componentDef.setup;
|
|
2404
|
+
const result = originalSetup ? await originalSetup(ctx) : {};
|
|
2405
|
+
return result;
|
|
2406
|
+
}
|
|
2407
|
+
});
|
|
2408
|
+
|
|
2409
|
+
// Call original mount with wrapped component
|
|
2410
|
+
return await originalMount.call(eleva, container, wrappedComponent, props);
|
|
2411
|
+
};
|
|
2412
|
+
|
|
2413
|
+
// Override _mountComponents to ensure child components also get store context
|
|
2414
|
+
const originalMountComponents = eleva._mountComponents;
|
|
2415
|
+
eleva._mountComponents = async (container, children, childInstances) => {
|
|
2416
|
+
// Create wrapped children with store injection
|
|
2417
|
+
const wrappedChildren = {};
|
|
2418
|
+
for (const [selector, childComponent] of Object.entries(children)) {
|
|
2419
|
+
const componentDef = typeof childComponent === "string" ? eleva._components.get(childComponent) || childComponent : childComponent;
|
|
2420
|
+
if (componentDef && typeof componentDef === "object") {
|
|
2421
|
+
wrappedChildren[selector] = _extends({}, componentDef, {
|
|
2422
|
+
async setup(ctx) {
|
|
2423
|
+
// Inject store into the context with enhanced API
|
|
2424
|
+
ctx.store = {
|
|
2425
|
+
// Core store functionality
|
|
2426
|
+
state: store.state,
|
|
2427
|
+
dispatch: store.dispatch.bind(store),
|
|
2428
|
+
subscribe: store.subscribe.bind(store),
|
|
2429
|
+
getState: store.getState.bind(store),
|
|
2430
|
+
// Module management
|
|
2431
|
+
registerModule: store.registerModule.bind(store),
|
|
2432
|
+
unregisterModule: store.unregisterModule.bind(store),
|
|
2433
|
+
// Utilities for dynamic state/action creation
|
|
2434
|
+
createState: store.createState.bind(store),
|
|
2435
|
+
createAction: store.createAction.bind(store),
|
|
2436
|
+
// Access to signal constructor for manual state creation
|
|
2437
|
+
signal: eleva.signal
|
|
2438
|
+
};
|
|
2439
|
+
|
|
2440
|
+
// Call original setup if it exists
|
|
2441
|
+
const originalSetup = componentDef.setup;
|
|
2442
|
+
const result = originalSetup ? await originalSetup(ctx) : {};
|
|
2443
|
+
return result;
|
|
2444
|
+
}
|
|
2445
|
+
});
|
|
2446
|
+
} else {
|
|
2447
|
+
wrappedChildren[selector] = childComponent;
|
|
2448
|
+
}
|
|
2449
|
+
}
|
|
2450
|
+
|
|
2451
|
+
// Call original _mountComponents with wrapped children
|
|
2452
|
+
return await originalMountComponents.call(eleva, container, wrappedChildren, childInstances);
|
|
2453
|
+
};
|
|
2454
|
+
|
|
2455
|
+
// Expose store instance and utilities on the Eleva instance
|
|
2456
|
+
eleva.store = store;
|
|
2457
|
+
|
|
2458
|
+
/**
|
|
2459
|
+
* Expose utility methods on the Eleva instance
|
|
2460
|
+
* @namespace eleva.store
|
|
2461
|
+
*/
|
|
2462
|
+
eleva.createAction = (name, actionFn) => {
|
|
2463
|
+
store.actions[name] = actionFn;
|
|
2464
|
+
};
|
|
2465
|
+
eleva.dispatch = (actionName, payload) => {
|
|
2466
|
+
return store.dispatch(actionName, payload);
|
|
2467
|
+
};
|
|
2468
|
+
eleva.getState = () => {
|
|
2469
|
+
return store.getState();
|
|
2470
|
+
};
|
|
2471
|
+
eleva.subscribe = callback => {
|
|
2472
|
+
return store.subscribe(callback);
|
|
2473
|
+
};
|
|
2474
|
+
|
|
2475
|
+
// Store original methods for cleanup
|
|
2476
|
+
eleva._originalMount = originalMount;
|
|
2477
|
+
eleva._originalMountComponents = originalMountComponents;
|
|
2478
|
+
},
|
|
2479
|
+
/**
|
|
2480
|
+
* Uninstalls the plugin from the Eleva instance
|
|
2481
|
+
*
|
|
2482
|
+
* @param {Object} eleva - The Eleva instance
|
|
2483
|
+
*
|
|
2484
|
+
* @description
|
|
2485
|
+
* Restores the original Eleva methods and removes all plugin-specific
|
|
2486
|
+
* functionality. This method should be called when the plugin is no
|
|
2487
|
+
* longer needed.
|
|
2488
|
+
*
|
|
2489
|
+
* @example
|
|
2490
|
+
* // Uninstall the plugin
|
|
2491
|
+
* StorePlugin.uninstall(app);
|
|
2492
|
+
*/
|
|
2493
|
+
uninstall(eleva) {
|
|
2494
|
+
// Restore original mount method
|
|
2495
|
+
if (eleva._originalMount) {
|
|
2496
|
+
eleva.mount = eleva._originalMount;
|
|
2497
|
+
delete eleva._originalMount;
|
|
2498
|
+
}
|
|
2499
|
+
|
|
2500
|
+
// Restore original _mountComponents method
|
|
2501
|
+
if (eleva._originalMountComponents) {
|
|
2502
|
+
eleva._mountComponents = eleva._originalMountComponents;
|
|
2503
|
+
delete eleva._originalMountComponents;
|
|
2504
|
+
}
|
|
2505
|
+
|
|
2506
|
+
// Remove store instance and utility methods
|
|
2507
|
+
if (eleva.store) {
|
|
2508
|
+
delete eleva.store;
|
|
2509
|
+
}
|
|
2510
|
+
if (eleva.createAction) {
|
|
2511
|
+
delete eleva.createAction;
|
|
2512
|
+
}
|
|
2513
|
+
if (eleva.dispatch) {
|
|
2514
|
+
delete eleva.dispatch;
|
|
2515
|
+
}
|
|
2516
|
+
if (eleva.getState) {
|
|
2517
|
+
delete eleva.getState;
|
|
2518
|
+
}
|
|
2519
|
+
if (eleva.subscribe) {
|
|
2520
|
+
delete eleva.subscribe;
|
|
2521
|
+
}
|
|
2522
|
+
}
|
|
2523
|
+
};
|
|
2524
|
+
|
|
1924
2525
|
exports.Attr = AttrPlugin;
|
|
1925
2526
|
exports.Props = PropsPlugin;
|
|
1926
2527
|
exports.Router = RouterPlugin;
|
|
2528
|
+
exports.Store = StorePlugin;
|
|
1927
2529
|
|
|
1928
2530
|
}));
|
|
1929
2531
|
//# sourceMappingURL=eleva-plugins.umd.js.map
|