snice 2.3.0 → 2.5.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/README.md +76 -4
- package/bin/templates/base/src/main.ts +3 -0
- package/bin/templates/base/src/pages/about-page.ts +10 -1
- package/bin/templates/base/src/pages/home-page.ts +10 -1
- package/bin/templates/base/src/router.ts +3 -2
- package/dist/components/drawer/snice-drawer.d.ts +4 -0
- package/dist/components/drawer/snice-drawer.js +58 -23
- package/dist/components/drawer/snice-drawer.js.map +1 -1
- package/dist/components/drawer/snice-drawer.types.d.ts +1 -0
- package/dist/components/layout/snice-layout-blog.d.ts +10 -1
- package/dist/components/layout/snice-layout-blog.js +47 -8
- package/dist/components/layout/snice-layout-blog.js.map +1 -1
- package/dist/components/layout/snice-layout-dashboard.d.ts +12 -1
- package/dist/components/layout/snice-layout-dashboard.js +92 -13
- package/dist/components/layout/snice-layout-dashboard.js.map +1 -1
- package/dist/components/layout/snice-layout-landing.d.ts +10 -1
- package/dist/components/layout/snice-layout-landing.js +47 -8
- package/dist/components/layout/snice-layout-landing.js.map +1 -1
- package/dist/components/layout/snice-layout-sidebar.d.ts +13 -1
- package/dist/components/layout/snice-layout-sidebar.js +69 -28
- package/dist/components/layout/snice-layout-sidebar.js.map +1 -1
- package/dist/components/layout/snice-layout.d.ts +9 -1
- package/dist/components/layout/snice-layout.js +35 -8
- package/dist/components/layout/snice-layout.js.map +1 -1
- package/dist/components/layout/snice-layout.types.d.ts +2 -1
- package/dist/components/nav/snice-nav.d.ts +16 -0
- package/dist/components/nav/snice-nav.js +158 -0
- package/dist/components/nav/snice-nav.js.map +1 -0
- package/dist/components/nav/snice-nav.types.d.ts +11 -0
- package/dist/index.cjs +243 -45
- package/dist/index.cjs.map +1 -1
- package/dist/index.esm.js +242 -42
- package/dist/index.esm.js.map +1 -1
- package/dist/index.iife.js +243 -45
- package/dist/index.iife.js.map +1 -1
- package/dist/symbols.cjs.map +1 -1
- package/dist/symbols.esm.js +8 -2
- package/dist/symbols.esm.js.map +1 -1
- package/dist/transitions.esm.js +1 -1
- package/dist/types/controller.d.ts +1 -1
- package/dist/types/element.d.ts +70 -3
- package/dist/types/events.d.ts +2 -2
- package/dist/types/global.d.ts +1 -1
- package/dist/types/index.d.ts +3 -2
- package/dist/types/observe.d.ts +1 -1
- package/dist/types/request-response.d.ts +2 -2
- package/dist/types/router.d.ts +2 -2
- package/dist/types/symbols.d.ts +5 -0
- package/dist/types/testing.d.ts +1 -1
- package/dist/types/transitions.d.ts +1 -1
- package/dist/types/types/adopted-options.d.ts +4 -0
- package/dist/types/types/app-context.d.ts +81 -0
- package/dist/types/types/guard.d.ts +19 -0
- package/dist/types/types/index.d.ts +23 -17
- package/dist/types/types/moved-options.d.ts +4 -0
- package/dist/types/types/{PageOptions.d.ts → page-options.d.ts} +13 -4
- package/dist/types/types/placard.d.ts +90 -0
- package/dist/types/types/{PropertyOptions.d.ts → property-options.d.ts} +2 -2
- package/dist/types/types/route-params.d.ts +21 -0
- package/dist/types/types/{RouterInstance.d.ts → router-instance.d.ts} +5 -3
- package/dist/types/types/{RouterOptions.d.ts → router-options.d.ts} +1 -1
- package/package.json +3 -3
- /package/dist/types/types/{DispatchOptions.d.ts → dispatch-options.d.ts} +0 -0
- /package/dist/types/types/{IController.d.ts → i-controller.d.ts} +0 -0
- /package/dist/types/types/{ObserveOptions.d.ts → observe-options.d.ts} +0 -0
- /package/dist/types/types/{OnOptions.d.ts → on-options.d.ts} +0 -0
- /package/dist/types/types/{PartOptions.d.ts → part-options.d.ts} +0 -0
- /package/dist/types/types/{PropertyConverter.d.ts → property-converter.d.ts} +0 -0
- /package/dist/types/types/{QueryOptions.d.ts → query-options.d.ts} +0 -0
- /package/dist/types/types/{RequestOptions.d.ts → request-options.d.ts} +0 -0
- /package/dist/types/types/{RespondOptions.d.ts → respond-options.d.ts} +0 -0
- /package/dist/types/types/{SimpleArray.d.ts → simple-array.d.ts} +0 -0
- /package/dist/types/types/{SniceElement.d.ts → snice-element.d.ts} +0 -0
- /package/dist/types/types/{SniceGlobal.d.ts → snice-global.d.ts} +0 -0
- /package/dist/types/types/{Transition.d.ts → transition.d.ts} +0 -0
package/dist/index.iife.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*!
|
|
2
|
-
* snice v2.
|
|
2
|
+
* snice v2.4.0
|
|
3
3
|
* Imperative TypeScript framework for building vanilla web components with decorators, routing, and controllers. No virtual DOM, no build complexity.
|
|
4
4
|
* (c) 2024
|
|
5
5
|
* Released under the MIT License.
|
|
@@ -40,6 +40,7 @@ var Snice = (function (exports) {
|
|
|
40
40
|
const READY_PROMISE = getSymbol('ready-promise');
|
|
41
41
|
const READY_RESOLVE = getSymbol('ready-resolve');
|
|
42
42
|
const CONTROLLER = getSymbol('controller');
|
|
43
|
+
const INITIALIZED = getSymbol('initialized');
|
|
43
44
|
// Event handler symbols
|
|
44
45
|
const ON_HANDLERS = getSymbol('on-handlers');
|
|
45
46
|
// Controller symbols
|
|
@@ -65,11 +66,16 @@ var Snice = (function (exports) {
|
|
|
65
66
|
// Lifecycle symbols
|
|
66
67
|
const READY_HANDLERS = getSymbol('ready-handlers');
|
|
67
68
|
const DISPOSE_HANDLERS = getSymbol('dispose-handlers');
|
|
69
|
+
const MOVED_HANDLERS = getSymbol('moved-handlers');
|
|
70
|
+
const ADOPTED_HANDLERS = getSymbol('adopted-handlers');
|
|
68
71
|
// Observer symbols
|
|
69
72
|
const OBSERVERS = getSymbol('observers');
|
|
70
73
|
// Part symbols
|
|
71
74
|
const PARTS = getSymbol('parts');
|
|
72
75
|
const PART_TIMERS = getSymbol('part-timers');
|
|
76
|
+
// Lifecycle callback timers
|
|
77
|
+
const MOVED_TIMERS = getSymbol('moved-timers');
|
|
78
|
+
const ADOPTED_TIMERS = getSymbol('adopted-timers');
|
|
73
79
|
// Dispatch timing symbols
|
|
74
80
|
const DISPATCH_TIMERS = getSymbol('dispatch-timers');
|
|
75
81
|
|
|
@@ -1124,14 +1130,6 @@ var Snice = (function (exports) {
|
|
|
1124
1130
|
detail: { name: controllerName, controller: controllerInstance }
|
|
1125
1131
|
}));
|
|
1126
1132
|
}
|
|
1127
|
-
/**
|
|
1128
|
-
* Gets the controller instance attached to an element
|
|
1129
|
-
* @param element The element to get the controller from
|
|
1130
|
-
* @returns The controller instance or undefined
|
|
1131
|
-
*/
|
|
1132
|
-
function getController(element) {
|
|
1133
|
-
return element[CONTROLLER_KEY];
|
|
1134
|
-
}
|
|
1135
1133
|
/**
|
|
1136
1134
|
* Enable controller support for native HTML elements
|
|
1137
1135
|
* This sets up a MutationObserver to watch for controller attributes
|
|
@@ -1229,17 +1227,6 @@ var Snice = (function (exports) {
|
|
|
1229
1227
|
// Store observer reference for cleanup if needed
|
|
1230
1228
|
globalThis.sniceNativeControllerObserver = observer;
|
|
1231
1229
|
}
|
|
1232
|
-
/**
|
|
1233
|
-
* Stop watching for native element controllers
|
|
1234
|
-
*/
|
|
1235
|
-
function cleanupNativeElementControllers() {
|
|
1236
|
-
const observer = globalThis.sniceNativeControllerObserver;
|
|
1237
|
-
if (observer) {
|
|
1238
|
-
observer.disconnect();
|
|
1239
|
-
delete globalThis.sniceNativeControllerObserver;
|
|
1240
|
-
delete globalThis.sniceNativeControllersInitialized;
|
|
1241
|
-
}
|
|
1242
|
-
}
|
|
1243
1230
|
|
|
1244
1231
|
/**
|
|
1245
1232
|
* SimpleArray type for arrays that can be safely reflected to attributes
|
|
@@ -1503,6 +1490,24 @@ var Snice = (function (exports) {
|
|
|
1503
1490
|
this[READY_RESOLVE] = resolve;
|
|
1504
1491
|
});
|
|
1505
1492
|
}
|
|
1493
|
+
// Only run initialization logic once, but re-establish handlers on reconnection
|
|
1494
|
+
if (this[INITIALIZED]) {
|
|
1495
|
+
// Re-establish handlers that get cleaned up on disconnect
|
|
1496
|
+
setupEventHandlers(this, this);
|
|
1497
|
+
setupResponseHandlers(this, this);
|
|
1498
|
+
// Re-establish observers that get cleaned up on disconnect
|
|
1499
|
+
try {
|
|
1500
|
+
setupObservers(this, this);
|
|
1501
|
+
}
|
|
1502
|
+
catch (error) {
|
|
1503
|
+
console.error(`Error setting up observers for ${this.tagName} on reconnection:`, error);
|
|
1504
|
+
}
|
|
1505
|
+
// Call user's connectedCallback
|
|
1506
|
+
if (originalConnectedCallback) {
|
|
1507
|
+
originalConnectedCallback.call(this);
|
|
1508
|
+
}
|
|
1509
|
+
return;
|
|
1510
|
+
}
|
|
1506
1511
|
try {
|
|
1507
1512
|
// Initialize properties from attributes before rendering
|
|
1508
1513
|
const properties = constructor[PROPERTIES];
|
|
@@ -1526,8 +1531,6 @@ var Snice = (function (exports) {
|
|
|
1526
1531
|
this[PROPERTIES_INITIALIZED] = true;
|
|
1527
1532
|
// Properties are now stateless and read from DOM attributes only
|
|
1528
1533
|
// Initial values are not automatically reflected
|
|
1529
|
-
// Clean up any existing event handlers first (for reconnection)
|
|
1530
|
-
cleanupEventHandlers(this);
|
|
1531
1534
|
// Create shadow root if it doesn't exist
|
|
1532
1535
|
if (!this.shadowRoot) {
|
|
1533
1536
|
this.attachShadow({ mode: 'open' });
|
|
@@ -1600,6 +1603,8 @@ var Snice = (function (exports) {
|
|
|
1600
1603
|
catch (error) {
|
|
1601
1604
|
console.error(`Error setting up observers for ${this.tagName}:`, error);
|
|
1602
1605
|
}
|
|
1606
|
+
// Mark as initialized
|
|
1607
|
+
this[INITIALIZED] = true;
|
|
1603
1608
|
// NOW call the original user-defined connectedCallback after shadow DOM is set up
|
|
1604
1609
|
if (originalConnectedCallback) {
|
|
1605
1610
|
originalConnectedCallback.call(this);
|
|
@@ -1721,6 +1726,36 @@ var Snice = (function (exports) {
|
|
|
1721
1726
|
}
|
|
1722
1727
|
}
|
|
1723
1728
|
};
|
|
1729
|
+
// Add connectedMoveCallback for handling DOM moves
|
|
1730
|
+
constructor.prototype.connectedMoveCallback = async function () {
|
|
1731
|
+
// Call @moved handlers
|
|
1732
|
+
const movedHandlers = constructor[MOVED_HANDLERS];
|
|
1733
|
+
if (movedHandlers) {
|
|
1734
|
+
for (const handler of movedHandlers) {
|
|
1735
|
+
try {
|
|
1736
|
+
await handler.method.call(this);
|
|
1737
|
+
}
|
|
1738
|
+
catch (error) {
|
|
1739
|
+
console.error(`Error in @moved handler ${handler.methodName}:`, error);
|
|
1740
|
+
}
|
|
1741
|
+
}
|
|
1742
|
+
}
|
|
1743
|
+
};
|
|
1744
|
+
// Add adoptedCallback for handling document adoption
|
|
1745
|
+
constructor.prototype.adoptedCallback = async function () {
|
|
1746
|
+
// Call @adopted handlers
|
|
1747
|
+
const adoptedHandlers = constructor[ADOPTED_HANDLERS];
|
|
1748
|
+
if (adoptedHandlers) {
|
|
1749
|
+
for (const handler of adoptedHandlers) {
|
|
1750
|
+
try {
|
|
1751
|
+
await handler.method.call(this);
|
|
1752
|
+
}
|
|
1753
|
+
catch (error) {
|
|
1754
|
+
console.error(`Error in @adopted handler ${handler.methodName}:`, error);
|
|
1755
|
+
}
|
|
1756
|
+
}
|
|
1757
|
+
}
|
|
1758
|
+
};
|
|
1724
1759
|
}
|
|
1725
1760
|
function element(tagName) {
|
|
1726
1761
|
return function (constructor, context) {
|
|
@@ -2063,6 +2098,148 @@ var Snice = (function (exports) {
|
|
|
2063
2098
|
});
|
|
2064
2099
|
};
|
|
2065
2100
|
}
|
|
2101
|
+
/**
|
|
2102
|
+
* Decorator for methods that should run when element is moved within DOM
|
|
2103
|
+
* Supports debounce and throttle options to control execution timing
|
|
2104
|
+
*/
|
|
2105
|
+
function moved(options = {}) {
|
|
2106
|
+
return function (originalMethod, context) {
|
|
2107
|
+
const methodName = context.name;
|
|
2108
|
+
context.addInitializer(function () {
|
|
2109
|
+
const constructor = this.constructor;
|
|
2110
|
+
if (!constructor[MOVED_HANDLERS]) {
|
|
2111
|
+
constructor[MOVED_HANDLERS] = [];
|
|
2112
|
+
}
|
|
2113
|
+
constructor[MOVED_HANDLERS].push({
|
|
2114
|
+
methodName,
|
|
2115
|
+
method: originalMethod,
|
|
2116
|
+
options
|
|
2117
|
+
});
|
|
2118
|
+
});
|
|
2119
|
+
// Return wrapped method that handles timing options
|
|
2120
|
+
return function (...args) {
|
|
2121
|
+
// Initialize timers storage if not present
|
|
2122
|
+
if (!this[MOVED_TIMERS]) {
|
|
2123
|
+
this[MOVED_TIMERS] = new Map();
|
|
2124
|
+
}
|
|
2125
|
+
// Get or create timers for this specific method
|
|
2126
|
+
if (!this[MOVED_TIMERS].has(methodName)) {
|
|
2127
|
+
this[MOVED_TIMERS].set(methodName, {
|
|
2128
|
+
throttleTimer: null,
|
|
2129
|
+
debounceTimer: null,
|
|
2130
|
+
lastThrottleCall: 0
|
|
2131
|
+
});
|
|
2132
|
+
}
|
|
2133
|
+
const timers = this[MOVED_TIMERS].get(methodName);
|
|
2134
|
+
// Helper function to execute method
|
|
2135
|
+
const executeMethod = (...methodArgs) => {
|
|
2136
|
+
return originalMethod.apply(this, methodArgs);
|
|
2137
|
+
};
|
|
2138
|
+
const hasDebounce = options.debounce !== undefined && options.debounce > 0;
|
|
2139
|
+
const hasThrottle = options.throttle !== undefined && options.throttle > 0;
|
|
2140
|
+
// Handle timing based on priority: debounce > throttle > immediate
|
|
2141
|
+
switch (true) {
|
|
2142
|
+
case hasDebounce: {
|
|
2143
|
+
clearTimeout(timers.debounceTimer);
|
|
2144
|
+
timers.debounceTimer = setTimeout(() => executeMethod(...args), options.debounce);
|
|
2145
|
+
return undefined;
|
|
2146
|
+
}
|
|
2147
|
+
case hasThrottle: {
|
|
2148
|
+
const throttleMs = options.throttle;
|
|
2149
|
+
const now = Date.now();
|
|
2150
|
+
const canExecuteImmediately = timers.lastThrottleCall === 0 || now - timers.lastThrottleCall >= throttleMs;
|
|
2151
|
+
if (canExecuteImmediately) {
|
|
2152
|
+
timers.lastThrottleCall = now;
|
|
2153
|
+
return executeMethod(...args);
|
|
2154
|
+
}
|
|
2155
|
+
const hasScheduledTimer = !!timers.throttleTimer;
|
|
2156
|
+
if (!hasScheduledTimer) {
|
|
2157
|
+
const remainingTime = throttleMs - (now - timers.lastThrottleCall);
|
|
2158
|
+
timers.throttleTimer = setTimeout(() => {
|
|
2159
|
+
timers.throttleTimer = null;
|
|
2160
|
+
timers.lastThrottleCall = Date.now();
|
|
2161
|
+
executeMethod(...args);
|
|
2162
|
+
}, remainingTime);
|
|
2163
|
+
}
|
|
2164
|
+
return undefined;
|
|
2165
|
+
}
|
|
2166
|
+
default:
|
|
2167
|
+
return executeMethod(...args);
|
|
2168
|
+
}
|
|
2169
|
+
};
|
|
2170
|
+
};
|
|
2171
|
+
}
|
|
2172
|
+
/**
|
|
2173
|
+
* Decorator for methods that should run when element is adopted to new document
|
|
2174
|
+
* Supports debounce and throttle options to control execution timing
|
|
2175
|
+
*/
|
|
2176
|
+
function adopted(options = {}) {
|
|
2177
|
+
return function (originalMethod, context) {
|
|
2178
|
+
const methodName = context.name;
|
|
2179
|
+
context.addInitializer(function () {
|
|
2180
|
+
const constructor = this.constructor;
|
|
2181
|
+
if (!constructor[ADOPTED_HANDLERS]) {
|
|
2182
|
+
constructor[ADOPTED_HANDLERS] = [];
|
|
2183
|
+
}
|
|
2184
|
+
constructor[ADOPTED_HANDLERS].push({
|
|
2185
|
+
methodName,
|
|
2186
|
+
method: originalMethod,
|
|
2187
|
+
options
|
|
2188
|
+
});
|
|
2189
|
+
});
|
|
2190
|
+
// Return wrapped method that handles timing options
|
|
2191
|
+
return function (...args) {
|
|
2192
|
+
// Initialize timers storage if not present
|
|
2193
|
+
if (!this[ADOPTED_TIMERS]) {
|
|
2194
|
+
this[ADOPTED_TIMERS] = new Map();
|
|
2195
|
+
}
|
|
2196
|
+
// Get or create timers for this specific method
|
|
2197
|
+
if (!this[ADOPTED_TIMERS].has(methodName)) {
|
|
2198
|
+
this[ADOPTED_TIMERS].set(methodName, {
|
|
2199
|
+
throttleTimer: null,
|
|
2200
|
+
debounceTimer: null,
|
|
2201
|
+
lastThrottleCall: 0
|
|
2202
|
+
});
|
|
2203
|
+
}
|
|
2204
|
+
const timers = this[ADOPTED_TIMERS].get(methodName);
|
|
2205
|
+
// Helper function to execute method
|
|
2206
|
+
const executeMethod = (...methodArgs) => {
|
|
2207
|
+
return originalMethod.apply(this, methodArgs);
|
|
2208
|
+
};
|
|
2209
|
+
const hasDebounce = options.debounce !== undefined && options.debounce > 0;
|
|
2210
|
+
const hasThrottle = options.throttle !== undefined && options.throttle > 0;
|
|
2211
|
+
// Handle timing based on priority: debounce > throttle > immediate
|
|
2212
|
+
switch (true) {
|
|
2213
|
+
case hasDebounce: {
|
|
2214
|
+
clearTimeout(timers.debounceTimer);
|
|
2215
|
+
timers.debounceTimer = setTimeout(() => executeMethod(...args), options.debounce);
|
|
2216
|
+
return undefined;
|
|
2217
|
+
}
|
|
2218
|
+
case hasThrottle: {
|
|
2219
|
+
const throttleMs = options.throttle;
|
|
2220
|
+
const now = Date.now();
|
|
2221
|
+
const canExecuteImmediately = timers.lastThrottleCall === 0 || now - timers.lastThrottleCall >= throttleMs;
|
|
2222
|
+
if (canExecuteImmediately) {
|
|
2223
|
+
timers.lastThrottleCall = now;
|
|
2224
|
+
return executeMethod(...args);
|
|
2225
|
+
}
|
|
2226
|
+
const hasScheduledTimer = !!timers.throttleTimer;
|
|
2227
|
+
if (!hasScheduledTimer) {
|
|
2228
|
+
const remainingTime = throttleMs - (now - timers.lastThrottleCall);
|
|
2229
|
+
timers.throttleTimer = setTimeout(() => {
|
|
2230
|
+
timers.throttleTimer = null;
|
|
2231
|
+
timers.lastThrottleCall = Date.now();
|
|
2232
|
+
executeMethod(...args);
|
|
2233
|
+
}, remainingTime);
|
|
2234
|
+
}
|
|
2235
|
+
return undefined;
|
|
2236
|
+
}
|
|
2237
|
+
default:
|
|
2238
|
+
return executeMethod(...args);
|
|
2239
|
+
}
|
|
2240
|
+
};
|
|
2241
|
+
};
|
|
2242
|
+
}
|
|
2066
2243
|
/**
|
|
2067
2244
|
* Decorator for methods that render specific parts of the template
|
|
2068
2245
|
* Parts are identified by the 'part' attribute in the HTML template
|
|
@@ -2888,6 +3065,7 @@ var Snice = (function (exports) {
|
|
|
2888
3065
|
function Router(options) {
|
|
2889
3066
|
const routes = [];
|
|
2890
3067
|
let is_sorted = false;
|
|
3068
|
+
let placards = []; // Store collected placards
|
|
2891
3069
|
let _404; // the 404 page
|
|
2892
3070
|
let _403; // the 403 forbidden page
|
|
2893
3071
|
let home; // the home page
|
|
@@ -2916,7 +3094,7 @@ var Snice = (function (exports) {
|
|
|
2916
3094
|
function page(pageOptions) {
|
|
2917
3095
|
return function (constructor, context) {
|
|
2918
3096
|
// Transfer metadata from context to constructor (for new decorators)
|
|
2919
|
-
if (context
|
|
3097
|
+
if (context?.metadata && context.metadata[PROPERTIES]) {
|
|
2920
3098
|
if (!constructor[PROPERTIES]) {
|
|
2921
3099
|
constructor[PROPERTIES] = new Map();
|
|
2922
3100
|
}
|
|
@@ -2961,8 +3139,8 @@ var Snice = (function (exports) {
|
|
|
2961
3139
|
};
|
|
2962
3140
|
// Define the custom element
|
|
2963
3141
|
customElements.define(pageOptions.tag, constructor);
|
|
2964
|
-
// Register the routes with guards and
|
|
2965
|
-
pageOptions.routes.forEach(route => register(route, pageOptions.tag, pageOptions.transition, pageOptions.guards, pageOptions.layout));
|
|
3142
|
+
// Register the routes with guards, layout, and placard
|
|
3143
|
+
pageOptions.routes.forEach(route => register(route, pageOptions.tag, pageOptions.transition, pageOptions.guards, pageOptions.layout, pageOptions.placard));
|
|
2966
3144
|
return constructor;
|
|
2967
3145
|
};
|
|
2968
3146
|
}
|
|
@@ -2973,8 +3151,8 @@ var Snice = (function (exports) {
|
|
|
2973
3151
|
* @example
|
|
2974
3152
|
* register('/custom-route', 'custom-element');
|
|
2975
3153
|
*/
|
|
2976
|
-
function register(route, tag, transition, guards, layout) {
|
|
2977
|
-
routes.push({ route: new Route(route), tag, transition, guards, layout });
|
|
3154
|
+
function register(route, tag, transition, guards, layout, placard) {
|
|
3155
|
+
routes.push({ route: new Route(route), tag, transition, guards, layout, placard });
|
|
2978
3156
|
is_sorted = false;
|
|
2979
3157
|
if (route === '/404') {
|
|
2980
3158
|
_404 = tag;
|
|
@@ -3025,10 +3203,28 @@ var Snice = (function (exports) {
|
|
|
3025
3203
|
routes.sort((a, b) => b.route.spec.length - a.route.spec.length);
|
|
3026
3204
|
is_sorted = true;
|
|
3027
3205
|
}
|
|
3206
|
+
// Collect placards from registered routes
|
|
3207
|
+
collectPlacards();
|
|
3028
3208
|
setupEventListeners();
|
|
3029
3209
|
const path = getPath();
|
|
3030
3210
|
navigate(path);
|
|
3031
3211
|
}
|
|
3212
|
+
function collectPlacards() {
|
|
3213
|
+
placards = routes
|
|
3214
|
+
.filter(route => route.placard)
|
|
3215
|
+
.map(route => {
|
|
3216
|
+
const placard = route.placard;
|
|
3217
|
+
return typeof placard === 'function'
|
|
3218
|
+
? placard(context)
|
|
3219
|
+
: placard;
|
|
3220
|
+
});
|
|
3221
|
+
}
|
|
3222
|
+
function updateLayout(layoutElement, currentPath, routeParams) {
|
|
3223
|
+
// Check if layout implements the update method
|
|
3224
|
+
if (typeof layoutElement.update === 'function') {
|
|
3225
|
+
layoutElement.update(context, placards, currentPath, routeParams);
|
|
3226
|
+
}
|
|
3227
|
+
}
|
|
3032
3228
|
function getPath() {
|
|
3033
3229
|
switch (options.type) {
|
|
3034
3230
|
case 'hash':
|
|
@@ -3037,7 +3233,7 @@ var Snice = (function (exports) {
|
|
|
3037
3233
|
return window.location.pathname;
|
|
3038
3234
|
}
|
|
3039
3235
|
}
|
|
3040
|
-
|
|
3236
|
+
function renderForbiddenPage(target) {
|
|
3041
3237
|
let newPageElement;
|
|
3042
3238
|
const has403Page = !!_403;
|
|
3043
3239
|
if (has403Page) {
|
|
@@ -3055,16 +3251,16 @@ var Snice = (function (exports) {
|
|
|
3055
3251
|
currentLayoutName = null;
|
|
3056
3252
|
currentLayoutTimestamp = null;
|
|
3057
3253
|
}
|
|
3058
|
-
|
|
3254
|
+
function checkGuards(guards, params, target) {
|
|
3059
3255
|
const hasGuards = !!guards;
|
|
3060
3256
|
if (!hasGuards) {
|
|
3061
3257
|
return true;
|
|
3062
3258
|
}
|
|
3063
3259
|
const guardsArray = Array.isArray(guards) ? guards : [guards];
|
|
3064
3260
|
for (const guard of guardsArray) {
|
|
3065
|
-
const allowed =
|
|
3261
|
+
const allowed = guard(context, params);
|
|
3066
3262
|
if (!allowed) {
|
|
3067
|
-
|
|
3263
|
+
renderForbiddenPage(target);
|
|
3068
3264
|
return false;
|
|
3069
3265
|
}
|
|
3070
3266
|
}
|
|
@@ -3092,14 +3288,14 @@ var Snice = (function (exports) {
|
|
|
3092
3288
|
div.innerHTML = /*html*/ `<h1>404</h1><p>Page not found</p>`;
|
|
3093
3289
|
return { element: div, transition: undefined, layout: undefined };
|
|
3094
3290
|
}
|
|
3095
|
-
|
|
3291
|
+
function resolveRoute(path, target) {
|
|
3096
3292
|
for (const route of routes) {
|
|
3097
3293
|
const params = route.route.match(path);
|
|
3098
3294
|
const isMatch = params !== false;
|
|
3099
3295
|
if (!isMatch) {
|
|
3100
3296
|
continue;
|
|
3101
3297
|
}
|
|
3102
|
-
const guardsAllowed =
|
|
3298
|
+
const guardsAllowed = checkGuards(route.guards, params, target);
|
|
3103
3299
|
if (!guardsAllowed) {
|
|
3104
3300
|
return { result: RouteResult.GUARDS_FAILED };
|
|
3105
3301
|
}
|
|
@@ -3107,7 +3303,7 @@ var Snice = (function (exports) {
|
|
|
3107
3303
|
newPageElement[ROUTER_CONTEXT] = context;
|
|
3108
3304
|
const routeParams = params;
|
|
3109
3305
|
Object.keys(routeParams).forEach(key => newPageElement.setAttribute(key, routeParams[key]));
|
|
3110
|
-
return { result: RouteResult.SUCCESS, element: newPageElement, transition: route.transition, layout: route.layout };
|
|
3306
|
+
return { result: RouteResult.SUCCESS, element: newPageElement, transition: route.transition, layout: route.layout, routeParams };
|
|
3111
3307
|
}
|
|
3112
3308
|
return { result: RouteResult.NOT_FOUND };
|
|
3113
3309
|
}
|
|
@@ -3144,11 +3340,13 @@ var Snice = (function (exports) {
|
|
|
3144
3340
|
currentLayoutTimestamp = null;
|
|
3145
3341
|
return { element: null, needsNewLayout: true };
|
|
3146
3342
|
}
|
|
3147
|
-
async function renderWithLayout(target, pageElement, transition, layoutElement, needsNewLayout) {
|
|
3343
|
+
async function renderWithLayout(target, pageElement, transition, layoutElement, needsNewLayout, currentPath, routeParams) {
|
|
3148
3344
|
const currentLayout = layoutElement || getCurrentLayoutElement(target);
|
|
3149
3345
|
if (!currentLayout) {
|
|
3150
3346
|
return;
|
|
3151
3347
|
}
|
|
3348
|
+
// Update layout with current context and placards
|
|
3349
|
+
updateLayout(currentLayout, currentPath, routeParams);
|
|
3152
3350
|
const oldPageInLayout = currentLayout.querySelector('[slot="page"]');
|
|
3153
3351
|
const shouldTransition = !!(transition && oldPageInLayout);
|
|
3154
3352
|
if (shouldTransition) {
|
|
@@ -3190,11 +3388,13 @@ var Snice = (function (exports) {
|
|
|
3190
3388
|
if (!target) {
|
|
3191
3389
|
throw new Error(`Target element not found: ${options.target}`);
|
|
3192
3390
|
}
|
|
3391
|
+
// Collect fresh placards before navigation
|
|
3392
|
+
collectPlacards();
|
|
3193
3393
|
window.scrollTo(0, 0);
|
|
3194
3394
|
const isHomePath = (path.trim() === '' || path === '/') && !!home;
|
|
3195
3395
|
if (isHomePath) {
|
|
3196
3396
|
const homeRoute = routes.find(r => r.route.match('/'));
|
|
3197
|
-
const guardsAllowed =
|
|
3397
|
+
const guardsAllowed = checkGuards(homeRoute?.guards, {}, target);
|
|
3198
3398
|
if (!guardsAllowed) {
|
|
3199
3399
|
return;
|
|
3200
3400
|
}
|
|
@@ -3204,26 +3404,26 @@ var Snice = (function (exports) {
|
|
|
3204
3404
|
const finalTransition = transition || options.transition;
|
|
3205
3405
|
const hasLayout = layoutElement !== null || getCurrentLayoutElement(target) !== null;
|
|
3206
3406
|
if (hasLayout) {
|
|
3207
|
-
await renderWithLayout(target, element, finalTransition, layoutElement, needsNewLayout);
|
|
3407
|
+
await renderWithLayout(target, element, finalTransition, layoutElement, needsNewLayout, path, {});
|
|
3208
3408
|
return;
|
|
3209
3409
|
}
|
|
3210
3410
|
await renderDirect(target, element, finalTransition);
|
|
3211
3411
|
return;
|
|
3212
3412
|
}
|
|
3213
|
-
const routeResult =
|
|
3413
|
+
const routeResult = resolveRoute(path, target);
|
|
3214
3414
|
const isGuardsFailed = routeResult.result === RouteResult.GUARDS_FAILED;
|
|
3215
3415
|
if (isGuardsFailed) {
|
|
3216
3416
|
return;
|
|
3217
3417
|
}
|
|
3218
3418
|
const isSuccess = routeResult.result === RouteResult.SUCCESS;
|
|
3219
3419
|
if (isSuccess) {
|
|
3220
|
-
const { element, transition, layout } = routeResult;
|
|
3420
|
+
const { element, transition, layout, routeParams = {} } = routeResult;
|
|
3221
3421
|
const layoutToUse = determineLayout(layout);
|
|
3222
3422
|
const { element: layoutElement, needsNewLayout } = setupLayout(layoutToUse);
|
|
3223
3423
|
const finalTransition = transition || options.transition;
|
|
3224
3424
|
const hasLayout = layoutElement !== null || getCurrentLayoutElement(target) !== null;
|
|
3225
3425
|
if (hasLayout) {
|
|
3226
|
-
await renderWithLayout(target, element, finalTransition, layoutElement, needsNewLayout);
|
|
3426
|
+
await renderWithLayout(target, element, finalTransition, layoutElement, needsNewLayout, path, routeParams);
|
|
3227
3427
|
return;
|
|
3228
3428
|
}
|
|
3229
3429
|
await renderDirect(target, element, finalTransition);
|
|
@@ -3235,7 +3435,7 @@ var Snice = (function (exports) {
|
|
|
3235
3435
|
const finalTransition = transition || options.transition;
|
|
3236
3436
|
const hasLayout = layoutElement !== null || getCurrentLayoutElement(target) !== null;
|
|
3237
3437
|
if (hasLayout) {
|
|
3238
|
-
await renderWithLayout(target, element, finalTransition, layoutElement, needsNewLayout);
|
|
3438
|
+
await renderWithLayout(target, element, finalTransition, layoutElement, needsNewLayout, path, {});
|
|
3239
3439
|
return;
|
|
3240
3440
|
}
|
|
3241
3441
|
await renderDirect(target, element, finalTransition);
|
|
@@ -3254,18 +3454,16 @@ var Snice = (function (exports) {
|
|
|
3254
3454
|
exports.IS_CONTROLLER_INSTANCE = IS_CONTROLLER_INSTANCE;
|
|
3255
3455
|
exports.Router = Router;
|
|
3256
3456
|
exports.SimpleArray = SimpleArray;
|
|
3457
|
+
exports.adopted = adopted;
|
|
3257
3458
|
exports.applyElementFunctionality = applyElementFunctionality;
|
|
3258
|
-
exports.attachController = attachController;
|
|
3259
|
-
exports.cleanupNativeElementControllers = cleanupNativeElementControllers;
|
|
3260
3459
|
exports.context = context;
|
|
3261
3460
|
exports.controller = controller;
|
|
3262
|
-
exports.detachController = detachController;
|
|
3263
3461
|
exports.dispatch = dispatch;
|
|
3264
3462
|
exports.dispose = dispose;
|
|
3265
3463
|
exports.element = element;
|
|
3266
|
-
exports.getController = getController;
|
|
3267
3464
|
exports.getSymbol = getSymbol;
|
|
3268
3465
|
exports.layout = layout;
|
|
3466
|
+
exports.moved = moved;
|
|
3269
3467
|
exports.observe = observe;
|
|
3270
3468
|
exports.on = on;
|
|
3271
3469
|
exports.part = part;
|