@servlyadmin/runtime-core 0.1.6 → 0.1.8
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-IWFVKY5N.js +154 -0
- package/dist/index.cjs +2136 -160
- package/dist/index.d.cts +631 -2
- package/dist/index.d.ts +631 -2
- package/dist/index.js +1955 -178
- package/dist/tailwind-CGAHPC3O.js +24 -0
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -20,6 +20,167 @@ var __copyProps = (to, from, except, desc) => {
|
|
|
20
20
|
};
|
|
21
21
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
22
22
|
|
|
23
|
+
// src/tailwind.ts
|
|
24
|
+
var tailwind_exports = {};
|
|
25
|
+
__export(tailwind_exports, {
|
|
26
|
+
DEFAULT_SERVLY_TAILWIND_CONFIG: () => DEFAULT_SERVLY_TAILWIND_CONFIG,
|
|
27
|
+
addCustomStyles: () => addCustomStyles,
|
|
28
|
+
default: () => tailwind_default,
|
|
29
|
+
getTailwind: () => getTailwind,
|
|
30
|
+
initServlyTailwind: () => initServlyTailwind,
|
|
31
|
+
injectTailwind: () => injectTailwind,
|
|
32
|
+
isTailwindLoaded: () => isTailwindLoaded,
|
|
33
|
+
removeCustomStyles: () => removeCustomStyles,
|
|
34
|
+
removeTailwind: () => removeTailwind,
|
|
35
|
+
updateTailwindConfig: () => updateTailwindConfig
|
|
36
|
+
});
|
|
37
|
+
function injectTailwind(config = {}) {
|
|
38
|
+
return new Promise((resolve, reject) => {
|
|
39
|
+
if (tailwindInjected && tailwindScript) {
|
|
40
|
+
resolve();
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
if (typeof document === "undefined") {
|
|
44
|
+
resolve();
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
if (window.tailwind) {
|
|
48
|
+
tailwindInjected = true;
|
|
49
|
+
config.onReady?.();
|
|
50
|
+
resolve();
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
const {
|
|
54
|
+
cdnUrl = DEFAULT_TAILWIND_CDN,
|
|
55
|
+
config: tailwindConfig,
|
|
56
|
+
plugins = [],
|
|
57
|
+
usePlayCdn = false,
|
|
58
|
+
onReady,
|
|
59
|
+
onError
|
|
60
|
+
} = config;
|
|
61
|
+
const script = document.createElement("script");
|
|
62
|
+
script.src = usePlayCdn ? `${cdnUrl}?plugins=forms,typography,aspect-ratio` : cdnUrl;
|
|
63
|
+
script.async = true;
|
|
64
|
+
script.onload = () => {
|
|
65
|
+
tailwindInjected = true;
|
|
66
|
+
tailwindScript = script;
|
|
67
|
+
if (tailwindConfig && window.tailwind) {
|
|
68
|
+
window.tailwind.config = tailwindConfig;
|
|
69
|
+
}
|
|
70
|
+
onReady?.();
|
|
71
|
+
resolve();
|
|
72
|
+
};
|
|
73
|
+
script.onerror = (event) => {
|
|
74
|
+
const error = new Error(`Failed to load Tailwind CSS from ${cdnUrl}`);
|
|
75
|
+
onError?.(error);
|
|
76
|
+
reject(error);
|
|
77
|
+
};
|
|
78
|
+
document.head.appendChild(script);
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
function removeTailwind() {
|
|
82
|
+
if (tailwindScript && tailwindScript.parentNode) {
|
|
83
|
+
tailwindScript.parentNode.removeChild(tailwindScript);
|
|
84
|
+
tailwindScript = null;
|
|
85
|
+
tailwindInjected = false;
|
|
86
|
+
delete window.tailwind;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
function isTailwindLoaded() {
|
|
90
|
+
return tailwindInjected || !!window.tailwind;
|
|
91
|
+
}
|
|
92
|
+
function getTailwind() {
|
|
93
|
+
return window.tailwind;
|
|
94
|
+
}
|
|
95
|
+
function updateTailwindConfig(config) {
|
|
96
|
+
if (window.tailwind) {
|
|
97
|
+
window.tailwind.config = {
|
|
98
|
+
...window.tailwind.config,
|
|
99
|
+
...config
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
function addCustomStyles(css, id) {
|
|
104
|
+
if (typeof document === "undefined") {
|
|
105
|
+
throw new Error("addCustomStyles can only be used in browser environment");
|
|
106
|
+
}
|
|
107
|
+
const styleId = id || `servly-custom-styles-${Date.now()}`;
|
|
108
|
+
let existingStyle = document.getElementById(styleId);
|
|
109
|
+
if (existingStyle) {
|
|
110
|
+
existingStyle.textContent = css;
|
|
111
|
+
return existingStyle;
|
|
112
|
+
}
|
|
113
|
+
const style = document.createElement("style");
|
|
114
|
+
style.id = styleId;
|
|
115
|
+
style.textContent = css;
|
|
116
|
+
document.head.appendChild(style);
|
|
117
|
+
return style;
|
|
118
|
+
}
|
|
119
|
+
function removeCustomStyles(id) {
|
|
120
|
+
if (typeof document === "undefined") return;
|
|
121
|
+
const style = document.getElementById(id);
|
|
122
|
+
if (style && style.parentNode) {
|
|
123
|
+
style.parentNode.removeChild(style);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
async function initServlyTailwind(customConfig) {
|
|
127
|
+
const config = customConfig ? { ...DEFAULT_SERVLY_TAILWIND_CONFIG, ...customConfig } : DEFAULT_SERVLY_TAILWIND_CONFIG;
|
|
128
|
+
await injectTailwind({
|
|
129
|
+
config,
|
|
130
|
+
usePlayCdn: true
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
var DEFAULT_TAILWIND_CDN, tailwindInjected, tailwindScript, DEFAULT_SERVLY_TAILWIND_CONFIG, tailwind_default;
|
|
134
|
+
var init_tailwind = __esm({
|
|
135
|
+
"src/tailwind.ts"() {
|
|
136
|
+
"use strict";
|
|
137
|
+
DEFAULT_TAILWIND_CDN = "https://cdn.tailwindcss.com";
|
|
138
|
+
tailwindInjected = false;
|
|
139
|
+
tailwindScript = null;
|
|
140
|
+
DEFAULT_SERVLY_TAILWIND_CONFIG = {
|
|
141
|
+
theme: {
|
|
142
|
+
extend: {
|
|
143
|
+
// Add any Servly-specific theme extensions here
|
|
144
|
+
}
|
|
145
|
+
},
|
|
146
|
+
// Safelist common dynamic classes
|
|
147
|
+
safelist: [
|
|
148
|
+
// Spacing
|
|
149
|
+
{ pattern: /^(p|m|gap)-/ },
|
|
150
|
+
// Sizing
|
|
151
|
+
{ pattern: /^(w|h|min-w|min-h|max-w|max-h)-/ },
|
|
152
|
+
// Flexbox
|
|
153
|
+
{ pattern: /^(flex|justify|items|self)-/ },
|
|
154
|
+
// Grid
|
|
155
|
+
{ pattern: /^(grid|col|row)-/ },
|
|
156
|
+
// Colors
|
|
157
|
+
{ pattern: /^(bg|text|border|ring)-/ },
|
|
158
|
+
// Typography
|
|
159
|
+
{ pattern: /^(font|text|leading|tracking)-/ },
|
|
160
|
+
// Borders
|
|
161
|
+
{ pattern: /^(rounded|border)-/ },
|
|
162
|
+
// Effects
|
|
163
|
+
{ pattern: /^(shadow|opacity|blur)-/ },
|
|
164
|
+
// Transforms
|
|
165
|
+
{ pattern: /^(scale|rotate|translate|skew)-/ },
|
|
166
|
+
// Transitions
|
|
167
|
+
{ pattern: /^(transition|duration|ease|delay)-/ }
|
|
168
|
+
]
|
|
169
|
+
};
|
|
170
|
+
tailwind_default = {
|
|
171
|
+
injectTailwind,
|
|
172
|
+
removeTailwind,
|
|
173
|
+
isTailwindLoaded,
|
|
174
|
+
getTailwind,
|
|
175
|
+
updateTailwindConfig,
|
|
176
|
+
addCustomStyles,
|
|
177
|
+
removeCustomStyles,
|
|
178
|
+
initServlyTailwind,
|
|
179
|
+
DEFAULT_SERVLY_TAILWIND_CONFIG
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
});
|
|
183
|
+
|
|
23
184
|
// src/registry.ts
|
|
24
185
|
var registry_exports = {};
|
|
25
186
|
__export(registry_exports, {
|
|
@@ -214,9 +375,16 @@ __export(index_exports, {
|
|
|
214
375
|
AnalyticsCollector: () => AnalyticsCollector,
|
|
215
376
|
DEFAULT_CACHE_CONFIG: () => DEFAULT_CACHE_CONFIG,
|
|
216
377
|
DEFAULT_RETRY_CONFIG: () => DEFAULT_RETRY_CONFIG,
|
|
378
|
+
DEFAULT_SERVLY_TAILWIND_CONFIG: () => DEFAULT_SERVLY_TAILWIND_CONFIG,
|
|
379
|
+
EVENT_HANDLERS: () => EVENT_HANDLERS,
|
|
380
|
+
EventSystem: () => EventSystem,
|
|
217
381
|
LongTaskObserver: () => LongTaskObserver,
|
|
218
382
|
MemorySampler: () => MemorySampler,
|
|
383
|
+
OverrideSystem: () => OverrideSystem,
|
|
219
384
|
SessionManager: () => SessionManager,
|
|
385
|
+
StateManager: () => StateManager,
|
|
386
|
+
addClass: () => addClass,
|
|
387
|
+
addCustomStyles: () => addCustomStyles,
|
|
220
388
|
analytics: () => analytics,
|
|
221
389
|
applyStyles: () => applyStyles,
|
|
222
390
|
batchFetchComponents: () => batchFetchComponents,
|
|
@@ -230,13 +398,20 @@ __export(index_exports, {
|
|
|
230
398
|
clearMemoryCache: () => clearMemoryCache,
|
|
231
399
|
clearStyles: () => clearStyles,
|
|
232
400
|
collectAllDependencies: () => collectAllDependencies,
|
|
401
|
+
collectAllViewDependencies: () => collectAllViewDependencies,
|
|
233
402
|
compareVersions: () => compareVersions,
|
|
234
403
|
configureAnalytics: () => configureAnalytics,
|
|
235
404
|
createRegistry: () => createRegistry,
|
|
405
|
+
createServlyRenderer: () => createServlyRenderer,
|
|
406
|
+
createViewsMap: () => createViewsMap,
|
|
407
|
+
deepMerge: () => deepMerge,
|
|
408
|
+
deleteValueByPath: () => deleteValueByPath,
|
|
236
409
|
detectCircularDependencies: () => detectCircularDependencies,
|
|
237
410
|
extractBindingKeys: () => extractBindingKeys,
|
|
238
411
|
extractDependencies: () => extractDependencies,
|
|
239
412
|
extractDependenciesFromCode: () => extractDependenciesFromCode,
|
|
413
|
+
extractOverrideDependencies: () => extractOverrideDependencies,
|
|
414
|
+
extractReferencedViewIds: () => extractReferencedViewIds,
|
|
240
415
|
fetchComponent: () => fetchComponent,
|
|
241
416
|
fetchComponentWithDependencies: () => fetchComponentWithDependencies,
|
|
242
417
|
formatStyleValue: () => formatStyleValue,
|
|
@@ -244,25 +419,52 @@ __export(index_exports, {
|
|
|
244
419
|
generateTestCases: () => generateTestCases,
|
|
245
420
|
getAnalytics: () => getAnalytics,
|
|
246
421
|
getCacheKey: () => getCacheKey,
|
|
422
|
+
getCleanupOverrides: () => getCleanupOverrides,
|
|
247
423
|
getDependencyTree: () => getDependencyTree,
|
|
424
|
+
getEventSystem: () => getEventSystem,
|
|
248
425
|
getFromCache: () => getFromCache,
|
|
426
|
+
getLocalStorage: () => getLocalStorage,
|
|
249
427
|
getLongTaskObserver: () => getLongTaskObserver,
|
|
250
428
|
getMemoryCacheSize: () => getMemoryCacheSize,
|
|
251
429
|
getMemorySampler: () => getMemorySampler,
|
|
430
|
+
getMountOverrides: () => getMountOverrides,
|
|
431
|
+
getOverrideSystem: () => getOverrideSystem,
|
|
252
432
|
getRegistryUrl: () => getRegistryUrl,
|
|
253
433
|
getSessionManager: () => getSessionManager,
|
|
434
|
+
getSessionStorage: () => getSessionStorage,
|
|
435
|
+
getTailwind: () => getTailwind,
|
|
436
|
+
getUrlInfo: () => getUrlInfo,
|
|
437
|
+
getValueByPath: () => getValueByPath,
|
|
438
|
+
goBack: () => goBack,
|
|
439
|
+
goForward: () => goForward,
|
|
440
|
+
hasClass: () => hasClass,
|
|
441
|
+
hasDependencyOverrides: () => hasDependencyOverrides,
|
|
442
|
+
hasOverrides: () => hasOverrides,
|
|
254
443
|
hasTemplateSyntax: () => hasTemplateSyntax,
|
|
444
|
+
initServlyTailwind: () => initServlyTailwind,
|
|
445
|
+
injectTailwind: () => injectTailwind,
|
|
255
446
|
invalidateCache: () => invalidateCache,
|
|
256
447
|
isComponentAvailable: () => isComponentAvailable,
|
|
448
|
+
isTailwindLoaded: () => isTailwindLoaded,
|
|
257
449
|
isValidSpecifier: () => isValidSpecifier,
|
|
450
|
+
navigateTo: () => navigateTo,
|
|
258
451
|
parseVersion: () => parseVersion,
|
|
259
452
|
prefetchComponents: () => prefetchComponents,
|
|
260
453
|
processStyles: () => processStyles,
|
|
454
|
+
removeClass: () => removeClass,
|
|
455
|
+
removeCustomStyles: () => removeCustomStyles,
|
|
456
|
+
removeLocalStorage: () => removeLocalStorage,
|
|
457
|
+
removeSessionStorage: () => removeSessionStorage,
|
|
458
|
+
removeTailwind: () => removeTailwind,
|
|
261
459
|
render: () => render,
|
|
262
460
|
renderDynamicList: () => renderDynamicList,
|
|
461
|
+
renderInShadow: () => renderInShadow,
|
|
462
|
+
renderNode: () => renderNode,
|
|
263
463
|
resetAnalytics: () => resetAnalytics,
|
|
464
|
+
resetEventSystem: () => resetEventSystem,
|
|
264
465
|
resetLongTaskObserver: () => resetLongTaskObserver,
|
|
265
466
|
resetMemorySampler: () => resetMemorySampler,
|
|
467
|
+
resetOverrideSystem: () => resetOverrideSystem,
|
|
266
468
|
resetSessionManager: () => resetSessionManager,
|
|
267
469
|
resolveBindingPath: () => resolveBindingPath,
|
|
268
470
|
resolveTemplate: () => resolveTemplate,
|
|
@@ -273,8 +475,15 @@ __export(index_exports, {
|
|
|
273
475
|
runTestCase: () => runTestCase,
|
|
274
476
|
satisfiesVersion: () => satisfiesVersion,
|
|
275
477
|
setInCache: () => setInCache,
|
|
478
|
+
setLocalStorage: () => setLocalStorage,
|
|
276
479
|
setRegistryUrl: () => setRegistryUrl,
|
|
480
|
+
setSessionStorage: () => setSessionStorage,
|
|
481
|
+
setValueByPath: () => setValueByPath,
|
|
482
|
+
toDomEventName: () => toDomEventName,
|
|
483
|
+
toReactEventName: () => toReactEventName,
|
|
484
|
+
toggleClass: () => toggleClass,
|
|
277
485
|
updateStyles: () => updateStyles,
|
|
486
|
+
updateTailwindConfig: () => updateTailwindConfig,
|
|
278
487
|
validateAssertion: () => validateAssertion,
|
|
279
488
|
validateProps: () => validateProps
|
|
280
489
|
});
|
|
@@ -799,16 +1008,95 @@ var BINDING_SOURCES = [
|
|
|
799
1008
|
"input",
|
|
800
1009
|
"currentItem",
|
|
801
1010
|
"localStore",
|
|
1011
|
+
"localStorage",
|
|
1012
|
+
"sessionStorage",
|
|
802
1013
|
"config",
|
|
803
1014
|
"element",
|
|
804
1015
|
"self",
|
|
805
1016
|
"params",
|
|
806
|
-
"query"
|
|
1017
|
+
"query",
|
|
1018
|
+
"window",
|
|
1019
|
+
"bindings",
|
|
1020
|
+
"binding",
|
|
1021
|
+
"boundInputs",
|
|
1022
|
+
"parent"
|
|
807
1023
|
];
|
|
808
1024
|
var TEMPLATE_REGEX = /\{\{([^}]+)\}\}/g;
|
|
809
1025
|
function hasTemplateSyntax(value) {
|
|
810
1026
|
return typeof value === "string" && value.includes("{{") && value.includes("}}");
|
|
811
1027
|
}
|
|
1028
|
+
function getLocalStorageValue(key) {
|
|
1029
|
+
if (typeof localStorage === "undefined") return void 0;
|
|
1030
|
+
try {
|
|
1031
|
+
const stored = localStorage.getItem(key);
|
|
1032
|
+
if (stored === null) return void 0;
|
|
1033
|
+
try {
|
|
1034
|
+
return JSON.parse(stored);
|
|
1035
|
+
} catch {
|
|
1036
|
+
return stored;
|
|
1037
|
+
}
|
|
1038
|
+
} catch {
|
|
1039
|
+
return void 0;
|
|
1040
|
+
}
|
|
1041
|
+
}
|
|
1042
|
+
function getSessionStorageValue(key) {
|
|
1043
|
+
if (typeof sessionStorage === "undefined") return void 0;
|
|
1044
|
+
try {
|
|
1045
|
+
const stored = sessionStorage.getItem(key);
|
|
1046
|
+
if (stored === null) return void 0;
|
|
1047
|
+
try {
|
|
1048
|
+
return JSON.parse(stored);
|
|
1049
|
+
} catch {
|
|
1050
|
+
return stored;
|
|
1051
|
+
}
|
|
1052
|
+
} catch {
|
|
1053
|
+
return void 0;
|
|
1054
|
+
}
|
|
1055
|
+
}
|
|
1056
|
+
function getWindowInfo() {
|
|
1057
|
+
if (typeof window === "undefined") return {};
|
|
1058
|
+
const searchParams = {};
|
|
1059
|
+
try {
|
|
1060
|
+
const urlSearchParams = new URLSearchParams(window.location.search);
|
|
1061
|
+
urlSearchParams.forEach((value, key) => {
|
|
1062
|
+
searchParams[key] = value;
|
|
1063
|
+
});
|
|
1064
|
+
} catch {
|
|
1065
|
+
}
|
|
1066
|
+
return {
|
|
1067
|
+
href: window.location.href,
|
|
1068
|
+
pathname: window.location.pathname,
|
|
1069
|
+
search: window.location.search,
|
|
1070
|
+
hash: window.location.hash,
|
|
1071
|
+
origin: window.location.origin,
|
|
1072
|
+
protocol: window.location.protocol,
|
|
1073
|
+
host: window.location.host,
|
|
1074
|
+
hostname: window.location.hostname,
|
|
1075
|
+
port: window.location.port,
|
|
1076
|
+
searchParams,
|
|
1077
|
+
params: searchParams,
|
|
1078
|
+
innerWidth: window.innerWidth,
|
|
1079
|
+
innerHeight: window.innerHeight,
|
|
1080
|
+
screenWidth: window.screen?.width,
|
|
1081
|
+
screenHeight: window.screen?.height
|
|
1082
|
+
};
|
|
1083
|
+
}
|
|
1084
|
+
function navigatePath(obj, parts) {
|
|
1085
|
+
let current = obj;
|
|
1086
|
+
for (const part of parts) {
|
|
1087
|
+
if (current === null || current === void 0) return void 0;
|
|
1088
|
+
const arrayMatch = part.match(/^(\w+)\[(\d+)\]$/);
|
|
1089
|
+
if (arrayMatch) {
|
|
1090
|
+
const [, propName, indexStr] = arrayMatch;
|
|
1091
|
+
current = current[propName];
|
|
1092
|
+
if (!Array.isArray(current)) return void 0;
|
|
1093
|
+
current = current[parseInt(indexStr, 10)];
|
|
1094
|
+
} else {
|
|
1095
|
+
current = current[part];
|
|
1096
|
+
}
|
|
1097
|
+
}
|
|
1098
|
+
return current;
|
|
1099
|
+
}
|
|
812
1100
|
function resolveBindingPath(path, context) {
|
|
813
1101
|
const trimmed = path.trim();
|
|
814
1102
|
const parts = trimmed.split(".");
|
|
@@ -816,17 +1104,70 @@ function resolveBindingPath(path, context) {
|
|
|
816
1104
|
return void 0;
|
|
817
1105
|
}
|
|
818
1106
|
const prefix = parts[0].toLowerCase();
|
|
1107
|
+
if (prefix === "localstore" || prefix === "localstorage") {
|
|
1108
|
+
if (parts.length === 1) {
|
|
1109
|
+
const all = {};
|
|
1110
|
+
if (typeof localStorage !== "undefined") {
|
|
1111
|
+
for (let i = 0; i < localStorage.length; i++) {
|
|
1112
|
+
const key2 = localStorage.key(i);
|
|
1113
|
+
if (key2) {
|
|
1114
|
+
all[key2] = getLocalStorageValue(key2);
|
|
1115
|
+
}
|
|
1116
|
+
}
|
|
1117
|
+
}
|
|
1118
|
+
return all;
|
|
1119
|
+
}
|
|
1120
|
+
const key = parts[1];
|
|
1121
|
+
const value = getLocalStorageValue(key);
|
|
1122
|
+
if (parts.length === 2) return value;
|
|
1123
|
+
return navigatePath(value, parts.slice(2));
|
|
1124
|
+
}
|
|
1125
|
+
if (prefix === "sessionstorage") {
|
|
1126
|
+
if (parts.length === 1) {
|
|
1127
|
+
const all = {};
|
|
1128
|
+
if (typeof sessionStorage !== "undefined") {
|
|
1129
|
+
for (let i = 0; i < sessionStorage.length; i++) {
|
|
1130
|
+
const key2 = sessionStorage.key(i);
|
|
1131
|
+
if (key2) {
|
|
1132
|
+
all[key2] = getSessionStorageValue(key2);
|
|
1133
|
+
}
|
|
1134
|
+
}
|
|
1135
|
+
}
|
|
1136
|
+
return all;
|
|
1137
|
+
}
|
|
1138
|
+
const key = parts[1];
|
|
1139
|
+
const value = getSessionStorageValue(key);
|
|
1140
|
+
if (parts.length === 2) return value;
|
|
1141
|
+
return navigatePath(value, parts.slice(2));
|
|
1142
|
+
}
|
|
1143
|
+
if (prefix === "window" || prefix === "url") {
|
|
1144
|
+
const windowInfo = getWindowInfo();
|
|
1145
|
+
if (parts.length === 1) return windowInfo;
|
|
1146
|
+
return navigatePath(windowInfo, parts.slice(1));
|
|
1147
|
+
}
|
|
1148
|
+
if (prefix === "params" || prefix === "query") {
|
|
1149
|
+
const windowInfo = getWindowInfo();
|
|
1150
|
+
const params = windowInfo.searchParams || {};
|
|
1151
|
+
if (parts.length === 1) return params;
|
|
1152
|
+
return navigatePath(params, parts.slice(1));
|
|
1153
|
+
}
|
|
819
1154
|
let source;
|
|
820
1155
|
let startIndex = 0;
|
|
821
|
-
if (prefix === "props" || prefix === "input") {
|
|
1156
|
+
if (prefix === "props" || prefix === "input" || prefix === "bindings" || prefix === "binding" || prefix === "boundinputs" || prefix === "parent") {
|
|
822
1157
|
source = context.props;
|
|
823
1158
|
startIndex = 1;
|
|
824
1159
|
} else if (prefix === "state" || prefix === "appstate") {
|
|
825
1160
|
source = context.state;
|
|
826
1161
|
startIndex = 1;
|
|
827
|
-
} else if (prefix === "context") {
|
|
1162
|
+
} else if (prefix === "context" || prefix === "config") {
|
|
828
1163
|
source = context.context;
|
|
829
1164
|
startIndex = 1;
|
|
1165
|
+
} else if (prefix === "currentitem") {
|
|
1166
|
+
source = context.props;
|
|
1167
|
+
startIndex = 0;
|
|
1168
|
+
} else if (prefix === "self" || prefix === "element") {
|
|
1169
|
+
source = context.state;
|
|
1170
|
+
startIndex = 1;
|
|
830
1171
|
} else if (BINDING_SOURCES.includes(prefix)) {
|
|
831
1172
|
source = context.props;
|
|
832
1173
|
startIndex = 1;
|
|
@@ -837,41 +1178,49 @@ function resolveBindingPath(path, context) {
|
|
|
837
1178
|
if (!source) {
|
|
838
1179
|
return void 0;
|
|
839
1180
|
}
|
|
840
|
-
|
|
841
|
-
for (let i = startIndex; i < parts.length; i++) {
|
|
842
|
-
if (value === null || value === void 0) {
|
|
843
|
-
return void 0;
|
|
844
|
-
}
|
|
845
|
-
value = value[parts[i]];
|
|
846
|
-
}
|
|
847
|
-
return value;
|
|
1181
|
+
return navigatePath(source, parts.slice(startIndex));
|
|
848
1182
|
}
|
|
849
1183
|
function resolveExpression(expression, context) {
|
|
850
1184
|
const trimmed = expression.trim();
|
|
851
|
-
const
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
1185
|
+
const comparisonOperators = ["===", "!==", "==", "!=", ">=", "<=", ">", "<"];
|
|
1186
|
+
for (const op of comparisonOperators) {
|
|
1187
|
+
if (trimmed.includes(op)) {
|
|
1188
|
+
const [left, right] = trimmed.split(op, 2);
|
|
1189
|
+
const leftVal = resolveExpressionValue(left.trim(), context);
|
|
1190
|
+
const rightVal = resolveExpressionValue(right.trim(), context);
|
|
1191
|
+
switch (op) {
|
|
1192
|
+
case "===":
|
|
1193
|
+
return leftVal === rightVal;
|
|
1194
|
+
case "!==":
|
|
1195
|
+
return leftVal !== rightVal;
|
|
1196
|
+
case "==":
|
|
1197
|
+
return leftVal == rightVal;
|
|
1198
|
+
case "!=":
|
|
1199
|
+
return leftVal != rightVal;
|
|
1200
|
+
case ">":
|
|
1201
|
+
return leftVal > rightVal;
|
|
1202
|
+
case "<":
|
|
1203
|
+
return leftVal < rightVal;
|
|
1204
|
+
case ">=":
|
|
1205
|
+
return leftVal >= rightVal;
|
|
1206
|
+
case "<=":
|
|
1207
|
+
return leftVal <= rightVal;
|
|
1208
|
+
}
|
|
863
1209
|
}
|
|
864
|
-
if (defaultVal === "true") return true;
|
|
865
|
-
if (defaultVal === "false") return false;
|
|
866
|
-
return resolveExpression(defaultVal, context);
|
|
867
1210
|
}
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
1211
|
+
if (trimmed.startsWith("!") && !trimmed.startsWith("!=")) {
|
|
1212
|
+
const innerValue = resolveExpression(trimmed.slice(1).trim(), context);
|
|
1213
|
+
return !innerValue;
|
|
1214
|
+
}
|
|
1215
|
+
if (trimmed.includes("||")) {
|
|
1216
|
+
const parts = trimmed.split("||");
|
|
1217
|
+
for (let i = 0; i < parts.length; i++) {
|
|
1218
|
+
const part = parts[i].trim();
|
|
1219
|
+
const value = resolveExpressionValue(part, context);
|
|
1220
|
+
if (value || i === parts.length - 1) {
|
|
1221
|
+
return value;
|
|
1222
|
+
}
|
|
873
1223
|
}
|
|
874
|
-
return resolveTernaryValue(ternaryMatch[3].trim(), context);
|
|
875
1224
|
}
|
|
876
1225
|
if (trimmed.includes("&&")) {
|
|
877
1226
|
const parts = trimmed.split("&&");
|
|
@@ -881,6 +1230,28 @@ function resolveExpression(expression, context) {
|
|
|
881
1230
|
}
|
|
882
1231
|
return resolveExpression(parts[parts.length - 1].trim(), context);
|
|
883
1232
|
}
|
|
1233
|
+
const ternaryMatch = trimmed.match(/^(.+?)\s*\?\s*(.+?)\s*:\s*(.+)$/);
|
|
1234
|
+
if (ternaryMatch) {
|
|
1235
|
+
const condition = resolveExpression(ternaryMatch[1].trim(), context);
|
|
1236
|
+
if (condition) {
|
|
1237
|
+
return resolveTernaryValue(ternaryMatch[2].trim(), context);
|
|
1238
|
+
}
|
|
1239
|
+
return resolveTernaryValue(ternaryMatch[3].trim(), context);
|
|
1240
|
+
}
|
|
1241
|
+
return resolveExpressionValue(trimmed, context);
|
|
1242
|
+
}
|
|
1243
|
+
function resolveExpressionValue(value, context) {
|
|
1244
|
+
const trimmed = value.trim();
|
|
1245
|
+
if (trimmed.startsWith('"') && trimmed.endsWith('"') || trimmed.startsWith("'") && trimmed.endsWith("'")) {
|
|
1246
|
+
return trimmed.slice(1, -1);
|
|
1247
|
+
}
|
|
1248
|
+
if (!isNaN(Number(trimmed)) && trimmed !== "") {
|
|
1249
|
+
return Number(trimmed);
|
|
1250
|
+
}
|
|
1251
|
+
if (trimmed === "true") return true;
|
|
1252
|
+
if (trimmed === "false") return false;
|
|
1253
|
+
if (trimmed === "null") return null;
|
|
1254
|
+
if (trimmed === "undefined") return void 0;
|
|
884
1255
|
return resolveBindingPath(trimmed, context);
|
|
885
1256
|
}
|
|
886
1257
|
function resolveTernaryValue(value, context) {
|
|
@@ -912,7 +1283,7 @@ function resolveTemplate(template, context, componentId) {
|
|
|
912
1283
|
}
|
|
913
1284
|
return String(value);
|
|
914
1285
|
}
|
|
915
|
-
return template.replace(TEMPLATE_REGEX, (
|
|
1286
|
+
return template.replace(TEMPLATE_REGEX, (_match, expression) => {
|
|
916
1287
|
const value = resolveExpression(expression, context);
|
|
917
1288
|
if (value === void 0 || value === null) {
|
|
918
1289
|
return "";
|
|
@@ -1342,131 +1713,1180 @@ function resetLongTaskObserver() {
|
|
|
1342
1713
|
longTaskObserverInstance = null;
|
|
1343
1714
|
}
|
|
1344
1715
|
|
|
1345
|
-
// src/
|
|
1346
|
-
var
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
list: "ul",
|
|
1359
|
-
listItem: "li",
|
|
1360
|
-
heading: "h1",
|
|
1361
|
-
paragraph: "p",
|
|
1362
|
-
section: "section",
|
|
1363
|
-
article: "article",
|
|
1364
|
-
header: "header",
|
|
1365
|
-
footer: "footer",
|
|
1366
|
-
nav: "nav",
|
|
1367
|
-
aside: "aside",
|
|
1368
|
-
main: "main",
|
|
1369
|
-
span: "span",
|
|
1370
|
-
div: "div"
|
|
1371
|
-
};
|
|
1372
|
-
var SELF_CLOSING_TAGS = /* @__PURE__ */ new Set([
|
|
1373
|
-
"input",
|
|
1374
|
-
"img",
|
|
1375
|
-
"br",
|
|
1376
|
-
"hr",
|
|
1377
|
-
"area",
|
|
1378
|
-
"base",
|
|
1379
|
-
"col",
|
|
1380
|
-
"embed",
|
|
1381
|
-
"link",
|
|
1382
|
-
"meta",
|
|
1383
|
-
"param",
|
|
1384
|
-
"source",
|
|
1385
|
-
"track",
|
|
1386
|
-
"wbr"
|
|
1387
|
-
]);
|
|
1388
|
-
function getElementTag(element) {
|
|
1389
|
-
const config = element.configuration;
|
|
1390
|
-
if (config?.tag) {
|
|
1391
|
-
return config.tag;
|
|
1716
|
+
// src/stateManager.ts
|
|
1717
|
+
var StateManager = class {
|
|
1718
|
+
constructor(config = {}) {
|
|
1719
|
+
this.state = {};
|
|
1720
|
+
this.listeners = /* @__PURE__ */ new Set();
|
|
1721
|
+
this.config = config;
|
|
1722
|
+
this.state = config.initialState || {};
|
|
1723
|
+
if (config.persistToLocalStorage && typeof localStorage !== "undefined") {
|
|
1724
|
+
this.loadFromLocalStorage();
|
|
1725
|
+
}
|
|
1726
|
+
if (config.onStateChange) {
|
|
1727
|
+
this.listeners.add(config.onStateChange);
|
|
1728
|
+
}
|
|
1392
1729
|
}
|
|
1393
|
-
|
|
1394
|
-
|
|
1730
|
+
/**
|
|
1731
|
+
* Get value by path from state
|
|
1732
|
+
*/
|
|
1733
|
+
get(path) {
|
|
1734
|
+
return getValueByPath(this.state, path);
|
|
1395
1735
|
}
|
|
1396
|
-
|
|
1397
|
-
|
|
1736
|
+
/**
|
|
1737
|
+
* Get the entire state object
|
|
1738
|
+
*/
|
|
1739
|
+
getState() {
|
|
1740
|
+
return { ...this.state };
|
|
1398
1741
|
}
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1742
|
+
/**
|
|
1743
|
+
* Set a value in state
|
|
1744
|
+
*/
|
|
1745
|
+
set(key, value, elementId) {
|
|
1746
|
+
const previousValue = this.get(key);
|
|
1747
|
+
setValueByPath(this.state, key, value);
|
|
1748
|
+
this.notifyChange({
|
|
1749
|
+
key,
|
|
1750
|
+
value,
|
|
1751
|
+
previousValue,
|
|
1752
|
+
operation: "set",
|
|
1753
|
+
elementId,
|
|
1754
|
+
timestamp: Date.now()
|
|
1755
|
+
});
|
|
1412
1756
|
}
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1757
|
+
/**
|
|
1758
|
+
* Merge an object into state at the given path
|
|
1759
|
+
*/
|
|
1760
|
+
merge(key, value, deep = false, elementId) {
|
|
1761
|
+
const previousValue = this.get(key);
|
|
1762
|
+
const currentValue = previousValue || {};
|
|
1763
|
+
const newValue = deep ? deepMerge(currentValue, value) : { ...currentValue, ...value };
|
|
1764
|
+
setValueByPath(this.state, key, newValue);
|
|
1765
|
+
this.notifyChange({
|
|
1766
|
+
key,
|
|
1767
|
+
value: newValue,
|
|
1768
|
+
previousValue,
|
|
1769
|
+
operation: "merge",
|
|
1770
|
+
elementId,
|
|
1771
|
+
timestamp: Date.now()
|
|
1772
|
+
});
|
|
1419
1773
|
}
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1774
|
+
/**
|
|
1775
|
+
* Delete a key from state
|
|
1776
|
+
*/
|
|
1777
|
+
delete(key, elementId) {
|
|
1778
|
+
const previousValue = this.get(key);
|
|
1779
|
+
deleteValueByPath(this.state, key);
|
|
1780
|
+
this.notifyChange({
|
|
1781
|
+
key,
|
|
1782
|
+
value: void 0,
|
|
1783
|
+
previousValue,
|
|
1784
|
+
operation: "delete",
|
|
1785
|
+
elementId,
|
|
1786
|
+
timestamp: Date.now()
|
|
1787
|
+
});
|
|
1425
1788
|
}
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
{
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1789
|
+
/**
|
|
1790
|
+
* Append to an array in state
|
|
1791
|
+
*/
|
|
1792
|
+
append(key, value, elementId) {
|
|
1793
|
+
const previousValue = this.get(key);
|
|
1794
|
+
const currentArray = Array.isArray(previousValue) ? [...previousValue] : [];
|
|
1795
|
+
currentArray.push(value);
|
|
1796
|
+
setValueByPath(this.state, key, currentArray);
|
|
1797
|
+
this.notifyChange({
|
|
1798
|
+
key,
|
|
1799
|
+
value: currentArray,
|
|
1800
|
+
previousValue,
|
|
1801
|
+
operation: "append",
|
|
1802
|
+
elementId,
|
|
1803
|
+
timestamp: Date.now()
|
|
1804
|
+
});
|
|
1805
|
+
}
|
|
1806
|
+
/**
|
|
1807
|
+
* Prepend to an array in state
|
|
1808
|
+
*/
|
|
1809
|
+
prepend(key, value, elementId) {
|
|
1810
|
+
const previousValue = this.get(key);
|
|
1811
|
+
const currentArray = Array.isArray(previousValue) ? [...previousValue] : [];
|
|
1812
|
+
currentArray.unshift(value);
|
|
1813
|
+
setValueByPath(this.state, key, currentArray);
|
|
1814
|
+
this.notifyChange({
|
|
1815
|
+
key,
|
|
1816
|
+
value: currentArray,
|
|
1817
|
+
previousValue,
|
|
1818
|
+
operation: "prepend",
|
|
1819
|
+
elementId,
|
|
1820
|
+
timestamp: Date.now()
|
|
1821
|
+
});
|
|
1822
|
+
}
|
|
1823
|
+
/**
|
|
1824
|
+
* Toggle a boolean value in state
|
|
1825
|
+
*/
|
|
1826
|
+
toggle(key, elementId) {
|
|
1827
|
+
const previousValue = this.get(key);
|
|
1828
|
+
const newValue = !previousValue;
|
|
1829
|
+
setValueByPath(this.state, key, newValue);
|
|
1830
|
+
this.notifyChange({
|
|
1831
|
+
key,
|
|
1832
|
+
value: newValue,
|
|
1833
|
+
previousValue,
|
|
1834
|
+
operation: "toggle",
|
|
1835
|
+
elementId,
|
|
1836
|
+
timestamp: Date.now()
|
|
1837
|
+
});
|
|
1838
|
+
}
|
|
1839
|
+
/**
|
|
1840
|
+
* Subscribe to state changes
|
|
1841
|
+
*/
|
|
1842
|
+
subscribe(listener) {
|
|
1843
|
+
this.listeners.add(listener);
|
|
1844
|
+
return () => this.listeners.delete(listener);
|
|
1845
|
+
}
|
|
1846
|
+
/**
|
|
1847
|
+
* Notify all listeners of a state change
|
|
1848
|
+
*/
|
|
1849
|
+
notifyChange(event) {
|
|
1850
|
+
if (this.config.persistToLocalStorage) {
|
|
1851
|
+
this.saveToLocalStorage();
|
|
1852
|
+
}
|
|
1853
|
+
for (const listener of this.listeners) {
|
|
1854
|
+
try {
|
|
1855
|
+
listener(event);
|
|
1856
|
+
} catch (error) {
|
|
1857
|
+
console.error("State change listener error:", error);
|
|
1447
1858
|
}
|
|
1448
|
-
continue;
|
|
1449
1859
|
}
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1860
|
+
}
|
|
1861
|
+
/**
|
|
1862
|
+
* Load state from localStorage
|
|
1863
|
+
*/
|
|
1864
|
+
loadFromLocalStorage() {
|
|
1865
|
+
try {
|
|
1866
|
+
const prefix = this.config.localStoragePrefix || "servly_state_";
|
|
1867
|
+
const stored = localStorage.getItem(`${prefix}state`);
|
|
1868
|
+
if (stored) {
|
|
1869
|
+
const parsed = JSON.parse(stored);
|
|
1870
|
+
this.state = { ...this.state, ...parsed };
|
|
1455
1871
|
}
|
|
1872
|
+
} catch (error) {
|
|
1873
|
+
console.warn("Failed to load state from localStorage:", error);
|
|
1456
1874
|
}
|
|
1457
1875
|
}
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
const
|
|
1464
|
-
|
|
1876
|
+
/**
|
|
1877
|
+
* Save state to localStorage
|
|
1878
|
+
*/
|
|
1879
|
+
saveToLocalStorage() {
|
|
1880
|
+
try {
|
|
1881
|
+
const prefix = this.config.localStoragePrefix || "servly_state_";
|
|
1882
|
+
localStorage.setItem(`${prefix}state`, JSON.stringify(this.state));
|
|
1883
|
+
} catch (error) {
|
|
1884
|
+
console.warn("Failed to save state to localStorage:", error);
|
|
1465
1885
|
}
|
|
1466
1886
|
}
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1887
|
+
/**
|
|
1888
|
+
* Clear all state
|
|
1889
|
+
*/
|
|
1890
|
+
clear() {
|
|
1891
|
+
const previousState = { ...this.state };
|
|
1892
|
+
this.state = {};
|
|
1893
|
+
if (this.config.persistToLocalStorage) {
|
|
1894
|
+
try {
|
|
1895
|
+
const prefix = this.config.localStoragePrefix || "servly_state_";
|
|
1896
|
+
localStorage.removeItem(`${prefix}state`);
|
|
1897
|
+
} catch (error) {
|
|
1898
|
+
console.warn("Failed to clear localStorage:", error);
|
|
1899
|
+
}
|
|
1900
|
+
}
|
|
1901
|
+
this.notifyChange({
|
|
1902
|
+
key: "",
|
|
1903
|
+
value: {},
|
|
1904
|
+
previousValue: previousState,
|
|
1905
|
+
operation: "delete",
|
|
1906
|
+
timestamp: Date.now()
|
|
1907
|
+
});
|
|
1908
|
+
}
|
|
1909
|
+
};
|
|
1910
|
+
function getValueByPath(obj, path) {
|
|
1911
|
+
if (!obj || !path) return void 0;
|
|
1912
|
+
const parts = path.split(".");
|
|
1913
|
+
let current = obj;
|
|
1914
|
+
for (const part of parts) {
|
|
1915
|
+
if (current === null || current === void 0) return void 0;
|
|
1916
|
+
const arrayMatch = part.match(/^(\w+)\[(\d+)\]$/);
|
|
1917
|
+
if (arrayMatch) {
|
|
1918
|
+
const [, propName, indexStr] = arrayMatch;
|
|
1919
|
+
current = current[propName];
|
|
1920
|
+
if (!Array.isArray(current)) return void 0;
|
|
1921
|
+
current = current[parseInt(indexStr, 10)];
|
|
1922
|
+
} else {
|
|
1923
|
+
current = current[part];
|
|
1924
|
+
}
|
|
1925
|
+
}
|
|
1926
|
+
return current;
|
|
1927
|
+
}
|
|
1928
|
+
function setValueByPath(obj, path, value) {
|
|
1929
|
+
if (!obj || !path) return;
|
|
1930
|
+
const parts = path.split(".");
|
|
1931
|
+
let current = obj;
|
|
1932
|
+
for (let i = 0; i < parts.length - 1; i++) {
|
|
1933
|
+
const part = parts[i];
|
|
1934
|
+
const arrayMatch2 = part.match(/^(\w+)\[(\d+)\]$/);
|
|
1935
|
+
if (arrayMatch2) {
|
|
1936
|
+
const [, propName, indexStr] = arrayMatch2;
|
|
1937
|
+
if (!current[propName]) current[propName] = [];
|
|
1938
|
+
const index = parseInt(indexStr, 10);
|
|
1939
|
+
if (!current[propName][index]) current[propName][index] = {};
|
|
1940
|
+
current = current[propName][index];
|
|
1941
|
+
} else {
|
|
1942
|
+
if (!current[part]) current[part] = {};
|
|
1943
|
+
current = current[part];
|
|
1944
|
+
}
|
|
1945
|
+
}
|
|
1946
|
+
const lastPart = parts[parts.length - 1];
|
|
1947
|
+
const arrayMatch = lastPart.match(/^(\w+)\[(\d+)\]$/);
|
|
1948
|
+
if (arrayMatch) {
|
|
1949
|
+
const [, propName, indexStr] = arrayMatch;
|
|
1950
|
+
if (!current[propName]) current[propName] = [];
|
|
1951
|
+
current[propName][parseInt(indexStr, 10)] = value;
|
|
1952
|
+
} else {
|
|
1953
|
+
current[lastPart] = value;
|
|
1954
|
+
}
|
|
1955
|
+
}
|
|
1956
|
+
function deleteValueByPath(obj, path) {
|
|
1957
|
+
if (!obj || !path) return;
|
|
1958
|
+
const parts = path.split(".");
|
|
1959
|
+
let current = obj;
|
|
1960
|
+
for (let i = 0; i < parts.length - 1; i++) {
|
|
1961
|
+
const part = parts[i];
|
|
1962
|
+
if (current[part] === void 0) return;
|
|
1963
|
+
current = current[part];
|
|
1964
|
+
}
|
|
1965
|
+
const lastPart = parts[parts.length - 1];
|
|
1966
|
+
delete current[lastPart];
|
|
1967
|
+
}
|
|
1968
|
+
function deepMerge(target, source) {
|
|
1969
|
+
if (!source) return target;
|
|
1970
|
+
if (!target) return source;
|
|
1971
|
+
const result = { ...target };
|
|
1972
|
+
for (const key of Object.keys(source)) {
|
|
1973
|
+
if (source[key] && typeof source[key] === "object" && !Array.isArray(source[key]) && target[key] && typeof target[key] === "object" && !Array.isArray(target[key])) {
|
|
1974
|
+
result[key] = deepMerge(target[key], source[key]);
|
|
1975
|
+
} else {
|
|
1976
|
+
result[key] = source[key];
|
|
1977
|
+
}
|
|
1978
|
+
}
|
|
1979
|
+
return result;
|
|
1980
|
+
}
|
|
1981
|
+
function addClass(currentClasses, classToAdd) {
|
|
1982
|
+
const classes = currentClasses.split(/\s+/).filter(Boolean);
|
|
1983
|
+
if (!classes.includes(classToAdd)) {
|
|
1984
|
+
classes.push(classToAdd);
|
|
1985
|
+
}
|
|
1986
|
+
return classes.join(" ");
|
|
1987
|
+
}
|
|
1988
|
+
function removeClass(currentClasses, classToRemove) {
|
|
1989
|
+
return currentClasses.split(/\s+/).filter((cls) => cls && cls !== classToRemove).join(" ");
|
|
1990
|
+
}
|
|
1991
|
+
function toggleClass(currentClasses, classToToggle) {
|
|
1992
|
+
const classes = currentClasses.split(/\s+/).filter(Boolean);
|
|
1993
|
+
const index = classes.indexOf(classToToggle);
|
|
1994
|
+
if (index > -1) {
|
|
1995
|
+
classes.splice(index, 1);
|
|
1996
|
+
} else {
|
|
1997
|
+
classes.push(classToToggle);
|
|
1998
|
+
}
|
|
1999
|
+
return classes.join(" ");
|
|
2000
|
+
}
|
|
2001
|
+
function hasClass(currentClasses, classToCheck) {
|
|
2002
|
+
return currentClasses.split(/\s+/).includes(classToCheck);
|
|
2003
|
+
}
|
|
2004
|
+
function getLocalStorage(key, defaultValue) {
|
|
2005
|
+
if (typeof localStorage === "undefined") return defaultValue;
|
|
2006
|
+
try {
|
|
2007
|
+
const stored = localStorage.getItem(key);
|
|
2008
|
+
if (stored === null) return defaultValue;
|
|
2009
|
+
return JSON.parse(stored);
|
|
2010
|
+
} catch {
|
|
2011
|
+
return localStorage.getItem(key) ?? defaultValue;
|
|
2012
|
+
}
|
|
2013
|
+
}
|
|
2014
|
+
function setLocalStorage(key, value) {
|
|
2015
|
+
if (typeof localStorage === "undefined") return;
|
|
2016
|
+
try {
|
|
2017
|
+
localStorage.setItem(key, JSON.stringify(value));
|
|
2018
|
+
} catch (error) {
|
|
2019
|
+
console.warn("Failed to set localStorage:", error);
|
|
2020
|
+
}
|
|
2021
|
+
}
|
|
2022
|
+
function removeLocalStorage(key) {
|
|
2023
|
+
if (typeof localStorage === "undefined") return;
|
|
2024
|
+
localStorage.removeItem(key);
|
|
2025
|
+
}
|
|
2026
|
+
function getSessionStorage(key, defaultValue) {
|
|
2027
|
+
if (typeof sessionStorage === "undefined") return defaultValue;
|
|
2028
|
+
try {
|
|
2029
|
+
const stored = sessionStorage.getItem(key);
|
|
2030
|
+
if (stored === null) return defaultValue;
|
|
2031
|
+
return JSON.parse(stored);
|
|
2032
|
+
} catch {
|
|
2033
|
+
return sessionStorage.getItem(key) ?? defaultValue;
|
|
2034
|
+
}
|
|
2035
|
+
}
|
|
2036
|
+
function setSessionStorage(key, value) {
|
|
2037
|
+
if (typeof sessionStorage === "undefined") return;
|
|
2038
|
+
try {
|
|
2039
|
+
sessionStorage.setItem(key, JSON.stringify(value));
|
|
2040
|
+
} catch (error) {
|
|
2041
|
+
console.warn("Failed to set sessionStorage:", error);
|
|
2042
|
+
}
|
|
2043
|
+
}
|
|
2044
|
+
function removeSessionStorage(key) {
|
|
2045
|
+
if (typeof sessionStorage === "undefined") return;
|
|
2046
|
+
sessionStorage.removeItem(key);
|
|
2047
|
+
}
|
|
2048
|
+
function navigateTo(url, options = {}) {
|
|
2049
|
+
if (typeof window === "undefined") return;
|
|
2050
|
+
const { replace = false, state, newTab = false } = options;
|
|
2051
|
+
if (newTab) {
|
|
2052
|
+
window.open(url, "_blank");
|
|
2053
|
+
return;
|
|
2054
|
+
}
|
|
2055
|
+
if (url.startsWith("http://") || url.startsWith("https://")) {
|
|
2056
|
+
if (replace) {
|
|
2057
|
+
window.location.replace(url);
|
|
2058
|
+
} else {
|
|
2059
|
+
window.location.href = url;
|
|
2060
|
+
}
|
|
2061
|
+
return;
|
|
2062
|
+
}
|
|
2063
|
+
if (url.startsWith("#")) {
|
|
2064
|
+
window.location.hash = url;
|
|
2065
|
+
return;
|
|
2066
|
+
}
|
|
2067
|
+
if (replace) {
|
|
2068
|
+
window.history.replaceState(state, "", url);
|
|
2069
|
+
} else {
|
|
2070
|
+
window.history.pushState(state, "", url);
|
|
2071
|
+
}
|
|
2072
|
+
window.dispatchEvent(new PopStateEvent("popstate", { state }));
|
|
2073
|
+
}
|
|
2074
|
+
function goBack() {
|
|
2075
|
+
if (typeof window === "undefined") return;
|
|
2076
|
+
window.history.back();
|
|
2077
|
+
}
|
|
2078
|
+
function goForward() {
|
|
2079
|
+
if (typeof window === "undefined") return;
|
|
2080
|
+
window.history.forward();
|
|
2081
|
+
}
|
|
2082
|
+
function getUrlInfo() {
|
|
2083
|
+
if (typeof window === "undefined") {
|
|
2084
|
+
return {
|
|
2085
|
+
href: "",
|
|
2086
|
+
pathname: "",
|
|
2087
|
+
search: "",
|
|
2088
|
+
hash: "",
|
|
2089
|
+
searchParams: {}
|
|
2090
|
+
};
|
|
2091
|
+
}
|
|
2092
|
+
const searchParams = {};
|
|
2093
|
+
const urlSearchParams = new URLSearchParams(window.location.search);
|
|
2094
|
+
urlSearchParams.forEach((value, key) => {
|
|
2095
|
+
searchParams[key] = value;
|
|
2096
|
+
});
|
|
2097
|
+
return {
|
|
2098
|
+
href: window.location.href,
|
|
2099
|
+
pathname: window.location.pathname,
|
|
2100
|
+
search: window.location.search,
|
|
2101
|
+
hash: window.location.hash,
|
|
2102
|
+
searchParams
|
|
2103
|
+
};
|
|
2104
|
+
}
|
|
2105
|
+
|
|
2106
|
+
// src/eventSystem.ts
|
|
2107
|
+
var builtInPlugins = {
|
|
2108
|
+
/**
|
|
2109
|
+
* Set state value
|
|
2110
|
+
* Mirrors: state-setState from actions.ts
|
|
2111
|
+
*/
|
|
2112
|
+
"state-setState": (action, ctx) => {
|
|
2113
|
+
const { stateConfig } = action.config || {};
|
|
2114
|
+
if (!stateConfig || !ctx.stateManager) return;
|
|
2115
|
+
const config = typeof stateConfig === "string" ? JSON.parse(stateConfig) : stateConfig;
|
|
2116
|
+
const { key, value, operation = "set" } = config;
|
|
2117
|
+
if (!key) return;
|
|
2118
|
+
switch (operation) {
|
|
2119
|
+
case "set":
|
|
2120
|
+
ctx.stateManager.set(key, value, ctx.elementId);
|
|
2121
|
+
break;
|
|
2122
|
+
case "merge":
|
|
2123
|
+
ctx.stateManager.merge(key, value, false, ctx.elementId);
|
|
2124
|
+
break;
|
|
2125
|
+
case "deepMerge":
|
|
2126
|
+
ctx.stateManager.merge(key, value, true, ctx.elementId);
|
|
2127
|
+
break;
|
|
2128
|
+
case "toggle":
|
|
2129
|
+
ctx.stateManager.toggle(key, ctx.elementId);
|
|
2130
|
+
break;
|
|
2131
|
+
case "append":
|
|
2132
|
+
ctx.stateManager.append(key, value, ctx.elementId);
|
|
2133
|
+
break;
|
|
2134
|
+
case "prepend":
|
|
2135
|
+
ctx.stateManager.prepend(key, value, ctx.elementId);
|
|
2136
|
+
break;
|
|
2137
|
+
case "delete":
|
|
2138
|
+
ctx.stateManager.delete(key, ctx.elementId);
|
|
2139
|
+
break;
|
|
2140
|
+
case "increment":
|
|
2141
|
+
const currentVal = ctx.stateManager.get(key) || 0;
|
|
2142
|
+
ctx.stateManager.set(key, currentVal + (value || 1), ctx.elementId);
|
|
2143
|
+
break;
|
|
2144
|
+
case "decrement":
|
|
2145
|
+
const currVal = ctx.stateManager.get(key) || 0;
|
|
2146
|
+
ctx.stateManager.set(key, currVal - (value || 1), ctx.elementId);
|
|
2147
|
+
break;
|
|
2148
|
+
}
|
|
2149
|
+
return { success: true, key, operation };
|
|
2150
|
+
},
|
|
2151
|
+
/**
|
|
2152
|
+
* Navigate to URL
|
|
2153
|
+
* Mirrors: navigateTo from actions.ts
|
|
2154
|
+
*/
|
|
2155
|
+
"navigateTo": (action, ctx) => {
|
|
2156
|
+
const { url, replace, newTab, state } = action.config || {};
|
|
2157
|
+
if (!url) return;
|
|
2158
|
+
navigateTo(url, { replace, newTab, state });
|
|
2159
|
+
return { success: true, url };
|
|
2160
|
+
},
|
|
2161
|
+
/**
|
|
2162
|
+
* Set localStorage value
|
|
2163
|
+
*/
|
|
2164
|
+
"localStorage-set": (action, ctx) => {
|
|
2165
|
+
const { key, value } = action.config || {};
|
|
2166
|
+
if (!key) return;
|
|
2167
|
+
setLocalStorage(key, value);
|
|
2168
|
+
return { success: true, key };
|
|
2169
|
+
},
|
|
2170
|
+
/**
|
|
2171
|
+
* Get localStorage value
|
|
2172
|
+
*/
|
|
2173
|
+
"localStorage-get": (action, ctx) => {
|
|
2174
|
+
const { key, defaultValue } = action.config || {};
|
|
2175
|
+
if (!key) return defaultValue;
|
|
2176
|
+
return getLocalStorage(key, defaultValue);
|
|
2177
|
+
},
|
|
2178
|
+
/**
|
|
2179
|
+
* Remove localStorage value
|
|
2180
|
+
*/
|
|
2181
|
+
"localStorage-remove": (action, ctx) => {
|
|
2182
|
+
const { key } = action.config || {};
|
|
2183
|
+
if (!key) return;
|
|
2184
|
+
if (typeof localStorage !== "undefined") {
|
|
2185
|
+
localStorage.removeItem(key);
|
|
2186
|
+
}
|
|
2187
|
+
return { success: true, key };
|
|
2188
|
+
},
|
|
2189
|
+
/**
|
|
2190
|
+
* Set sessionStorage value
|
|
2191
|
+
*/
|
|
2192
|
+
"sessionStorage-set": (action, ctx) => {
|
|
2193
|
+
const { key, value } = action.config || {};
|
|
2194
|
+
if (!key) return;
|
|
2195
|
+
setSessionStorage(key, value);
|
|
2196
|
+
return { success: true, key };
|
|
2197
|
+
},
|
|
2198
|
+
/**
|
|
2199
|
+
* Get sessionStorage value
|
|
2200
|
+
*/
|
|
2201
|
+
"sessionStorage-get": (action, ctx) => {
|
|
2202
|
+
const { key, defaultValue } = action.config || {};
|
|
2203
|
+
if (!key) return defaultValue;
|
|
2204
|
+
return getSessionStorage(key, defaultValue);
|
|
2205
|
+
},
|
|
2206
|
+
/**
|
|
2207
|
+
* Console log (for debugging)
|
|
2208
|
+
*/
|
|
2209
|
+
"console-log": (action, ctx) => {
|
|
2210
|
+
const { message, data } = action.config || {};
|
|
2211
|
+
console.log("[Servly]", message, data);
|
|
2212
|
+
return { success: true };
|
|
2213
|
+
},
|
|
2214
|
+
/**
|
|
2215
|
+
* Show alert
|
|
2216
|
+
*/
|
|
2217
|
+
"alert": (action, ctx) => {
|
|
2218
|
+
const { message } = action.config || {};
|
|
2219
|
+
if (typeof alert !== "undefined") {
|
|
2220
|
+
alert(message);
|
|
2221
|
+
}
|
|
2222
|
+
return { success: true };
|
|
2223
|
+
},
|
|
2224
|
+
/**
|
|
2225
|
+
* Copy to clipboard
|
|
2226
|
+
*/
|
|
2227
|
+
"clipboard-copy": async (action, ctx) => {
|
|
2228
|
+
const { text } = action.config || {};
|
|
2229
|
+
if (!text || typeof navigator === "undefined") return { success: false };
|
|
2230
|
+
try {
|
|
2231
|
+
await navigator.clipboard.writeText(text);
|
|
2232
|
+
return { success: true };
|
|
2233
|
+
} catch (error) {
|
|
2234
|
+
return { success: false, error };
|
|
2235
|
+
}
|
|
2236
|
+
},
|
|
2237
|
+
/**
|
|
2238
|
+
* Scroll to element
|
|
2239
|
+
*/
|
|
2240
|
+
"scrollTo": (action, ctx) => {
|
|
2241
|
+
const { selector, behavior = "smooth", block = "start" } = action.config || {};
|
|
2242
|
+
if (!selector || typeof document === "undefined") return;
|
|
2243
|
+
const element = document.querySelector(selector);
|
|
2244
|
+
if (element) {
|
|
2245
|
+
element.scrollIntoView({ behavior, block });
|
|
2246
|
+
return { success: true };
|
|
2247
|
+
}
|
|
2248
|
+
return { success: false, error: "Element not found" };
|
|
2249
|
+
},
|
|
2250
|
+
/**
|
|
2251
|
+
* Focus element
|
|
2252
|
+
*/
|
|
2253
|
+
"focus": (action, ctx) => {
|
|
2254
|
+
const { selector } = action.config || {};
|
|
2255
|
+
if (!selector || typeof document === "undefined") return;
|
|
2256
|
+
const element = document.querySelector(selector);
|
|
2257
|
+
if (element && typeof element.focus === "function") {
|
|
2258
|
+
element.focus();
|
|
2259
|
+
return { success: true };
|
|
2260
|
+
}
|
|
2261
|
+
return { success: false, error: "Element not found" };
|
|
2262
|
+
},
|
|
2263
|
+
/**
|
|
2264
|
+
* Blur element
|
|
2265
|
+
*/
|
|
2266
|
+
"blur": (action, ctx) => {
|
|
2267
|
+
const { selector } = action.config || {};
|
|
2268
|
+
if (!selector || typeof document === "undefined") return;
|
|
2269
|
+
const element = document.querySelector(selector);
|
|
2270
|
+
if (element && typeof element.blur === "function") {
|
|
2271
|
+
element.blur();
|
|
2272
|
+
return { success: true };
|
|
2273
|
+
}
|
|
2274
|
+
return { success: false, error: "Element not found" };
|
|
2275
|
+
},
|
|
2276
|
+
/**
|
|
2277
|
+
* Add class to element
|
|
2278
|
+
*/
|
|
2279
|
+
"addClass": (action, ctx) => {
|
|
2280
|
+
const { selector, className } = action.config || {};
|
|
2281
|
+
if (!selector || !className || typeof document === "undefined") return;
|
|
2282
|
+
const element = document.querySelector(selector);
|
|
2283
|
+
if (element) {
|
|
2284
|
+
element.classList.add(className);
|
|
2285
|
+
return { success: true };
|
|
2286
|
+
}
|
|
2287
|
+
return { success: false, error: "Element not found" };
|
|
2288
|
+
},
|
|
2289
|
+
/**
|
|
2290
|
+
* Remove class from element
|
|
2291
|
+
*/
|
|
2292
|
+
"removeClass": (action, ctx) => {
|
|
2293
|
+
const { selector, className } = action.config || {};
|
|
2294
|
+
if (!selector || !className || typeof document === "undefined") return;
|
|
2295
|
+
const element = document.querySelector(selector);
|
|
2296
|
+
if (element) {
|
|
2297
|
+
element.classList.remove(className);
|
|
2298
|
+
return { success: true };
|
|
2299
|
+
}
|
|
2300
|
+
return { success: false, error: "Element not found" };
|
|
2301
|
+
},
|
|
2302
|
+
/**
|
|
2303
|
+
* Toggle class on element
|
|
2304
|
+
*/
|
|
2305
|
+
"toggleClass": (action, ctx) => {
|
|
2306
|
+
const { selector, className } = action.config || {};
|
|
2307
|
+
if (!selector || !className || typeof document === "undefined") return;
|
|
2308
|
+
const element = document.querySelector(selector);
|
|
2309
|
+
if (element) {
|
|
2310
|
+
element.classList.toggle(className);
|
|
2311
|
+
return { success: true };
|
|
2312
|
+
}
|
|
2313
|
+
return { success: false, error: "Element not found" };
|
|
2314
|
+
},
|
|
2315
|
+
/**
|
|
2316
|
+
* Set element attribute
|
|
2317
|
+
*/
|
|
2318
|
+
"setAttribute": (action, ctx) => {
|
|
2319
|
+
const { selector, attribute, value } = action.config || {};
|
|
2320
|
+
if (!selector || !attribute || typeof document === "undefined") return;
|
|
2321
|
+
const element = document.querySelector(selector);
|
|
2322
|
+
if (element) {
|
|
2323
|
+
element.setAttribute(attribute, value);
|
|
2324
|
+
return { success: true };
|
|
2325
|
+
}
|
|
2326
|
+
return { success: false, error: "Element not found" };
|
|
2327
|
+
},
|
|
2328
|
+
/**
|
|
2329
|
+
* Remove element attribute
|
|
2330
|
+
*/
|
|
2331
|
+
"removeAttribute": (action, ctx) => {
|
|
2332
|
+
const { selector, attribute } = action.config || {};
|
|
2333
|
+
if (!selector || !attribute || typeof document === "undefined") return;
|
|
2334
|
+
const element = document.querySelector(selector);
|
|
2335
|
+
if (element) {
|
|
2336
|
+
element.removeAttribute(attribute);
|
|
2337
|
+
return { success: true };
|
|
2338
|
+
}
|
|
2339
|
+
return { success: false, error: "Element not found" };
|
|
2340
|
+
},
|
|
2341
|
+
/**
|
|
2342
|
+
* Dispatch custom event
|
|
2343
|
+
*/
|
|
2344
|
+
"dispatchEvent": (action, ctx) => {
|
|
2345
|
+
const { selector, eventName, detail } = action.config || {};
|
|
2346
|
+
if (!eventName || typeof document === "undefined") return;
|
|
2347
|
+
const target = selector ? document.querySelector(selector) : document;
|
|
2348
|
+
if (target) {
|
|
2349
|
+
const event = new CustomEvent(eventName, { detail, bubbles: true });
|
|
2350
|
+
target.dispatchEvent(event);
|
|
2351
|
+
return { success: true };
|
|
2352
|
+
}
|
|
2353
|
+
return { success: false, error: "Target not found" };
|
|
2354
|
+
},
|
|
2355
|
+
/**
|
|
2356
|
+
* Delay execution
|
|
2357
|
+
*/
|
|
2358
|
+
"delay": async (action, ctx) => {
|
|
2359
|
+
const { ms = 0 } = action.config || {};
|
|
2360
|
+
await new Promise((resolve) => setTimeout(resolve, ms));
|
|
2361
|
+
return { success: true };
|
|
2362
|
+
},
|
|
2363
|
+
/**
|
|
2364
|
+
* Conditional execution
|
|
2365
|
+
*/
|
|
2366
|
+
"condition": (action, ctx) => {
|
|
2367
|
+
const { condition, thenActions, elseActions } = action.config || {};
|
|
2368
|
+
return { condition, thenActions, elseActions };
|
|
2369
|
+
}
|
|
2370
|
+
};
|
|
2371
|
+
var EventSystem = class {
|
|
2372
|
+
constructor(config = {}) {
|
|
2373
|
+
this.debounceTimers = /* @__PURE__ */ new Map();
|
|
2374
|
+
this.throttleTimers = /* @__PURE__ */ new Map();
|
|
2375
|
+
this.config = config;
|
|
2376
|
+
this.pluginExecutors = {
|
|
2377
|
+
...builtInPlugins,
|
|
2378
|
+
...config.pluginExecutors
|
|
2379
|
+
};
|
|
2380
|
+
}
|
|
2381
|
+
/**
|
|
2382
|
+
* Register a custom plugin executor
|
|
2383
|
+
*/
|
|
2384
|
+
registerPlugin(key, executor) {
|
|
2385
|
+
this.pluginExecutors[key] = executor;
|
|
2386
|
+
}
|
|
2387
|
+
/**
|
|
2388
|
+
* Create an event handler from Servly plugin format
|
|
2389
|
+
*/
|
|
2390
|
+
createHandler(elementId, handlerConfig, context, options = {}) {
|
|
2391
|
+
return (event) => {
|
|
2392
|
+
if (handlerConfig.preventDefault || options.preventDefault) {
|
|
2393
|
+
event.preventDefault();
|
|
2394
|
+
}
|
|
2395
|
+
if (handlerConfig.stopPropagation || options.stopPropagation) {
|
|
2396
|
+
event.stopPropagation();
|
|
2397
|
+
}
|
|
2398
|
+
if (this.config.onEvent) {
|
|
2399
|
+
this.config.onEvent(event.type, elementId, event);
|
|
2400
|
+
}
|
|
2401
|
+
if (handlerConfig.plugins && handlerConfig.plugins.length > 0) {
|
|
2402
|
+
this.executePlugins(handlerConfig.plugins, {
|
|
2403
|
+
event,
|
|
2404
|
+
elementId,
|
|
2405
|
+
context,
|
|
2406
|
+
stateManager: this.config.stateManager
|
|
2407
|
+
});
|
|
2408
|
+
}
|
|
2409
|
+
};
|
|
2410
|
+
}
|
|
2411
|
+
/**
|
|
2412
|
+
* Execute a sequence of plugin actions
|
|
2413
|
+
*/
|
|
2414
|
+
async executePlugins(plugins, eventContext) {
|
|
2415
|
+
const results = [];
|
|
2416
|
+
for (const action of plugins) {
|
|
2417
|
+
try {
|
|
2418
|
+
const executor = this.pluginExecutors[action.key];
|
|
2419
|
+
if (executor) {
|
|
2420
|
+
const result = await executor(action, eventContext);
|
|
2421
|
+
results.push(result);
|
|
2422
|
+
if (this.config.onPluginExecute) {
|
|
2423
|
+
this.config.onPluginExecute(action, result);
|
|
2424
|
+
}
|
|
2425
|
+
} else {
|
|
2426
|
+
console.warn(`[EventSystem] Unknown plugin: ${action.key}`);
|
|
2427
|
+
results.push({ error: `Unknown plugin: ${action.key}` });
|
|
2428
|
+
}
|
|
2429
|
+
} catch (error) {
|
|
2430
|
+
console.error(`[EventSystem] Plugin error (${action.key}):`, error);
|
|
2431
|
+
results.push({ error });
|
|
2432
|
+
}
|
|
2433
|
+
}
|
|
2434
|
+
return results;
|
|
2435
|
+
}
|
|
2436
|
+
/**
|
|
2437
|
+
* Create a debounced event handler
|
|
2438
|
+
*/
|
|
2439
|
+
createDebouncedHandler(elementId, handler, delay) {
|
|
2440
|
+
const key = `${elementId}-debounce`;
|
|
2441
|
+
return (event) => {
|
|
2442
|
+
const existingTimer = this.debounceTimers.get(key);
|
|
2443
|
+
if (existingTimer) {
|
|
2444
|
+
clearTimeout(existingTimer);
|
|
2445
|
+
}
|
|
2446
|
+
const timer = setTimeout(() => {
|
|
2447
|
+
handler(event);
|
|
2448
|
+
this.debounceTimers.delete(key);
|
|
2449
|
+
}, delay);
|
|
2450
|
+
this.debounceTimers.set(key, timer);
|
|
2451
|
+
};
|
|
2452
|
+
}
|
|
2453
|
+
/**
|
|
2454
|
+
* Create a throttled event handler
|
|
2455
|
+
*/
|
|
2456
|
+
createThrottledHandler(elementId, handler, delay) {
|
|
2457
|
+
const key = `${elementId}-throttle`;
|
|
2458
|
+
return (event) => {
|
|
2459
|
+
if (this.throttleTimers.get(key)) {
|
|
2460
|
+
return;
|
|
2461
|
+
}
|
|
2462
|
+
handler(event);
|
|
2463
|
+
this.throttleTimers.set(key, true);
|
|
2464
|
+
setTimeout(() => {
|
|
2465
|
+
this.throttleTimers.delete(key);
|
|
2466
|
+
}, delay);
|
|
2467
|
+
};
|
|
2468
|
+
}
|
|
2469
|
+
/**
|
|
2470
|
+
* Clean up all timers
|
|
2471
|
+
*/
|
|
2472
|
+
destroy() {
|
|
2473
|
+
for (const timer of this.debounceTimers.values()) {
|
|
2474
|
+
clearTimeout(timer);
|
|
2475
|
+
}
|
|
2476
|
+
this.debounceTimers.clear();
|
|
2477
|
+
this.throttleTimers.clear();
|
|
2478
|
+
}
|
|
2479
|
+
};
|
|
2480
|
+
var EVENT_HANDLERS = {
|
|
2481
|
+
onClick: "click",
|
|
2482
|
+
onDoubleClick: "dblclick",
|
|
2483
|
+
onMouseDown: "mousedown",
|
|
2484
|
+
onMouseUp: "mouseup",
|
|
2485
|
+
onMouseEnter: "mouseenter",
|
|
2486
|
+
onMouseLeave: "mouseleave",
|
|
2487
|
+
onMouseMove: "mousemove",
|
|
2488
|
+
onMouseOver: "mouseover",
|
|
2489
|
+
onMouseOut: "mouseout",
|
|
2490
|
+
onKeyDown: "keydown",
|
|
2491
|
+
onKeyUp: "keyup",
|
|
2492
|
+
onKeyPress: "keypress",
|
|
2493
|
+
onFocus: "focus",
|
|
2494
|
+
onBlur: "blur",
|
|
2495
|
+
onChange: "change",
|
|
2496
|
+
onInput: "input",
|
|
2497
|
+
onSubmit: "submit",
|
|
2498
|
+
onReset: "reset",
|
|
2499
|
+
onScroll: "scroll",
|
|
2500
|
+
onWheel: "wheel",
|
|
2501
|
+
onDragStart: "dragstart",
|
|
2502
|
+
onDrag: "drag",
|
|
2503
|
+
onDragEnd: "dragend",
|
|
2504
|
+
onDragEnter: "dragenter",
|
|
2505
|
+
onDragLeave: "dragleave",
|
|
2506
|
+
onDragOver: "dragover",
|
|
2507
|
+
onDrop: "drop",
|
|
2508
|
+
onTouchStart: "touchstart",
|
|
2509
|
+
onTouchMove: "touchmove",
|
|
2510
|
+
onTouchEnd: "touchend",
|
|
2511
|
+
onTouchCancel: "touchcancel",
|
|
2512
|
+
onContextMenu: "contextmenu",
|
|
2513
|
+
onCopy: "copy",
|
|
2514
|
+
onCut: "cut",
|
|
2515
|
+
onPaste: "paste",
|
|
2516
|
+
onLoad: "load",
|
|
2517
|
+
onError: "error",
|
|
2518
|
+
onAnimationStart: "animationstart",
|
|
2519
|
+
onAnimationEnd: "animationend",
|
|
2520
|
+
onAnimationIteration: "animationiteration",
|
|
2521
|
+
onTransitionEnd: "transitionend"
|
|
2522
|
+
};
|
|
2523
|
+
function toDomEventName(reactEventName) {
|
|
2524
|
+
return EVENT_HANDLERS[reactEventName] || reactEventName.replace(/^on/, "").toLowerCase();
|
|
2525
|
+
}
|
|
2526
|
+
function toReactEventName(domEventName) {
|
|
2527
|
+
const entry = Object.entries(EVENT_HANDLERS).find(([, dom]) => dom === domEventName);
|
|
2528
|
+
return entry ? entry[0] : `on${domEventName.charAt(0).toUpperCase()}${domEventName.slice(1)}`;
|
|
2529
|
+
}
|
|
2530
|
+
var defaultEventSystem = null;
|
|
2531
|
+
function getEventSystem(config) {
|
|
2532
|
+
if (!defaultEventSystem || config) {
|
|
2533
|
+
defaultEventSystem = new EventSystem(config);
|
|
2534
|
+
}
|
|
2535
|
+
return defaultEventSystem;
|
|
2536
|
+
}
|
|
2537
|
+
function resetEventSystem() {
|
|
2538
|
+
if (defaultEventSystem) {
|
|
2539
|
+
defaultEventSystem.destroy();
|
|
2540
|
+
defaultEventSystem = null;
|
|
2541
|
+
}
|
|
2542
|
+
}
|
|
2543
|
+
|
|
2544
|
+
// src/overrides.ts
|
|
2545
|
+
var OverrideSystem = class {
|
|
2546
|
+
constructor(config = {}) {
|
|
2547
|
+
this.elementStates = /* @__PURE__ */ new Map();
|
|
2548
|
+
this.watchIntervals = /* @__PURE__ */ new Map();
|
|
2549
|
+
this.config = config;
|
|
2550
|
+
}
|
|
2551
|
+
/**
|
|
2552
|
+
* Get overrides from an element
|
|
2553
|
+
*/
|
|
2554
|
+
getOverrides(element) {
|
|
2555
|
+
const rawOverrides = element.configuration?._overrides_;
|
|
2556
|
+
if (!rawOverrides) return [];
|
|
2557
|
+
if (Array.isArray(rawOverrides)) {
|
|
2558
|
+
return rawOverrides.filter((o) => o && typeof o === "object");
|
|
2559
|
+
}
|
|
2560
|
+
if (typeof rawOverrides === "object") {
|
|
2561
|
+
console.warn("[OverrideSystem] Legacy object override format detected, ignoring");
|
|
2562
|
+
return [];
|
|
2563
|
+
}
|
|
2564
|
+
return [];
|
|
2565
|
+
}
|
|
2566
|
+
/**
|
|
2567
|
+
* Initialize overrides for an element (called on mount)
|
|
2568
|
+
*/
|
|
2569
|
+
async initializeElement(element, context) {
|
|
2570
|
+
const elementId = element.i;
|
|
2571
|
+
const overrides = this.getOverrides(element);
|
|
2572
|
+
if (overrides.length === 0) return;
|
|
2573
|
+
const state = {
|
|
2574
|
+
previousValues: /* @__PURE__ */ new Map(),
|
|
2575
|
+
initialized: false,
|
|
2576
|
+
abortController: new AbortController()
|
|
2577
|
+
};
|
|
2578
|
+
this.elementStates.set(elementId, state);
|
|
2579
|
+
const mountOverrides = overrides.filter((o) => !o.isCleanUp);
|
|
2580
|
+
overrides.forEach((override, index) => {
|
|
2581
|
+
if (override.isCleanUp) return;
|
|
2582
|
+
if (!override.dependencies || override.dependencies.length === 0) return;
|
|
2583
|
+
const initialValues = override.dependencies.map(
|
|
2584
|
+
(dep) => resolveTemplate(dep, context)
|
|
2585
|
+
);
|
|
2586
|
+
state.previousValues.set(index, initialValues);
|
|
2587
|
+
});
|
|
2588
|
+
await this.processOverrides(elementId, mountOverrides, context, "onMount");
|
|
2589
|
+
state.initialized = true;
|
|
2590
|
+
}
|
|
2591
|
+
/**
|
|
2592
|
+
* Check and process dependency changes for an element
|
|
2593
|
+
*/
|
|
2594
|
+
async checkDependencies(element, context) {
|
|
2595
|
+
const elementId = element.i;
|
|
2596
|
+
const overrides = this.getOverrides(element);
|
|
2597
|
+
const state = this.elementStates.get(elementId);
|
|
2598
|
+
if (!state || !state.initialized) return;
|
|
2599
|
+
const overridesToTrigger = [];
|
|
2600
|
+
overrides.forEach((override, index) => {
|
|
2601
|
+
if (override.isCleanUp) return;
|
|
2602
|
+
if (!override.dependencies || override.dependencies.length === 0) return;
|
|
2603
|
+
const currentValues = override.dependencies.map(
|
|
2604
|
+
(dep) => resolveTemplate(dep, context)
|
|
2605
|
+
);
|
|
2606
|
+
const previousValues = state.previousValues.get(index) || [];
|
|
2607
|
+
let hasChanged = false;
|
|
2608
|
+
if (previousValues.length !== currentValues.length) {
|
|
2609
|
+
hasChanged = true;
|
|
2610
|
+
} else {
|
|
2611
|
+
for (let i = 0; i < currentValues.length; i++) {
|
|
2612
|
+
if (JSON.stringify(previousValues[i]) !== JSON.stringify(currentValues[i])) {
|
|
2613
|
+
hasChanged = true;
|
|
2614
|
+
break;
|
|
2615
|
+
}
|
|
2616
|
+
}
|
|
2617
|
+
}
|
|
2618
|
+
if (hasChanged) {
|
|
2619
|
+
overridesToTrigger.push(override);
|
|
2620
|
+
state.previousValues.set(index, currentValues);
|
|
2621
|
+
if (this.config.onDependencyChange) {
|
|
2622
|
+
this.config.onDependencyChange(elementId, override.dependencies, currentValues);
|
|
2623
|
+
}
|
|
2624
|
+
}
|
|
2625
|
+
});
|
|
2626
|
+
if (overridesToTrigger.length > 0) {
|
|
2627
|
+
await this.processOverrides(elementId, overridesToTrigger, context, "onDependencyChange");
|
|
2628
|
+
}
|
|
2629
|
+
}
|
|
2630
|
+
/**
|
|
2631
|
+
* Cleanup overrides for an element (called on unmount)
|
|
2632
|
+
*/
|
|
2633
|
+
async cleanupElement(element, context) {
|
|
2634
|
+
const elementId = element.i;
|
|
2635
|
+
const overrides = this.getOverrides(element);
|
|
2636
|
+
const state = this.elementStates.get(elementId);
|
|
2637
|
+
if (state?.abortController) {
|
|
2638
|
+
state.abortController.abort();
|
|
2639
|
+
}
|
|
2640
|
+
this.stopWatching(elementId);
|
|
2641
|
+
const cleanupOverrides = overrides.filter((o) => o.isCleanUp);
|
|
2642
|
+
if (cleanupOverrides.length > 0) {
|
|
2643
|
+
await this.processOverrides(elementId, cleanupOverrides, context, "onUnmount");
|
|
2644
|
+
}
|
|
2645
|
+
this.elementStates.delete(elementId);
|
|
2646
|
+
}
|
|
2647
|
+
/**
|
|
2648
|
+
* Process a list of overrides
|
|
2649
|
+
*/
|
|
2650
|
+
async processOverrides(elementId, overrides, context, eventType) {
|
|
2651
|
+
if (!overrides || overrides.length === 0) return;
|
|
2652
|
+
const validOverrides = overrides.filter((o) => o.plugins && o.plugins.length > 0);
|
|
2653
|
+
if (validOverrides.length === 0) return;
|
|
2654
|
+
const results = await Promise.allSettled(
|
|
2655
|
+
validOverrides.map(async (override) => {
|
|
2656
|
+
if (this.config.onOverrideTrigger) {
|
|
2657
|
+
this.config.onOverrideTrigger(elementId, override, eventType);
|
|
2658
|
+
}
|
|
2659
|
+
if (this.config.eventSystem && override.plugins) {
|
|
2660
|
+
const eventContext = {
|
|
2661
|
+
event: new CustomEvent(eventType),
|
|
2662
|
+
elementId,
|
|
2663
|
+
context,
|
|
2664
|
+
stateManager: this.config.stateManager
|
|
2665
|
+
};
|
|
2666
|
+
return this.config.eventSystem.executePlugins(override.plugins, eventContext);
|
|
2667
|
+
}
|
|
2668
|
+
return null;
|
|
2669
|
+
})
|
|
2670
|
+
);
|
|
2671
|
+
results.forEach((result, index) => {
|
|
2672
|
+
if (result.status === "rejected") {
|
|
2673
|
+
console.error(`[OverrideSystem] Override ${index} failed:`, result.reason);
|
|
2674
|
+
}
|
|
2675
|
+
});
|
|
2676
|
+
}
|
|
2677
|
+
/**
|
|
2678
|
+
* Start watching an element for dependency changes
|
|
2679
|
+
*/
|
|
2680
|
+
startWatching(element, context, intervalMs = 100) {
|
|
2681
|
+
const elementId = element.i;
|
|
2682
|
+
this.stopWatching(elementId);
|
|
2683
|
+
const overrides = this.getOverrides(element);
|
|
2684
|
+
const hasDependencies = overrides.some(
|
|
2685
|
+
(o) => !o.isCleanUp && o.dependencies && o.dependencies.length > 0
|
|
2686
|
+
);
|
|
2687
|
+
if (!hasDependencies) return;
|
|
2688
|
+
const interval = setInterval(() => {
|
|
2689
|
+
this.checkDependencies(element, context);
|
|
2690
|
+
}, intervalMs);
|
|
2691
|
+
this.watchIntervals.set(elementId, interval);
|
|
2692
|
+
}
|
|
2693
|
+
/**
|
|
2694
|
+
* Stop watching an element
|
|
2695
|
+
*/
|
|
2696
|
+
stopWatching(elementId) {
|
|
2697
|
+
const interval = this.watchIntervals.get(elementId);
|
|
2698
|
+
if (interval) {
|
|
2699
|
+
clearInterval(interval);
|
|
2700
|
+
this.watchIntervals.delete(elementId);
|
|
2701
|
+
}
|
|
2702
|
+
}
|
|
2703
|
+
/**
|
|
2704
|
+
* Destroy the override system
|
|
2705
|
+
*/
|
|
2706
|
+
destroy() {
|
|
2707
|
+
for (const interval of this.watchIntervals.values()) {
|
|
2708
|
+
clearInterval(interval);
|
|
2709
|
+
}
|
|
2710
|
+
this.watchIntervals.clear();
|
|
2711
|
+
for (const state of this.elementStates.values()) {
|
|
2712
|
+
if (state.abortController) {
|
|
2713
|
+
state.abortController.abort();
|
|
2714
|
+
}
|
|
2715
|
+
}
|
|
2716
|
+
this.elementStates.clear();
|
|
2717
|
+
}
|
|
2718
|
+
};
|
|
2719
|
+
function extractOverrideDependencies(element) {
|
|
2720
|
+
const overrides = element.configuration?._overrides_;
|
|
2721
|
+
if (!Array.isArray(overrides)) return [];
|
|
2722
|
+
const dependencies = [];
|
|
2723
|
+
for (const override of overrides) {
|
|
2724
|
+
if (override?.dependencies && Array.isArray(override.dependencies)) {
|
|
2725
|
+
dependencies.push(...override.dependencies);
|
|
2726
|
+
}
|
|
2727
|
+
}
|
|
2728
|
+
return [...new Set(dependencies)];
|
|
2729
|
+
}
|
|
2730
|
+
function hasOverrides(element) {
|
|
2731
|
+
const overrides = element.configuration?._overrides_;
|
|
2732
|
+
return Array.isArray(overrides) && overrides.length > 0;
|
|
2733
|
+
}
|
|
2734
|
+
function hasDependencyOverrides(element) {
|
|
2735
|
+
const overrides = element.configuration?._overrides_;
|
|
2736
|
+
if (!Array.isArray(overrides)) return false;
|
|
2737
|
+
return overrides.some(
|
|
2738
|
+
(o) => !o?.isCleanUp && o?.dependencies && o.dependencies.length > 0
|
|
2739
|
+
);
|
|
2740
|
+
}
|
|
2741
|
+
function getMountOverrides(element) {
|
|
2742
|
+
const overrides = element.configuration?._overrides_;
|
|
2743
|
+
if (!Array.isArray(overrides)) return [];
|
|
2744
|
+
return overrides.filter((o) => o && !o.isCleanUp);
|
|
2745
|
+
}
|
|
2746
|
+
function getCleanupOverrides(element) {
|
|
2747
|
+
const overrides = element.configuration?._overrides_;
|
|
2748
|
+
if (!Array.isArray(overrides)) return [];
|
|
2749
|
+
return overrides.filter((o) => o && o.isCleanUp);
|
|
2750
|
+
}
|
|
2751
|
+
var defaultOverrideSystem = null;
|
|
2752
|
+
function getOverrideSystem(config) {
|
|
2753
|
+
if (!defaultOverrideSystem || config) {
|
|
2754
|
+
defaultOverrideSystem = new OverrideSystem(config);
|
|
2755
|
+
}
|
|
2756
|
+
return defaultOverrideSystem;
|
|
2757
|
+
}
|
|
2758
|
+
function resetOverrideSystem() {
|
|
2759
|
+
if (defaultOverrideSystem) {
|
|
2760
|
+
defaultOverrideSystem.destroy();
|
|
2761
|
+
defaultOverrideSystem = null;
|
|
2762
|
+
}
|
|
2763
|
+
}
|
|
2764
|
+
|
|
2765
|
+
// src/renderer.ts
|
|
2766
|
+
var COMPONENT_TO_TAG = {
|
|
2767
|
+
container: "div",
|
|
2768
|
+
text: "span",
|
|
2769
|
+
button: "button",
|
|
2770
|
+
input: "input",
|
|
2771
|
+
image: "img",
|
|
2772
|
+
link: "a",
|
|
2773
|
+
form: "form",
|
|
2774
|
+
label: "label",
|
|
2775
|
+
textarea: "textarea",
|
|
2776
|
+
select: "select",
|
|
2777
|
+
option: "option",
|
|
2778
|
+
list: "ul",
|
|
2779
|
+
listItem: "li",
|
|
2780
|
+
heading: "h1",
|
|
2781
|
+
paragraph: "p",
|
|
2782
|
+
section: "section",
|
|
2783
|
+
article: "article",
|
|
2784
|
+
header: "header",
|
|
2785
|
+
footer: "footer",
|
|
2786
|
+
nav: "nav",
|
|
2787
|
+
aside: "aside",
|
|
2788
|
+
main: "main",
|
|
2789
|
+
span: "span",
|
|
2790
|
+
div: "div"
|
|
2791
|
+
};
|
|
2792
|
+
var SELF_CLOSING_TAGS = /* @__PURE__ */ new Set([
|
|
2793
|
+
"input",
|
|
2794
|
+
"img",
|
|
2795
|
+
"br",
|
|
2796
|
+
"hr",
|
|
2797
|
+
"area",
|
|
2798
|
+
"base",
|
|
2799
|
+
"col",
|
|
2800
|
+
"embed",
|
|
2801
|
+
"link",
|
|
2802
|
+
"meta",
|
|
2803
|
+
"param",
|
|
2804
|
+
"source",
|
|
2805
|
+
"track",
|
|
2806
|
+
"wbr"
|
|
2807
|
+
]);
|
|
2808
|
+
function getElementTag(element) {
|
|
2809
|
+
const config = element.configuration;
|
|
2810
|
+
if (config?.tag) {
|
|
2811
|
+
return config.tag;
|
|
2812
|
+
}
|
|
2813
|
+
if (element.componentId && COMPONENT_TO_TAG[element.componentId]) {
|
|
2814
|
+
return COMPONENT_TO_TAG[element.componentId];
|
|
2815
|
+
}
|
|
2816
|
+
if (element.isGroup) {
|
|
2817
|
+
return "div";
|
|
2818
|
+
}
|
|
2819
|
+
return "div";
|
|
2820
|
+
}
|
|
2821
|
+
function isSelfClosing(tag) {
|
|
2822
|
+
return SELF_CLOSING_TAGS.has(tag.toLowerCase());
|
|
2823
|
+
}
|
|
2824
|
+
function buildTree(elements) {
|
|
2825
|
+
const tree = /* @__PURE__ */ new Map();
|
|
2826
|
+
for (const element of elements) {
|
|
2827
|
+
const parentId = element.parent || null;
|
|
2828
|
+
if (!tree.has(parentId)) {
|
|
2829
|
+
tree.set(parentId, []);
|
|
2830
|
+
}
|
|
2831
|
+
tree.get(parentId).push(element);
|
|
2832
|
+
}
|
|
2833
|
+
return tree;
|
|
2834
|
+
}
|
|
2835
|
+
function getTextContent(element, context) {
|
|
2836
|
+
const config = element.configuration;
|
|
2837
|
+
if (config?.dynamicText) {
|
|
2838
|
+
return resolveTemplate(config.dynamicText, context);
|
|
2839
|
+
}
|
|
2840
|
+
if (config?.text) {
|
|
2841
|
+
if (hasTemplateSyntax(config.text)) {
|
|
2842
|
+
return resolveTemplate(config.text, context);
|
|
2843
|
+
}
|
|
2844
|
+
return config.text;
|
|
2845
|
+
}
|
|
2846
|
+
return "";
|
|
2847
|
+
}
|
|
2848
|
+
function applyAttributes(domElement, element, context) {
|
|
2849
|
+
const config = element.configuration || {};
|
|
2850
|
+
const attributeMap = [
|
|
2851
|
+
{ key: "id", attr: "id" },
|
|
2852
|
+
{ key: "src", dynamicKey: "dynamicSrc", attr: "src" },
|
|
2853
|
+
{ key: "alt", attr: "alt" },
|
|
2854
|
+
{ key: "href", dynamicKey: "dynamicHref", attr: "href" },
|
|
2855
|
+
{ key: "target", attr: "target" },
|
|
2856
|
+
{ key: "placeholder", attr: "placeholder" },
|
|
2857
|
+
{ key: "type", attr: "type" },
|
|
2858
|
+
{ key: "name", attr: "name" },
|
|
2859
|
+
{ key: "value", dynamicKey: "dynamicValue", attr: "value" }
|
|
2860
|
+
];
|
|
2861
|
+
for (const { key, dynamicKey, attr } of attributeMap) {
|
|
2862
|
+
const dynamicValue = dynamicKey ? config[dynamicKey] : void 0;
|
|
2863
|
+
if (dynamicValue !== void 0 && dynamicValue !== null && dynamicValue !== "") {
|
|
2864
|
+
const resolved = resolveTemplate(String(dynamicValue), context);
|
|
2865
|
+
if (resolved) {
|
|
2866
|
+
domElement.setAttribute(attr, resolved);
|
|
2867
|
+
}
|
|
2868
|
+
continue;
|
|
2869
|
+
}
|
|
2870
|
+
const staticValue = config[key];
|
|
2871
|
+
if (staticValue !== void 0 && staticValue !== null && staticValue !== "") {
|
|
2872
|
+
const resolved = hasTemplateSyntax(String(staticValue)) ? resolveTemplate(String(staticValue), context) : String(staticValue);
|
|
2873
|
+
if (resolved) {
|
|
2874
|
+
domElement.setAttribute(attr, resolved);
|
|
2875
|
+
}
|
|
2876
|
+
}
|
|
2877
|
+
}
|
|
2878
|
+
if (config.disabled) domElement.setAttribute("disabled", "");
|
|
2879
|
+
if (config.required) domElement.setAttribute("required", "");
|
|
2880
|
+
if (config.readOnly) domElement.setAttribute("readonly", "");
|
|
2881
|
+
for (const [key, value] of Object.entries(config)) {
|
|
2882
|
+
if (key.startsWith("data-") && value !== void 0) {
|
|
2883
|
+
const resolved = hasTemplateSyntax(String(value)) ? resolveTemplate(String(value), context) : String(value);
|
|
2884
|
+
domElement.setAttribute(key, resolved);
|
|
2885
|
+
}
|
|
2886
|
+
}
|
|
2887
|
+
for (const [key, value] of Object.entries(config)) {
|
|
2888
|
+
if (key.startsWith("aria-") && value !== void 0) {
|
|
2889
|
+
const resolved = hasTemplateSyntax(String(value)) ? resolveTemplate(String(value), context) : String(value);
|
|
1470
2890
|
domElement.setAttribute(key, resolved);
|
|
1471
2891
|
}
|
|
1472
2892
|
}
|
|
@@ -1491,7 +2911,7 @@ function resolveFunctionBinding(binding, context) {
|
|
|
1491
2911
|
}
|
|
1492
2912
|
return void 0;
|
|
1493
2913
|
}
|
|
1494
|
-
function attachEventHandlers(domElement, element, eventHandlers, context, elementState, isRootElement = false) {
|
|
2914
|
+
function attachEventHandlers(domElement, element, eventHandlers, context, elementState, isRootElement = false, state) {
|
|
1495
2915
|
const elementId = element.i;
|
|
1496
2916
|
if (eventHandlers && eventHandlers[elementId]) {
|
|
1497
2917
|
const handlers = eventHandlers[elementId];
|
|
@@ -1501,6 +2921,28 @@ function attachEventHandlers(domElement, element, eventHandlers, context, elemen
|
|
|
1501
2921
|
domElement.addEventListener(domEventName, handler);
|
|
1502
2922
|
}
|
|
1503
2923
|
}
|
|
2924
|
+
const config = element.configuration || {};
|
|
2925
|
+
for (const eventPropName of Object.keys(EVENT_HANDLERS)) {
|
|
2926
|
+
const handlerConfig = config[eventPropName];
|
|
2927
|
+
if (handlerConfig && handlerConfig.plugins && handlerConfig.plugins.length > 0) {
|
|
2928
|
+
const domEventName = toDomEventName(eventPropName);
|
|
2929
|
+
if (!elementState.eventListeners.has(domEventName)) {
|
|
2930
|
+
if (state?.eventSystem) {
|
|
2931
|
+
const handler = state.eventSystem.createHandler(elementId, handlerConfig, context);
|
|
2932
|
+
elementState.eventListeners.set(domEventName, handler);
|
|
2933
|
+
domElement.addEventListener(domEventName, handler);
|
|
2934
|
+
} else {
|
|
2935
|
+
const handler = (e) => {
|
|
2936
|
+
if (handlerConfig.preventDefault) e.preventDefault();
|
|
2937
|
+
if (handlerConfig.stopPropagation) e.stopPropagation();
|
|
2938
|
+
console.log(`[Servly] Event ${eventPropName} triggered on ${elementId}`, handlerConfig.plugins);
|
|
2939
|
+
};
|
|
2940
|
+
elementState.eventListeners.set(domEventName, handler);
|
|
2941
|
+
domElement.addEventListener(domEventName, handler);
|
|
2942
|
+
}
|
|
2943
|
+
}
|
|
2944
|
+
}
|
|
2945
|
+
}
|
|
1504
2946
|
const bindings = element.configuration?.bindings?.inputs;
|
|
1505
2947
|
if (bindings) {
|
|
1506
2948
|
for (const [propName, binding] of Object.entries(bindings)) {
|
|
@@ -1535,10 +2977,16 @@ function detachEventHandlers(elementState) {
|
|
|
1535
2977
|
}
|
|
1536
2978
|
elementState.eventListeners.clear();
|
|
1537
2979
|
}
|
|
1538
|
-
function createElement(element, context, eventHandlers, isRootElement = false) {
|
|
2980
|
+
function createElement(element, context, eventHandlers, isRootElement = false, state) {
|
|
1539
2981
|
const tag = getElementTag(element);
|
|
1540
2982
|
const domElement = document.createElement(tag);
|
|
1541
2983
|
domElement.setAttribute("data-servly-id", element.i);
|
|
2984
|
+
const slotName = element.slotName || element.configuration?.slotName;
|
|
2985
|
+
if (element.componentId === "slot" || slotName) {
|
|
2986
|
+
const name = slotName || element.i;
|
|
2987
|
+
domElement.setAttribute("data-slot", name);
|
|
2988
|
+
domElement.setAttribute("data-servly-slot", "true");
|
|
2989
|
+
}
|
|
1542
2990
|
const styles = buildElementStyles(element, context);
|
|
1543
2991
|
applyStyles(domElement, styles);
|
|
1544
2992
|
const className = buildClassName(element, context);
|
|
@@ -1561,7 +3009,7 @@ function createElement(element, context, eventHandlers, isRootElement = false) {
|
|
|
1561
3009
|
textContent,
|
|
1562
3010
|
eventListeners: /* @__PURE__ */ new Map()
|
|
1563
3011
|
};
|
|
1564
|
-
attachEventHandlers(domElement, element, eventHandlers, context, elementState, isRootElement);
|
|
3012
|
+
attachEventHandlers(domElement, element, eventHandlers, context, elementState, isRootElement, state);
|
|
1565
3013
|
return elementState;
|
|
1566
3014
|
}
|
|
1567
3015
|
var globalRenderingStack = /* @__PURE__ */ new Set();
|
|
@@ -1622,8 +3070,81 @@ function renderComponentRef(element, container, context, state) {
|
|
|
1622
3070
|
globalRenderingStack.delete(refId);
|
|
1623
3071
|
}
|
|
1624
3072
|
}
|
|
3073
|
+
function extractViewId(binding) {
|
|
3074
|
+
if (!binding) return null;
|
|
3075
|
+
if (typeof binding === "string") return binding;
|
|
3076
|
+
if (binding.source === "static" && typeof binding.value === "string") return binding.value;
|
|
3077
|
+
if (binding.source === "node" && binding.binding?.viewId) return binding.binding.viewId;
|
|
3078
|
+
if (binding.source === "view" && typeof binding.value === "string") return binding.value;
|
|
3079
|
+
if (binding.viewId) return binding.viewId;
|
|
3080
|
+
if (binding.type === "view" && binding.viewId) return binding.viewId;
|
|
3081
|
+
return null;
|
|
3082
|
+
}
|
|
3083
|
+
function resolveComponentViewInputs(bindings, context, parentInputs) {
|
|
3084
|
+
if (!bindings) return {};
|
|
3085
|
+
const resolved = {};
|
|
3086
|
+
for (const [key, binding] of Object.entries(bindings)) {
|
|
3087
|
+
if (!binding) continue;
|
|
3088
|
+
const source = (binding.source || "").toLowerCase();
|
|
3089
|
+
switch (source) {
|
|
3090
|
+
case "static":
|
|
3091
|
+
case "value":
|
|
3092
|
+
case "constant":
|
|
3093
|
+
resolved[key] = binding.value;
|
|
3094
|
+
break;
|
|
3095
|
+
case "state":
|
|
3096
|
+
if (binding.path && context.state) {
|
|
3097
|
+
resolved[key] = getValueByPath2(context.state, binding.path);
|
|
3098
|
+
}
|
|
3099
|
+
break;
|
|
3100
|
+
case "props":
|
|
3101
|
+
case "parent":
|
|
3102
|
+
case "input":
|
|
3103
|
+
if (binding.path) {
|
|
3104
|
+
const source2 = parentInputs || context.props;
|
|
3105
|
+
const resolvedValue = getValueByPath2(source2, binding.path);
|
|
3106
|
+
if (resolvedValue !== void 0) {
|
|
3107
|
+
resolved[key] = resolvedValue;
|
|
3108
|
+
} else {
|
|
3109
|
+
resolved[key] = binding.value;
|
|
3110
|
+
}
|
|
3111
|
+
} else {
|
|
3112
|
+
resolved[key] = binding.value;
|
|
3113
|
+
}
|
|
3114
|
+
break;
|
|
3115
|
+
case "node":
|
|
3116
|
+
if (binding.binding?.viewId) {
|
|
3117
|
+
resolved[key] = binding.binding.viewId;
|
|
3118
|
+
} else if (binding.binding) {
|
|
3119
|
+
resolved[key] = binding.binding;
|
|
3120
|
+
} else {
|
|
3121
|
+
resolved[key] = binding.value;
|
|
3122
|
+
}
|
|
3123
|
+
break;
|
|
3124
|
+
default:
|
|
3125
|
+
resolved[key] = binding.value;
|
|
3126
|
+
}
|
|
3127
|
+
}
|
|
3128
|
+
return resolved;
|
|
3129
|
+
}
|
|
3130
|
+
function getValueByPath2(obj, path) {
|
|
3131
|
+
if (!obj || !path) return void 0;
|
|
3132
|
+
const parts = path.split(".");
|
|
3133
|
+
let current = obj;
|
|
3134
|
+
for (const part of parts) {
|
|
3135
|
+
if (current === null || current === void 0) return void 0;
|
|
3136
|
+
current = current[part];
|
|
3137
|
+
}
|
|
3138
|
+
return current;
|
|
3139
|
+
}
|
|
1625
3140
|
function renderElement(element, tree, context, eventHandlers, elementStates, state, isRootElement = false) {
|
|
1626
|
-
|
|
3141
|
+
if (element.isComponentView) {
|
|
3142
|
+
return renderComponentViewElement(element, tree, context, eventHandlers, elementStates, state, isRootElement);
|
|
3143
|
+
}
|
|
3144
|
+
if (element.componentId === "slot") {
|
|
3145
|
+
return renderSlotElement(element, tree, context, eventHandlers, elementStates, state, isRootElement);
|
|
3146
|
+
}
|
|
3147
|
+
const elementState = createElement(element, context, eventHandlers, isRootElement, state);
|
|
1627
3148
|
elementStates.set(element.i, elementState);
|
|
1628
3149
|
const config = element.configuration;
|
|
1629
3150
|
if (config?.componentViewRef) {
|
|
@@ -1640,8 +3161,147 @@ function renderElement(element, tree, context, eventHandlers, elementStates, sta
|
|
|
1640
3161
|
}
|
|
1641
3162
|
return elementState.domElement;
|
|
1642
3163
|
}
|
|
3164
|
+
function renderComponentViewElement(element, tree, context, eventHandlers, elementStates, state, isRootElement) {
|
|
3165
|
+
const componentViewId = `${element.componentId}-${element.i}`;
|
|
3166
|
+
if (globalRenderingStack.has(componentViewId)) {
|
|
3167
|
+
const placeholder = document.createElement("div");
|
|
3168
|
+
placeholder.className = "border-2 border-red-500 border-dashed p-4 bg-red-50";
|
|
3169
|
+
placeholder.innerHTML = `<p class="text-red-600 text-sm">Recursive component: ${element.componentId}</p>`;
|
|
3170
|
+
return placeholder;
|
|
3171
|
+
}
|
|
3172
|
+
let viewLayout;
|
|
3173
|
+
if (state.views?.has(element.componentId)) {
|
|
3174
|
+
const view = state.views.get(element.componentId);
|
|
3175
|
+
viewLayout = view?.layout;
|
|
3176
|
+
}
|
|
3177
|
+
if (!viewLayout && state.componentRegistry) {
|
|
3178
|
+
const component = state.componentRegistry.get(element.componentId);
|
|
3179
|
+
if (component) {
|
|
3180
|
+
viewLayout = component.layout;
|
|
3181
|
+
}
|
|
3182
|
+
}
|
|
3183
|
+
if (!viewLayout) {
|
|
3184
|
+
const placeholder = document.createElement("div");
|
|
3185
|
+
placeholder.className = "border-2 border-yellow-500 border-dashed p-4 bg-yellow-50";
|
|
3186
|
+
placeholder.innerHTML = `<p class="text-yellow-600 text-sm">Component not found: ${element.componentId}</p>`;
|
|
3187
|
+
return placeholder;
|
|
3188
|
+
}
|
|
3189
|
+
const bindings = element.configuration?.bindings?.inputs || {};
|
|
3190
|
+
const resolvedInputs = resolveComponentViewInputs(bindings, context, context.props);
|
|
3191
|
+
const componentContext = {
|
|
3192
|
+
props: { ...context.props, ...resolvedInputs },
|
|
3193
|
+
state: context.state,
|
|
3194
|
+
context: context.context
|
|
3195
|
+
};
|
|
3196
|
+
globalRenderingStack.add(componentViewId);
|
|
3197
|
+
try {
|
|
3198
|
+
const wrapper = document.createElement("div");
|
|
3199
|
+
wrapper.id = element.i;
|
|
3200
|
+
wrapper.className = "contents";
|
|
3201
|
+
const viewTree = buildTree(viewLayout);
|
|
3202
|
+
const viewRoots = viewLayout.filter((el) => !el.parent || el.parent === null);
|
|
3203
|
+
const nestedState = {
|
|
3204
|
+
...state,
|
|
3205
|
+
elements: viewLayout,
|
|
3206
|
+
context: componentContext,
|
|
3207
|
+
elementStates: /* @__PURE__ */ new Map(),
|
|
3208
|
+
rootElement: null,
|
|
3209
|
+
renderingStack: new Set(state.renderingStack)
|
|
3210
|
+
};
|
|
3211
|
+
nestedState.renderingStack.add(componentViewId);
|
|
3212
|
+
for (const root of viewRoots) {
|
|
3213
|
+
const rootElement = renderElement(
|
|
3214
|
+
root,
|
|
3215
|
+
viewTree,
|
|
3216
|
+
componentContext,
|
|
3217
|
+
eventHandlers,
|
|
3218
|
+
nestedState.elementStates,
|
|
3219
|
+
nestedState,
|
|
3220
|
+
true
|
|
3221
|
+
);
|
|
3222
|
+
wrapper.appendChild(rootElement);
|
|
3223
|
+
}
|
|
3224
|
+
const elementState = {
|
|
3225
|
+
element,
|
|
3226
|
+
domElement: wrapper,
|
|
3227
|
+
styles: {},
|
|
3228
|
+
className: "contents",
|
|
3229
|
+
textContent: "",
|
|
3230
|
+
eventListeners: /* @__PURE__ */ new Map()
|
|
3231
|
+
};
|
|
3232
|
+
elementStates.set(element.i, elementState);
|
|
3233
|
+
return wrapper;
|
|
3234
|
+
} finally {
|
|
3235
|
+
globalRenderingStack.delete(componentViewId);
|
|
3236
|
+
}
|
|
3237
|
+
}
|
|
3238
|
+
function renderSlotElement(element, tree, context, eventHandlers, elementStates, state, isRootElement) {
|
|
3239
|
+
const elementState = createElement(element, context, eventHandlers, isRootElement, state);
|
|
3240
|
+
elementStates.set(element.i, elementState);
|
|
3241
|
+
const bindings = element.configuration?.bindings?.inputs || {};
|
|
3242
|
+
let childViewId = extractViewId(bindings.child) || extractViewId(bindings.children) || extractViewId(bindings.content);
|
|
3243
|
+
if (!childViewId && context.props) {
|
|
3244
|
+
childViewId = extractViewId(context.props.child) || extractViewId(context.props.children) || extractViewId(context.props.content);
|
|
3245
|
+
}
|
|
3246
|
+
if (childViewId && typeof childViewId === "string") {
|
|
3247
|
+
let viewLayout;
|
|
3248
|
+
if (state.views?.has(childViewId)) {
|
|
3249
|
+
const view = state.views.get(childViewId);
|
|
3250
|
+
viewLayout = view?.layout;
|
|
3251
|
+
}
|
|
3252
|
+
if (!viewLayout && state.componentRegistry) {
|
|
3253
|
+
const component = state.componentRegistry.get(childViewId);
|
|
3254
|
+
if (component) {
|
|
3255
|
+
viewLayout = component.layout;
|
|
3256
|
+
}
|
|
3257
|
+
}
|
|
3258
|
+
if (viewLayout) {
|
|
3259
|
+
const viewTree = buildTree(viewLayout);
|
|
3260
|
+
const viewRoots = viewLayout.filter((el) => !el.parent || el.parent === null);
|
|
3261
|
+
const nestedState = {
|
|
3262
|
+
...state,
|
|
3263
|
+
elements: viewLayout,
|
|
3264
|
+
elementStates: /* @__PURE__ */ new Map(),
|
|
3265
|
+
rootElement: null
|
|
3266
|
+
};
|
|
3267
|
+
for (const root of viewRoots) {
|
|
3268
|
+
const rootElement = renderElement(
|
|
3269
|
+
root,
|
|
3270
|
+
viewTree,
|
|
3271
|
+
context,
|
|
3272
|
+
eventHandlers,
|
|
3273
|
+
nestedState.elementStates,
|
|
3274
|
+
nestedState,
|
|
3275
|
+
false
|
|
3276
|
+
);
|
|
3277
|
+
elementState.domElement.appendChild(rootElement);
|
|
3278
|
+
}
|
|
3279
|
+
return elementState.domElement;
|
|
3280
|
+
}
|
|
3281
|
+
}
|
|
3282
|
+
const children = tree.get(element.i) || [];
|
|
3283
|
+
for (const child of children) {
|
|
3284
|
+
const childElement = renderElement(child, tree, context, eventHandlers, elementStates, state, false);
|
|
3285
|
+
elementState.domElement.appendChild(childElement);
|
|
3286
|
+
}
|
|
3287
|
+
return elementState.domElement;
|
|
3288
|
+
}
|
|
1643
3289
|
function render(options) {
|
|
1644
|
-
const {
|
|
3290
|
+
const {
|
|
3291
|
+
container,
|
|
3292
|
+
elements,
|
|
3293
|
+
context,
|
|
3294
|
+
eventHandlers,
|
|
3295
|
+
componentRegistry,
|
|
3296
|
+
onDependencyNeeded,
|
|
3297
|
+
views,
|
|
3298
|
+
enableStateManager,
|
|
3299
|
+
initialState,
|
|
3300
|
+
onStateChange,
|
|
3301
|
+
pluginExecutors,
|
|
3302
|
+
onNavigate,
|
|
3303
|
+
onApiCall
|
|
3304
|
+
} = options;
|
|
1645
3305
|
const startTime = performance.now();
|
|
1646
3306
|
const memorySampler = getMemorySampler();
|
|
1647
3307
|
const longTaskObserver = getLongTaskObserver();
|
|
@@ -1650,6 +3310,29 @@ function render(options) {
|
|
|
1650
3310
|
const rootElements = elements.filter((el) => !el.parent || el.parent === null);
|
|
1651
3311
|
const componentId = rootElements[0]?.componentId || "unknown";
|
|
1652
3312
|
const version = "latest";
|
|
3313
|
+
let stateManager;
|
|
3314
|
+
if (enableStateManager) {
|
|
3315
|
+
stateManager = new StateManager({
|
|
3316
|
+
initialState: initialState || context.state,
|
|
3317
|
+
onStateChange: onStateChange ? (event) => onStateChange({
|
|
3318
|
+
key: event.key,
|
|
3319
|
+
value: event.value,
|
|
3320
|
+
previousValue: event.previousValue
|
|
3321
|
+
}) : void 0
|
|
3322
|
+
});
|
|
3323
|
+
}
|
|
3324
|
+
const eventSystem = new EventSystem({
|
|
3325
|
+
stateManager,
|
|
3326
|
+
pluginExecutors,
|
|
3327
|
+
onNavigate,
|
|
3328
|
+
onApiCall
|
|
3329
|
+
});
|
|
3330
|
+
const overrideSystem = new OverrideSystem({
|
|
3331
|
+
eventSystem,
|
|
3332
|
+
stateManager
|
|
3333
|
+
});
|
|
3334
|
+
const hasAnyOverrides = elements.some((el) => hasOverrides(el));
|
|
3335
|
+
const hasAnyDependencyOverrides = elements.some((el) => hasDependencyOverrides(el));
|
|
1653
3336
|
try {
|
|
1654
3337
|
const tree = buildTree(elements);
|
|
1655
3338
|
const state = {
|
|
@@ -1661,7 +3344,12 @@ function render(options) {
|
|
|
1661
3344
|
rootElement: null,
|
|
1662
3345
|
componentRegistry,
|
|
1663
3346
|
onDependencyNeeded,
|
|
1664
|
-
renderingStack: /* @__PURE__ */ new Set()
|
|
3347
|
+
renderingStack: /* @__PURE__ */ new Set(),
|
|
3348
|
+
views,
|
|
3349
|
+
eventSystem,
|
|
3350
|
+
stateManager,
|
|
3351
|
+
overrideSystem,
|
|
3352
|
+
enableOverrides: hasAnyOverrides
|
|
1665
3353
|
};
|
|
1666
3354
|
container.innerHTML = "";
|
|
1667
3355
|
if (rootElements.length === 0) {
|
|
@@ -1699,6 +3387,16 @@ function render(options) {
|
|
|
1699
3387
|
state.rootElement = wrapper;
|
|
1700
3388
|
container.appendChild(wrapper);
|
|
1701
3389
|
}
|
|
3390
|
+
if (hasAnyOverrides && overrideSystem) {
|
|
3391
|
+
for (const element of elements) {
|
|
3392
|
+
if (hasOverrides(element)) {
|
|
3393
|
+
overrideSystem.initializeElement(element, context);
|
|
3394
|
+
if (hasDependencyOverrides(element)) {
|
|
3395
|
+
overrideSystem.startWatching(element, context);
|
|
3396
|
+
}
|
|
3397
|
+
}
|
|
3398
|
+
}
|
|
3399
|
+
}
|
|
1702
3400
|
const duration = performance.now() - startTime;
|
|
1703
3401
|
const memoryAfter = memorySampler.sample();
|
|
1704
3402
|
const longTasks = longTaskObserver.stop();
|
|
@@ -1717,6 +3415,7 @@ function render(options) {
|
|
|
1717
3415
|
};
|
|
1718
3416
|
} catch (error) {
|
|
1719
3417
|
longTaskObserver.stop();
|
|
3418
|
+
overrideSystem.destroy();
|
|
1720
3419
|
analytics.trackError(componentId, version, error, {
|
|
1721
3420
|
errorType: "render"
|
|
1722
3421
|
});
|
|
@@ -1747,12 +3446,23 @@ function update(state, newContext) {
|
|
|
1747
3446
|
}
|
|
1748
3447
|
}
|
|
1749
3448
|
function destroy(state) {
|
|
3449
|
+
if (state.overrideSystem && state.enableOverrides) {
|
|
3450
|
+
for (const element of state.elements) {
|
|
3451
|
+
if (hasOverrides(element)) {
|
|
3452
|
+
state.overrideSystem.cleanupElement(element, state.context);
|
|
3453
|
+
}
|
|
3454
|
+
}
|
|
3455
|
+
state.overrideSystem.destroy();
|
|
3456
|
+
}
|
|
1750
3457
|
for (const elementState of state.elementStates.values()) {
|
|
1751
3458
|
detachEventHandlers(elementState);
|
|
1752
3459
|
if (elementState.nestedRender) {
|
|
1753
3460
|
elementState.nestedRender.destroy();
|
|
1754
3461
|
}
|
|
1755
3462
|
}
|
|
3463
|
+
if (state.eventSystem) {
|
|
3464
|
+
state.eventSystem.destroy();
|
|
3465
|
+
}
|
|
1756
3466
|
state.elementStates.clear();
|
|
1757
3467
|
if (state.rootElement && state.rootElement.parentNode) {
|
|
1758
3468
|
state.rootElement.parentNode.removeChild(state.rootElement);
|
|
@@ -1819,6 +3529,200 @@ function renderDynamicList(options) {
|
|
|
1819
3529
|
}
|
|
1820
3530
|
return results;
|
|
1821
3531
|
}
|
|
3532
|
+
function renderNode(options) {
|
|
3533
|
+
const {
|
|
3534
|
+
container,
|
|
3535
|
+
elements,
|
|
3536
|
+
nodeId,
|
|
3537
|
+
context,
|
|
3538
|
+
includeChildren = true,
|
|
3539
|
+
eventHandlers,
|
|
3540
|
+
componentRegistry,
|
|
3541
|
+
views
|
|
3542
|
+
} = options;
|
|
3543
|
+
const targetNode = elements.find((el) => el.i === nodeId);
|
|
3544
|
+
if (!targetNode) {
|
|
3545
|
+
console.error(`renderNode: Node not found: ${nodeId}`);
|
|
3546
|
+
return null;
|
|
3547
|
+
}
|
|
3548
|
+
if (!includeChildren) {
|
|
3549
|
+
const singleElementLayout = [targetNode];
|
|
3550
|
+
return render({
|
|
3551
|
+
container,
|
|
3552
|
+
elements: singleElementLayout,
|
|
3553
|
+
context,
|
|
3554
|
+
eventHandlers,
|
|
3555
|
+
componentRegistry,
|
|
3556
|
+
views
|
|
3557
|
+
});
|
|
3558
|
+
}
|
|
3559
|
+
const descendantIds = /* @__PURE__ */ new Set();
|
|
3560
|
+
const collectDescendants = (parentId) => {
|
|
3561
|
+
for (const el of elements) {
|
|
3562
|
+
if (el.parent === parentId) {
|
|
3563
|
+
descendantIds.add(el.i);
|
|
3564
|
+
collectDescendants(el.i);
|
|
3565
|
+
}
|
|
3566
|
+
}
|
|
3567
|
+
};
|
|
3568
|
+
descendantIds.add(nodeId);
|
|
3569
|
+
collectDescendants(nodeId);
|
|
3570
|
+
const nodeElements = elements.filter((el) => descendantIds.has(el.i));
|
|
3571
|
+
const rootedElements = nodeElements.map(
|
|
3572
|
+
(el) => el.i === nodeId ? { ...el, parent: null } : el
|
|
3573
|
+
);
|
|
3574
|
+
return render({
|
|
3575
|
+
container,
|
|
3576
|
+
elements: rootedElements,
|
|
3577
|
+
context,
|
|
3578
|
+
eventHandlers,
|
|
3579
|
+
componentRegistry,
|
|
3580
|
+
views
|
|
3581
|
+
});
|
|
3582
|
+
}
|
|
3583
|
+
function renderInShadow(options) {
|
|
3584
|
+
const { container, mode = "open", styles, injectTailwind: shouldInjectTailwind, ...renderOptions } = options;
|
|
3585
|
+
const shadowRoot = container.attachShadow({ mode });
|
|
3586
|
+
const innerContainer = document.createElement("div");
|
|
3587
|
+
innerContainer.className = "servly-shadow-container";
|
|
3588
|
+
shadowRoot.appendChild(innerContainer);
|
|
3589
|
+
if (styles) {
|
|
3590
|
+
const styleEl = document.createElement("style");
|
|
3591
|
+
styleEl.textContent = styles;
|
|
3592
|
+
shadowRoot.insertBefore(styleEl, innerContainer);
|
|
3593
|
+
}
|
|
3594
|
+
if (shouldInjectTailwind) {
|
|
3595
|
+
const tailwindLink = document.createElement("link");
|
|
3596
|
+
tailwindLink.rel = "stylesheet";
|
|
3597
|
+
tailwindLink.href = "https://cdn.tailwindcss.com";
|
|
3598
|
+
shadowRoot.insertBefore(tailwindLink, innerContainer);
|
|
3599
|
+
}
|
|
3600
|
+
const result = render({
|
|
3601
|
+
...renderOptions,
|
|
3602
|
+
container: innerContainer
|
|
3603
|
+
});
|
|
3604
|
+
return {
|
|
3605
|
+
...result,
|
|
3606
|
+
shadowRoot
|
|
3607
|
+
};
|
|
3608
|
+
}
|
|
3609
|
+
async function createServlyRenderer(options) {
|
|
3610
|
+
const {
|
|
3611
|
+
container: containerOption,
|
|
3612
|
+
injectTailwind: shouldInjectTailwind = true,
|
|
3613
|
+
tailwindConfig,
|
|
3614
|
+
initialState,
|
|
3615
|
+
onStateChange,
|
|
3616
|
+
onNavigate
|
|
3617
|
+
} = options;
|
|
3618
|
+
let container;
|
|
3619
|
+
if (typeof containerOption === "string") {
|
|
3620
|
+
const el = document.querySelector(containerOption);
|
|
3621
|
+
if (!el) {
|
|
3622
|
+
throw new Error(`Container not found: ${containerOption}`);
|
|
3623
|
+
}
|
|
3624
|
+
container = el;
|
|
3625
|
+
} else {
|
|
3626
|
+
container = containerOption;
|
|
3627
|
+
}
|
|
3628
|
+
if (shouldInjectTailwind) {
|
|
3629
|
+
const { initServlyTailwind: initServlyTailwind2 } = await Promise.resolve().then(() => (init_tailwind(), tailwind_exports));
|
|
3630
|
+
await initServlyTailwind2(tailwindConfig);
|
|
3631
|
+
}
|
|
3632
|
+
const activeRenders = [];
|
|
3633
|
+
return {
|
|
3634
|
+
render: (elements, context = { props: {} }) => {
|
|
3635
|
+
const result = render({
|
|
3636
|
+
container,
|
|
3637
|
+
elements,
|
|
3638
|
+
context,
|
|
3639
|
+
enableStateManager: true,
|
|
3640
|
+
initialState,
|
|
3641
|
+
onStateChange,
|
|
3642
|
+
onNavigate
|
|
3643
|
+
});
|
|
3644
|
+
activeRenders.push(result);
|
|
3645
|
+
return result;
|
|
3646
|
+
},
|
|
3647
|
+
renderNode: (elements, nodeId, context = { props: {} }) => {
|
|
3648
|
+
const result = renderNode({
|
|
3649
|
+
container,
|
|
3650
|
+
elements,
|
|
3651
|
+
nodeId,
|
|
3652
|
+
context
|
|
3653
|
+
});
|
|
3654
|
+
if (result) {
|
|
3655
|
+
activeRenders.push(result);
|
|
3656
|
+
}
|
|
3657
|
+
return result;
|
|
3658
|
+
},
|
|
3659
|
+
renderDynamicList,
|
|
3660
|
+
destroy: () => {
|
|
3661
|
+
for (const result of activeRenders) {
|
|
3662
|
+
result.destroy();
|
|
3663
|
+
}
|
|
3664
|
+
activeRenders.length = 0;
|
|
3665
|
+
container.innerHTML = "";
|
|
3666
|
+
}
|
|
3667
|
+
};
|
|
3668
|
+
}
|
|
3669
|
+
function createViewsMap(views) {
|
|
3670
|
+
const viewsMap = /* @__PURE__ */ new Map();
|
|
3671
|
+
for (const view of views) {
|
|
3672
|
+
const id = view.id || view._id;
|
|
3673
|
+
if (id && view.layout) {
|
|
3674
|
+
viewsMap.set(id, {
|
|
3675
|
+
id,
|
|
3676
|
+
layout: view.layout,
|
|
3677
|
+
props: view.props
|
|
3678
|
+
});
|
|
3679
|
+
}
|
|
3680
|
+
}
|
|
3681
|
+
return viewsMap;
|
|
3682
|
+
}
|
|
3683
|
+
function extractReferencedViewIds(elements) {
|
|
3684
|
+
const viewIds = /* @__PURE__ */ new Set();
|
|
3685
|
+
for (const element of elements) {
|
|
3686
|
+
if (element.isComponentView && element.componentId) {
|
|
3687
|
+
viewIds.add(element.componentId);
|
|
3688
|
+
}
|
|
3689
|
+
if (element.configuration?.componentViewRef) {
|
|
3690
|
+
viewIds.add(element.configuration.componentViewRef);
|
|
3691
|
+
}
|
|
3692
|
+
const bindings = element.configuration?.bindings?.inputs;
|
|
3693
|
+
if (bindings) {
|
|
3694
|
+
for (const binding of Object.values(bindings)) {
|
|
3695
|
+
if (binding && typeof binding === "object") {
|
|
3696
|
+
if (binding.source === "node" && binding.binding?.viewId) {
|
|
3697
|
+
viewIds.add(binding.binding.viewId);
|
|
3698
|
+
}
|
|
3699
|
+
if (binding.viewId) {
|
|
3700
|
+
viewIds.add(binding.viewId);
|
|
3701
|
+
}
|
|
3702
|
+
}
|
|
3703
|
+
}
|
|
3704
|
+
}
|
|
3705
|
+
}
|
|
3706
|
+
return Array.from(viewIds);
|
|
3707
|
+
}
|
|
3708
|
+
function collectAllViewDependencies(views, startViewId) {
|
|
3709
|
+
const collected = /* @__PURE__ */ new Set();
|
|
3710
|
+
const toProcess = [startViewId];
|
|
3711
|
+
while (toProcess.length > 0) {
|
|
3712
|
+
const viewId = toProcess.pop();
|
|
3713
|
+
if (collected.has(viewId)) continue;
|
|
3714
|
+
collected.add(viewId);
|
|
3715
|
+
const view = views.get(viewId);
|
|
3716
|
+
if (!view) continue;
|
|
3717
|
+
const referencedIds = extractReferencedViewIds(view.layout);
|
|
3718
|
+
for (const refId of referencedIds) {
|
|
3719
|
+
if (!collected.has(refId)) {
|
|
3720
|
+
toProcess.push(refId);
|
|
3721
|
+
}
|
|
3722
|
+
}
|
|
3723
|
+
}
|
|
3724
|
+
return collected;
|
|
3725
|
+
}
|
|
1822
3726
|
|
|
1823
3727
|
// src/cache.ts
|
|
1824
3728
|
var DEFAULT_CACHE_CONFIG = {
|
|
@@ -2093,6 +3997,18 @@ function calculateBackoffDelay(retryCount, config) {
|
|
|
2093
3997
|
function sleep(ms) {
|
|
2094
3998
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
2095
3999
|
}
|
|
4000
|
+
function buildViewsMap(views) {
|
|
4001
|
+
if (!views || views.length === 0) return void 0;
|
|
4002
|
+
const viewsMap = /* @__PURE__ */ new Map();
|
|
4003
|
+
for (const view of views) {
|
|
4004
|
+
viewsMap.set(view.id, {
|
|
4005
|
+
id: view.id,
|
|
4006
|
+
layout: view.layout,
|
|
4007
|
+
props: view.props
|
|
4008
|
+
});
|
|
4009
|
+
}
|
|
4010
|
+
return viewsMap;
|
|
4011
|
+
}
|
|
2096
4012
|
async function resolveVersionFromApi(id, specifier, apiKey) {
|
|
2097
4013
|
if (/^\d+\.\d+\.\d+$/.test(specifier)) {
|
|
2098
4014
|
return specifier;
|
|
@@ -2122,7 +4038,7 @@ async function resolveVersionFromApi(id, specifier, apiKey) {
|
|
|
2122
4038
|
return "latest";
|
|
2123
4039
|
}
|
|
2124
4040
|
}
|
|
2125
|
-
async function fetchFromRegistry(id, version, apiKey, includeBundle) {
|
|
4041
|
+
async function fetchFromRegistry(id, version, apiKey, includeBundle, includeViews) {
|
|
2126
4042
|
const baseUrl = getRegistryUrl();
|
|
2127
4043
|
const headers = {
|
|
2128
4044
|
"Content-Type": "application/json"
|
|
@@ -2141,6 +4057,9 @@ async function fetchFromRegistry(id, version, apiKey, includeBundle) {
|
|
|
2141
4057
|
if (includeBundle) {
|
|
2142
4058
|
url += (url.includes("?") ? "&" : "?") + "bundle=true";
|
|
2143
4059
|
}
|
|
4060
|
+
if (includeViews) {
|
|
4061
|
+
url += (url.includes("?") ? "&" : "?") + "includeViews=true";
|
|
4062
|
+
}
|
|
2144
4063
|
const response = await fetch(url, { headers });
|
|
2145
4064
|
if (!response.ok) {
|
|
2146
4065
|
if (response.status === 404) {
|
|
@@ -2174,7 +4093,9 @@ async function fetchComponent(id, options = {}) {
|
|
|
2174
4093
|
forceRefresh = false,
|
|
2175
4094
|
signal,
|
|
2176
4095
|
bundleStrategy = "eager",
|
|
2177
|
-
includeBundle = true
|
|
4096
|
+
includeBundle = true,
|
|
4097
|
+
includeViews = true
|
|
4098
|
+
// Default to true - fetch all views needed
|
|
2178
4099
|
} = options;
|
|
2179
4100
|
const fullRetryConfig = {
|
|
2180
4101
|
...DEFAULT_RETRY_CONFIG,
|
|
@@ -2188,6 +4109,7 @@ async function fetchComponent(id, options = {}) {
|
|
|
2188
4109
|
if (cached.bundle) {
|
|
2189
4110
|
registry = buildRegistryFromBundle(cached);
|
|
2190
4111
|
}
|
|
4112
|
+
const views = buildViewsMap(cached.views);
|
|
2191
4113
|
const duration = performance.now() - startTime;
|
|
2192
4114
|
analytics.trackFetch(id, cached.version, duration, true, {
|
|
2193
4115
|
cacheHit: true,
|
|
@@ -2197,7 +4119,8 @@ async function fetchComponent(id, options = {}) {
|
|
|
2197
4119
|
data: cached,
|
|
2198
4120
|
fromCache: true,
|
|
2199
4121
|
version: cached.version,
|
|
2200
|
-
registry
|
|
4122
|
+
registry,
|
|
4123
|
+
views
|
|
2201
4124
|
};
|
|
2202
4125
|
}
|
|
2203
4126
|
}
|
|
@@ -2209,11 +4132,13 @@ async function fetchComponent(id, options = {}) {
|
|
|
2209
4132
|
if (cached.bundle) {
|
|
2210
4133
|
registry = buildRegistryFromBundle(cached);
|
|
2211
4134
|
}
|
|
4135
|
+
const views = buildViewsMap(cached.views);
|
|
2212
4136
|
return {
|
|
2213
4137
|
data: cached,
|
|
2214
4138
|
fromCache: true,
|
|
2215
4139
|
version: resolvedVersion,
|
|
2216
|
-
registry
|
|
4140
|
+
registry,
|
|
4141
|
+
views
|
|
2217
4142
|
};
|
|
2218
4143
|
}
|
|
2219
4144
|
}
|
|
@@ -2224,7 +4149,7 @@ async function fetchComponent(id, options = {}) {
|
|
|
2224
4149
|
throw new Error("Fetch aborted");
|
|
2225
4150
|
}
|
|
2226
4151
|
try {
|
|
2227
|
-
const data = await fetchFromRegistry(id, resolvedVersion, apiKey, shouldIncludeBundle);
|
|
4152
|
+
const data = await fetchFromRegistry(id, resolvedVersion, apiKey, shouldIncludeBundle, includeViews);
|
|
2228
4153
|
setInCache(id, resolvedVersion, data, cacheStrategy, cacheConfig);
|
|
2229
4154
|
if (version !== resolvedVersion) {
|
|
2230
4155
|
setInCache(id, version, data, cacheStrategy, cacheConfig);
|
|
@@ -2239,6 +4164,7 @@ async function fetchComponent(id, options = {}) {
|
|
|
2239
4164
|
version: entry.resolved || entry.version
|
|
2240
4165
|
}));
|
|
2241
4166
|
}
|
|
4167
|
+
const views = buildViewsMap(data.views);
|
|
2242
4168
|
const duration = performance.now() - startTime;
|
|
2243
4169
|
analytics.trackFetch(id, resolvedVersion, duration, false, {
|
|
2244
4170
|
cacheHit: false,
|
|
@@ -2250,7 +4176,8 @@ async function fetchComponent(id, options = {}) {
|
|
|
2250
4176
|
fromCache: false,
|
|
2251
4177
|
version: resolvedVersion,
|
|
2252
4178
|
registry,
|
|
2253
|
-
pendingDependencies
|
|
4179
|
+
pendingDependencies,
|
|
4180
|
+
views
|
|
2254
4181
|
};
|
|
2255
4182
|
} catch (error) {
|
|
2256
4183
|
lastError = error instanceof Error ? error : new Error(String(error));
|
|
@@ -2621,13 +4548,13 @@ function validateAssertion(container, assertion) {
|
|
|
2621
4548
|
message: `Element "${assertion.selector}" not found`
|
|
2622
4549
|
};
|
|
2623
4550
|
}
|
|
2624
|
-
const
|
|
4551
|
+
const hasClass2 = elements[0].classList.contains(assertion.expected);
|
|
2625
4552
|
return {
|
|
2626
4553
|
assertion,
|
|
2627
|
-
passed:
|
|
4554
|
+
passed: hasClass2,
|
|
2628
4555
|
actual: Array.from(elements[0].classList),
|
|
2629
4556
|
expected: assertion.expected,
|
|
2630
|
-
message:
|
|
4557
|
+
message: hasClass2 ? `Element has class "${assertion.expected}"` : `Element does not have class "${assertion.expected}"`
|
|
2631
4558
|
};
|
|
2632
4559
|
case "style":
|
|
2633
4560
|
if (elements.length === 0) {
|
|
@@ -2747,14 +4674,22 @@ function getSampleValue(def) {
|
|
|
2747
4674
|
|
|
2748
4675
|
// src/index.ts
|
|
2749
4676
|
init_registry();
|
|
4677
|
+
init_tailwind();
|
|
2750
4678
|
// Annotate the CommonJS export names for ESM import in node:
|
|
2751
4679
|
0 && (module.exports = {
|
|
2752
4680
|
AnalyticsCollector,
|
|
2753
4681
|
DEFAULT_CACHE_CONFIG,
|
|
2754
4682
|
DEFAULT_RETRY_CONFIG,
|
|
4683
|
+
DEFAULT_SERVLY_TAILWIND_CONFIG,
|
|
4684
|
+
EVENT_HANDLERS,
|
|
4685
|
+
EventSystem,
|
|
2755
4686
|
LongTaskObserver,
|
|
2756
4687
|
MemorySampler,
|
|
4688
|
+
OverrideSystem,
|
|
2757
4689
|
SessionManager,
|
|
4690
|
+
StateManager,
|
|
4691
|
+
addClass,
|
|
4692
|
+
addCustomStyles,
|
|
2758
4693
|
analytics,
|
|
2759
4694
|
applyStyles,
|
|
2760
4695
|
batchFetchComponents,
|
|
@@ -2768,13 +4703,20 @@ init_registry();
|
|
|
2768
4703
|
clearMemoryCache,
|
|
2769
4704
|
clearStyles,
|
|
2770
4705
|
collectAllDependencies,
|
|
4706
|
+
collectAllViewDependencies,
|
|
2771
4707
|
compareVersions,
|
|
2772
4708
|
configureAnalytics,
|
|
2773
4709
|
createRegistry,
|
|
4710
|
+
createServlyRenderer,
|
|
4711
|
+
createViewsMap,
|
|
4712
|
+
deepMerge,
|
|
4713
|
+
deleteValueByPath,
|
|
2774
4714
|
detectCircularDependencies,
|
|
2775
4715
|
extractBindingKeys,
|
|
2776
4716
|
extractDependencies,
|
|
2777
4717
|
extractDependenciesFromCode,
|
|
4718
|
+
extractOverrideDependencies,
|
|
4719
|
+
extractReferencedViewIds,
|
|
2778
4720
|
fetchComponent,
|
|
2779
4721
|
fetchComponentWithDependencies,
|
|
2780
4722
|
formatStyleValue,
|
|
@@ -2782,25 +4724,52 @@ init_registry();
|
|
|
2782
4724
|
generateTestCases,
|
|
2783
4725
|
getAnalytics,
|
|
2784
4726
|
getCacheKey,
|
|
4727
|
+
getCleanupOverrides,
|
|
2785
4728
|
getDependencyTree,
|
|
4729
|
+
getEventSystem,
|
|
2786
4730
|
getFromCache,
|
|
4731
|
+
getLocalStorage,
|
|
2787
4732
|
getLongTaskObserver,
|
|
2788
4733
|
getMemoryCacheSize,
|
|
2789
4734
|
getMemorySampler,
|
|
4735
|
+
getMountOverrides,
|
|
4736
|
+
getOverrideSystem,
|
|
2790
4737
|
getRegistryUrl,
|
|
2791
4738
|
getSessionManager,
|
|
4739
|
+
getSessionStorage,
|
|
4740
|
+
getTailwind,
|
|
4741
|
+
getUrlInfo,
|
|
4742
|
+
getValueByPath,
|
|
4743
|
+
goBack,
|
|
4744
|
+
goForward,
|
|
4745
|
+
hasClass,
|
|
4746
|
+
hasDependencyOverrides,
|
|
4747
|
+
hasOverrides,
|
|
2792
4748
|
hasTemplateSyntax,
|
|
4749
|
+
initServlyTailwind,
|
|
4750
|
+
injectTailwind,
|
|
2793
4751
|
invalidateCache,
|
|
2794
4752
|
isComponentAvailable,
|
|
4753
|
+
isTailwindLoaded,
|
|
2795
4754
|
isValidSpecifier,
|
|
4755
|
+
navigateTo,
|
|
2796
4756
|
parseVersion,
|
|
2797
4757
|
prefetchComponents,
|
|
2798
4758
|
processStyles,
|
|
4759
|
+
removeClass,
|
|
4760
|
+
removeCustomStyles,
|
|
4761
|
+
removeLocalStorage,
|
|
4762
|
+
removeSessionStorage,
|
|
4763
|
+
removeTailwind,
|
|
2799
4764
|
render,
|
|
2800
4765
|
renderDynamicList,
|
|
4766
|
+
renderInShadow,
|
|
4767
|
+
renderNode,
|
|
2801
4768
|
resetAnalytics,
|
|
4769
|
+
resetEventSystem,
|
|
2802
4770
|
resetLongTaskObserver,
|
|
2803
4771
|
resetMemorySampler,
|
|
4772
|
+
resetOverrideSystem,
|
|
2804
4773
|
resetSessionManager,
|
|
2805
4774
|
resolveBindingPath,
|
|
2806
4775
|
resolveTemplate,
|
|
@@ -2811,8 +4780,15 @@ init_registry();
|
|
|
2811
4780
|
runTestCase,
|
|
2812
4781
|
satisfiesVersion,
|
|
2813
4782
|
setInCache,
|
|
4783
|
+
setLocalStorage,
|
|
2814
4784
|
setRegistryUrl,
|
|
4785
|
+
setSessionStorage,
|
|
4786
|
+
setValueByPath,
|
|
4787
|
+
toDomEventName,
|
|
4788
|
+
toReactEventName,
|
|
4789
|
+
toggleClass,
|
|
2815
4790
|
updateStyles,
|
|
4791
|
+
updateTailwindConfig,
|
|
2816
4792
|
validateAssertion,
|
|
2817
4793
|
validateProps
|
|
2818
4794
|
});
|