jsswift 2.0.1 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AI.md +5 -0
- package/README.md +2 -0
- package/dist/build/build-configured-runtime.d.mts +29 -0
- package/dist/build/build-configured-runtime.mjs +92 -0
- package/dist/build/runtime-layout.mjs +13 -0
- package/dist/build/select-ui.mjs +167 -0
- package/dist/build/vite-jss.d.mts +2 -0
- package/dist/build/vite-jss.mjs +73 -0
- package/dist/cms.js +3522 -0
- package/dist/documentions/AI.md +5 -0
- package/dist/documentions/README.md +1 -0
- package/dist/documentions/configured-runtime.md +82 -0
- package/dist/documentions/knowledge-map.json +20 -1
- package/dist/ui-src/00-bootstrap.js +330 -0
- package/dist/ui-src/01-foundation-helpers.js +809 -0
- package/dist/ui-src/10-primitives-layout.js +5091 -0
- package/dist/ui-src/20-display-content.js +780 -0
- package/dist/ui-src/21-list-content.js +246 -0
- package/dist/ui-src/22-banner-content.js +376 -0
- package/dist/ui-src/23-tooltip.js +276 -0
- package/dist/ui-src/31-form-advanced.js +2784 -0
- package/dist/ui-src/40-navigation.js +1262 -0
- package/dist/ui-src/41-tab-panel.js +896 -0
- package/dist/ui-src/50-feedback.js +999 -0
- package/dist/ui-src/51-feedback-service.js +166 -0
- package/dist/ui-src/60-shell-app.js +1223 -0
- package/dist/ui-src/61-form-service.js +368 -0
- package/dist/ui-src/70-dialog.js +333 -0
- package/dist/ui-src/71-menu-overlays.js +1217 -0
- package/dist/ui-src/80-data.js +437 -0
- package/dist/ui-src/modules.json +212 -0
- package/dist/ui.js +12565 -0
- package/package.json +26 -2
package/dist/cms.js
ADDED
|
@@ -0,0 +1,3522 @@
|
|
|
1
|
+
(() => {
|
|
2
|
+
var _a, _b, _c, _d, _e, _f, _g, _h2, _i;
|
|
3
|
+
window.JSswift = window.JSswift || {};
|
|
4
|
+
const JSswift = window.JSswift;
|
|
5
|
+
window._ = window.JSswift;
|
|
6
|
+
JSswift.config = JSswift.config || {};
|
|
7
|
+
const JSswiftBootstrapSetting = typeof globalThis !== "undefined" && globalThis.JSswift_setting ? globalThis.JSswift_setting : {};
|
|
8
|
+
JSswift.config.debug = (_a = JSswiftBootstrapSetting.modeDev) != null ? _a : false;
|
|
9
|
+
JSswift.isDev = function() {
|
|
10
|
+
return JSswift.config.debug;
|
|
11
|
+
};
|
|
12
|
+
JSswift.uiNormalizeArgs = function(args) {
|
|
13
|
+
let props = {};
|
|
14
|
+
let children = args;
|
|
15
|
+
if (args.length > 0 && typeof args[0] === "object" && args[0] !== null && !args[0].nodeType && !Array.isArray(args[0]) && !(args[0] instanceof Function)) {
|
|
16
|
+
props = args[0];
|
|
17
|
+
children = args.slice(1);
|
|
18
|
+
}
|
|
19
|
+
return { props, children };
|
|
20
|
+
};
|
|
21
|
+
JSswift.uiSizes = ["xxs", "xs", "sm", "md", "lg", "xl", "xxl", "xxxl"];
|
|
22
|
+
JSswift.uiColors = ["primary", "secondary", "success", "warning", "danger", "info", "light", "dark"];
|
|
23
|
+
JSswift.meta = JSswift.meta || { version: "1.0.23", policy: { sourceOfTruth: "docDevelop/reference/core.md", syncRule: "Quando cambia un modulo core, aggiornare sia questo meta sia docDevelop/reference/core.md." }, modules: { render: { description: "Bridge props -> DOM per hyperscript _, attributi, class, style, eventi e children reattivi.", entrypoints: ["createElement", "setProp", "bindProp"], status: "milestone-3-closed", knownLimits: ["Il mini-terzo-giro e il successivo micro-giro avanzato hanno chiuso style dinamico, eventi, children e props/attr speciali, ma restano altri edge case avanzati da esplorare.", "Gli eventi ora supportano composizione base di listener multipli, ma non hanno ancora delegation o diff avanzato.", "Restano da esplorare edge case avanzati del renderer oltre ai giri gia coperti dai test."] }, reactive: { description: "Core minimale signal/effect usato come base della reattivita.", entrypoints: ["JSswift.reactive.signal", "JSswift.reactive.effect", "JSswift.reactive.computed", "JSswift.reactive.untracked", "JSswift.reactive.batch"], status: "milestone-2-closed", knownLimits: ["La protezione loop copre i loop sincroni per singolo effect, non ancora i cicli complessi tra effect multipli.", "Il batching sincrono ora esiste, ma manca ancora scheduling configurabile oltre il flush immediato a fine batch.", "Mancano primitive avanzate per controllare scheduling e transazioni asincrone."] }, rod: { description: "Layer reattivo di alto livello per binding DOM, model e interpolazioni.", entrypoints: ["_.rod", "JSswift.rodBind", "JSswift.rodModel"], status: "milestone-2-closed", knownLimits: ["Va chiarito meglio il rapporto con JSswift.reactive oltre al primo riallineamento strutturale.", "Restano casi avanzati di model/binding da esplorare oltre al primo giro coperto dai test.", "Il modulo e piu pulito internamente ma non e ancora il punto finale della convergenza con il renderer."] }, mount: { description: "Mount, component instances e cleanup automatico del tree DOM.", entrypoints: ["JSswift.mount", "JSswift.component", "JSswift.enableAutoCleanup"], status: "milestone-2-closed", knownLimits: ["Il lifecycle e piu pulito internamente ma resta da chiarire meglio la semantica su multi-mount e cloni.", "Restano da esplorare edge case avanzati del cleanup automatico fuori dal primo giro gia coperto."] }, platform: { description: "Moduli applicativi nel core: overlay, store, auth, http, router, UI meta.", entrypoints: ["JSswift.overlay", "JSswift.store", "JSswift.plugins.auth", "JSswift.http", "JSswift.router", "JSswift.ui.meta"], status: "milestone-2-closed", knownLimits: ["Il secondo giro ha ripulito i moduli interni, ma mancano ancora demo separate per modulo e una validazione piu formale di alcune superfici pubbliche.", "Esiste una demo browser aggregata del blocco platform, ma non ancora demo separate per ogni modulo.", "Mancano configurazione pubblica piu coerente e confini piu netti tra auth, http e router.", "Il registry UI meta non ha ancora validazione formale del suo shape."] } } };
|
|
24
|
+
JSswift.omit = (obj, keys) => {
|
|
25
|
+
const out = {};
|
|
26
|
+
const skip = new Set(keys || []);
|
|
27
|
+
for (const k in obj || {}) {
|
|
28
|
+
if (!skip.has(k)) out[k] = obj[k];
|
|
29
|
+
}
|
|
30
|
+
return out;
|
|
31
|
+
};
|
|
32
|
+
JSswift._cleanupRegistry = /* @__PURE__ */ new WeakMap();
|
|
33
|
+
JSswift._registerCleanup = function(node, disposer) {
|
|
34
|
+
if (!node || typeof disposer !== "function") return disposer;
|
|
35
|
+
const list = JSswift._cleanupRegistry.get(node) || [];
|
|
36
|
+
list.push(disposer);
|
|
37
|
+
JSswift._cleanupRegistry.set(node, list);
|
|
38
|
+
return disposer;
|
|
39
|
+
};
|
|
40
|
+
JSswift.dom = { q(sel, root = document) {
|
|
41
|
+
return root.querySelector(sel);
|
|
42
|
+
}, qa(sel, root = document) {
|
|
43
|
+
return Array.from(root.querySelectorAll(sel));
|
|
44
|
+
}, attr(el, name, value) {
|
|
45
|
+
if (!el) return null;
|
|
46
|
+
if (value === void 0) return el.getAttribute(name);
|
|
47
|
+
if (value === null) el.removeAttribute(name);
|
|
48
|
+
else el.setAttribute(name, String(value));
|
|
49
|
+
return el;
|
|
50
|
+
} };
|
|
51
|
+
(() => {
|
|
52
|
+
let _queue = [];
|
|
53
|
+
let _ready = false;
|
|
54
|
+
function flushReady() {
|
|
55
|
+
if (_ready) return;
|
|
56
|
+
_ready = true;
|
|
57
|
+
const q = _queue.slice();
|
|
58
|
+
_queue.length = 0;
|
|
59
|
+
for (const fn of q) {
|
|
60
|
+
try {
|
|
61
|
+
fn();
|
|
62
|
+
} catch (e) {
|
|
63
|
+
console.error("[JSswift.ready] error:", e);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
JSswift.ready = function(fn) {
|
|
68
|
+
if (typeof fn !== "function") return;
|
|
69
|
+
if (_ready || document.readyState === "interactive" || document.readyState === "complete") {
|
|
70
|
+
queueMicrotask(() => {
|
|
71
|
+
try {
|
|
72
|
+
fn();
|
|
73
|
+
} catch (e) {
|
|
74
|
+
console.error("[JSswift.ready] error:", e);
|
|
75
|
+
}
|
|
76
|
+
});
|
|
77
|
+
} else {
|
|
78
|
+
_queue.push(fn);
|
|
79
|
+
}
|
|
80
|
+
};
|
|
81
|
+
document.addEventListener("DOMContentLoaded", flushReady, { once: true });
|
|
82
|
+
})();
|
|
83
|
+
JSswift.reactive = /* @__PURE__ */ (() => {
|
|
84
|
+
let CURRENT_EFFECT = null;
|
|
85
|
+
const EFFECT_STACK = [];
|
|
86
|
+
const MAX_SYNC_EFFECT_RERUNS = 100;
|
|
87
|
+
let BATCH_DEPTH = 0;
|
|
88
|
+
let BATCH_FLUSH_MODE = "sync";
|
|
89
|
+
let PENDING_FLUSH_MODE = "sync";
|
|
90
|
+
let MICROTASK_FLUSH_SCHEDULED = false;
|
|
91
|
+
const PENDING_RUNNERS = /* @__PURE__ */ new Set();
|
|
92
|
+
function normalizeFlushMode(options) {
|
|
93
|
+
const mode = options == null ? void 0 : options.flush;
|
|
94
|
+
if (mode == null || mode === "sync") return "sync";
|
|
95
|
+
if (mode === "microtask") return "microtask";
|
|
96
|
+
throw new Error("JSswift.reactive.batch: options.flush must be 'sync' or 'microtask'");
|
|
97
|
+
}
|
|
98
|
+
function flushPendingRunners() {
|
|
99
|
+
while (PENDING_RUNNERS.size) {
|
|
100
|
+
const jobs = Array.from(PENDING_RUNNERS);
|
|
101
|
+
PENDING_RUNNERS.clear();
|
|
102
|
+
jobs.forEach((fn) => fn());
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
function scheduleMicrotaskFlush() {
|
|
106
|
+
if (MICROTASK_FLUSH_SCHEDULED) return;
|
|
107
|
+
MICROTASK_FLUSH_SCHEDULED = true;
|
|
108
|
+
queueMicrotask(() => {
|
|
109
|
+
MICROTASK_FLUSH_SCHEDULED = false;
|
|
110
|
+
PENDING_FLUSH_MODE = "sync";
|
|
111
|
+
flushPendingRunners();
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
function notifySubscribers(subs) {
|
|
115
|
+
const jobs = Array.from(subs);
|
|
116
|
+
if (BATCH_DEPTH > 0 || PENDING_FLUSH_MODE === "microtask") {
|
|
117
|
+
jobs.forEach((fn) => PENDING_RUNNERS.add(fn));
|
|
118
|
+
if (BATCH_DEPTH === 0 && PENDING_FLUSH_MODE === "microtask") {
|
|
119
|
+
scheduleMicrotaskFlush();
|
|
120
|
+
}
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
jobs.forEach((fn) => fn());
|
|
124
|
+
}
|
|
125
|
+
function cleanupEffect(record) {
|
|
126
|
+
if (!record) return;
|
|
127
|
+
if (record._deps.size) {
|
|
128
|
+
record._deps.forEach((dep) => dep.delete(record._runner));
|
|
129
|
+
record._deps.clear();
|
|
130
|
+
}
|
|
131
|
+
if (typeof record._cleanup === "function") {
|
|
132
|
+
const cleanup = record._cleanup;
|
|
133
|
+
record._cleanup = null;
|
|
134
|
+
try {
|
|
135
|
+
cleanup();
|
|
136
|
+
} catch (e) {
|
|
137
|
+
console.error("[JSswift.reactive.effect] cleanup error:", e);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
function runEffect(record) {
|
|
142
|
+
if (!record || !record.active) return;
|
|
143
|
+
cleanupEffect(record);
|
|
144
|
+
EFFECT_STACK.push(record);
|
|
145
|
+
CURRENT_EFFECT = record;
|
|
146
|
+
try {
|
|
147
|
+
const onCleanup = (fn) => {
|
|
148
|
+
record._cleanup = typeof fn === "function" ? fn : null;
|
|
149
|
+
};
|
|
150
|
+
const out = record.fn.length > 0 ? record.fn(onCleanup) : record.fn();
|
|
151
|
+
if (typeof out === "function") {
|
|
152
|
+
record._cleanup = out;
|
|
153
|
+
}
|
|
154
|
+
} finally {
|
|
155
|
+
EFFECT_STACK.pop();
|
|
156
|
+
CURRENT_EFFECT = EFFECT_STACK[EFFECT_STACK.length - 1] || null;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
function effect(fn) {
|
|
160
|
+
if (typeof fn !== "function") return () => {
|
|
161
|
+
};
|
|
162
|
+
const record = { fn, active: true, _deps: /* @__PURE__ */ new Set(), _cleanup: null, _runner: null, _running: false, _queued: false };
|
|
163
|
+
const runner = () => {
|
|
164
|
+
if (!record.active) return;
|
|
165
|
+
if (record._running) {
|
|
166
|
+
record._queued = true;
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
169
|
+
record._running = true;
|
|
170
|
+
let reruns = 0;
|
|
171
|
+
try {
|
|
172
|
+
do {
|
|
173
|
+
record._queued = false;
|
|
174
|
+
runEffect(record);
|
|
175
|
+
reruns++;
|
|
176
|
+
if (reruns >= MAX_SYNC_EFFECT_RERUNS) {
|
|
177
|
+
record._queued = false;
|
|
178
|
+
console.warn("[JSswift.reactive.effect] loop guard triggered: too many synchronous reruns", { max: MAX_SYNC_EFFECT_RERUNS, effect: fn.name || "anonymous" });
|
|
179
|
+
break;
|
|
180
|
+
}
|
|
181
|
+
} while (record.active && record._queued);
|
|
182
|
+
} finally {
|
|
183
|
+
record._running = false;
|
|
184
|
+
}
|
|
185
|
+
};
|
|
186
|
+
record._runner = runner;
|
|
187
|
+
runner();
|
|
188
|
+
return () => {
|
|
189
|
+
if (!record.active) return;
|
|
190
|
+
record.active = false;
|
|
191
|
+
cleanupEffect(record);
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
function signal(initial) {
|
|
195
|
+
let value = initial;
|
|
196
|
+
const subs = /* @__PURE__ */ new Set();
|
|
197
|
+
function get() {
|
|
198
|
+
if (CURRENT_EFFECT && CURRENT_EFFECT.active) {
|
|
199
|
+
subs.add(CURRENT_EFFECT._runner);
|
|
200
|
+
CURRENT_EFFECT._deps.add(subs);
|
|
201
|
+
}
|
|
202
|
+
return value;
|
|
203
|
+
}
|
|
204
|
+
function set(v) {
|
|
205
|
+
value = v;
|
|
206
|
+
notifySubscribers(subs);
|
|
207
|
+
}
|
|
208
|
+
function dispose() {
|
|
209
|
+
subs.clear();
|
|
210
|
+
}
|
|
211
|
+
return [get, set, dispose];
|
|
212
|
+
}
|
|
213
|
+
function computed(fn) {
|
|
214
|
+
if (typeof fn !== "function") throw new Error("JSswift.reactive.computed: fn must be a function");
|
|
215
|
+
const [get, set, disposeSignal] = signal(void 0);
|
|
216
|
+
const disposeEffect = effect(() => {
|
|
217
|
+
set(fn());
|
|
218
|
+
});
|
|
219
|
+
const getter = () => get();
|
|
220
|
+
getter.dispose = () => {
|
|
221
|
+
disposeEffect();
|
|
222
|
+
disposeSignal();
|
|
223
|
+
};
|
|
224
|
+
getter.peek = () => get();
|
|
225
|
+
getter.type = "computed";
|
|
226
|
+
return getter;
|
|
227
|
+
}
|
|
228
|
+
function untracked(fn) {
|
|
229
|
+
if (typeof fn !== "function") throw new Error("JSswift.reactive.untracked: fn must be a function");
|
|
230
|
+
const prevEffect = CURRENT_EFFECT;
|
|
231
|
+
CURRENT_EFFECT = null;
|
|
232
|
+
try {
|
|
233
|
+
return fn();
|
|
234
|
+
} finally {
|
|
235
|
+
CURRENT_EFFECT = prevEffect;
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
function batch(fn, options) {
|
|
239
|
+
if (typeof fn !== "function") throw new Error("JSswift.reactive.batch: fn must be a function");
|
|
240
|
+
const flushMode = normalizeFlushMode(options);
|
|
241
|
+
if (BATCH_DEPTH === 0) {
|
|
242
|
+
BATCH_FLUSH_MODE = flushMode;
|
|
243
|
+
} else if (flushMode === "microtask") {
|
|
244
|
+
BATCH_FLUSH_MODE = "microtask";
|
|
245
|
+
}
|
|
246
|
+
BATCH_DEPTH += 1;
|
|
247
|
+
try {
|
|
248
|
+
return fn();
|
|
249
|
+
} finally {
|
|
250
|
+
BATCH_DEPTH -= 1;
|
|
251
|
+
if (BATCH_DEPTH === 0) {
|
|
252
|
+
const shouldUseMicrotask = BATCH_FLUSH_MODE === "microtask" || PENDING_FLUSH_MODE === "microtask";
|
|
253
|
+
BATCH_FLUSH_MODE = "sync";
|
|
254
|
+
if (shouldUseMicrotask) {
|
|
255
|
+
PENDING_FLUSH_MODE = "microtask";
|
|
256
|
+
scheduleMicrotaskFlush();
|
|
257
|
+
} else {
|
|
258
|
+
flushPendingRunners();
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
return { signal, effect, computed, untracked, batch };
|
|
264
|
+
})();
|
|
265
|
+
JSswift._overlayShared = (() => {
|
|
266
|
+
const focusSelector = ["button:not([disabled])", "[href]", "input:not([disabled])", "select:not([disabled])", "textarea:not([disabled])", "[tabindex]:not([tabindex='-1'])"].join(",");
|
|
267
|
+
function ensureRoot(getRoot, setRoot) {
|
|
268
|
+
const currentRoot = getRoot();
|
|
269
|
+
if (currentRoot && currentRoot.isConnected) return currentRoot;
|
|
270
|
+
let el = document.getElementById("jss-overlay-root");
|
|
271
|
+
if (!el && (document == null ? void 0 : document.body)) {
|
|
272
|
+
el = document.createElement("div");
|
|
273
|
+
el.id = "jss-overlay-root";
|
|
274
|
+
el.className = "jss-overlay-root";
|
|
275
|
+
document.body.appendChild(el);
|
|
276
|
+
}
|
|
277
|
+
if (!document.body && !el) {
|
|
278
|
+
JSswift.ready(() => {
|
|
279
|
+
let readyEl = document.getElementById("jss-overlay-root");
|
|
280
|
+
if (!readyEl) {
|
|
281
|
+
readyEl = document.createElement("div");
|
|
282
|
+
readyEl.id = "jss-overlay-root";
|
|
283
|
+
readyEl.className = "jss-overlay-root";
|
|
284
|
+
document.body.appendChild(readyEl);
|
|
285
|
+
}
|
|
286
|
+
setRoot(readyEl);
|
|
287
|
+
});
|
|
288
|
+
}
|
|
289
|
+
setRoot(el);
|
|
290
|
+
return el;
|
|
291
|
+
}
|
|
292
|
+
function focusFirst(container) {
|
|
293
|
+
var _a2;
|
|
294
|
+
const node = container.querySelector(focusSelector);
|
|
295
|
+
(_a2 = node == null ? void 0 : node.focus) == null ? void 0 : _a2.call(node);
|
|
296
|
+
}
|
|
297
|
+
function trapFocus(event, container) {
|
|
298
|
+
if (event.key !== "Tab") return;
|
|
299
|
+
const nodes = Array.from(container.querySelectorAll(focusSelector)).filter((node) => node.offsetParent !== null);
|
|
300
|
+
if (!nodes.length) return;
|
|
301
|
+
const first = nodes[0];
|
|
302
|
+
const last = nodes[nodes.length - 1];
|
|
303
|
+
const active = document.activeElement;
|
|
304
|
+
if (event.shiftKey && active === first) {
|
|
305
|
+
event.preventDefault();
|
|
306
|
+
last.focus();
|
|
307
|
+
} else if (!event.shiftKey && active === last) {
|
|
308
|
+
event.preventDefault();
|
|
309
|
+
first.focus();
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
function applyAnchoredPosition(panel, opts) {
|
|
313
|
+
var _a2, _b2, _c2, _d2, _e2, _f2;
|
|
314
|
+
if (!opts.anchorEl) return;
|
|
315
|
+
const anchorRect = opts.anchorEl.getBoundingClientRect();
|
|
316
|
+
const panelRect = panel.getBoundingClientRect();
|
|
317
|
+
const visualViewport = window.visualViewport || null;
|
|
318
|
+
const viewportOffsetLeft = Math.max(0, Number(visualViewport == null ? void 0 : visualViewport.offsetLeft) || 0);
|
|
319
|
+
const viewportOffsetTop = Math.max(0, Number(visualViewport == null ? void 0 : visualViewport.offsetTop) || 0);
|
|
320
|
+
const viewportWidth = Math.max(Number(visualViewport == null ? void 0 : visualViewport.width) || 0, window.innerWidth || 0, ((_a2 = document.documentElement) == null ? void 0 : _a2.clientWidth) || 0);
|
|
321
|
+
const viewportHeight = Math.max(Number(visualViewport == null ? void 0 : visualViewport.height) || 0, window.innerHeight || 0, ((_b2 = document.documentElement) == null ? void 0 : _b2.clientHeight) || 0);
|
|
322
|
+
const rawGutter = Number((_d2 = (_c2 = opts.viewportGutter) != null ? _c2 : opts.gutter) != null ? _d2 : 8);
|
|
323
|
+
const gutter = Number.isFinite(rawGutter) ? Math.max(0, rawGutter) : 8;
|
|
324
|
+
const rawOffsetX = Number((_e2 = opts.offsetX) != null ? _e2 : 0);
|
|
325
|
+
const rawOffsetY = Number((_f2 = opts.offsetY) != null ? _f2 : 8);
|
|
326
|
+
const offsetX = Number.isFinite(rawOffsetX) ? rawOffsetX : 0;
|
|
327
|
+
const offsetY = Number.isFinite(rawOffsetY) ? rawOffsetY : 8;
|
|
328
|
+
const clamp = (value, min, max) => {
|
|
329
|
+
if (!Number.isFinite(value)) return min;
|
|
330
|
+
if (!Number.isFinite(min)) min = 0;
|
|
331
|
+
if (!Number.isFinite(max)) max = min;
|
|
332
|
+
if (max < min) return min;
|
|
333
|
+
return Math.min(Math.max(value, min), max);
|
|
334
|
+
};
|
|
335
|
+
const rawPlacement = String(opts.placement || "bottom-start").toLowerCase();
|
|
336
|
+
const [rawSide, rawAlign] = rawPlacement.split("-");
|
|
337
|
+
const side = ["top", "bottom", "left", "right"].includes(rawSide) ? rawSide : "bottom";
|
|
338
|
+
const align = ["start", "end", "center"].includes(rawAlign) ? rawAlign : "start";
|
|
339
|
+
const oppositeSide = { top: "bottom", bottom: "top", left: "right", right: "left" }[side] || "top";
|
|
340
|
+
const isVertical = side === "top" || side === "bottom";
|
|
341
|
+
const panelWidth = panelRect.width;
|
|
342
|
+
const panelHeight = panelRect.height;
|
|
343
|
+
const computePosition = (targetSide) => {
|
|
344
|
+
let left2 = anchorRect.left + offsetX;
|
|
345
|
+
let top2 = anchorRect.top + offsetY;
|
|
346
|
+
if (targetSide === "top" || targetSide === "bottom") {
|
|
347
|
+
if (align === "center") left2 = anchorRect.left + (anchorRect.width - panelWidth) / 2 + offsetX;
|
|
348
|
+
else if (align === "end") left2 = anchorRect.right - panelWidth + offsetX;
|
|
349
|
+
top2 = targetSide === "top" ? anchorRect.top - panelHeight - offsetY : anchorRect.bottom + offsetY;
|
|
350
|
+
} else {
|
|
351
|
+
if (align === "center") top2 = anchorRect.top + (anchorRect.height - panelHeight) / 2 + offsetY;
|
|
352
|
+
else if (align === "end") top2 = anchorRect.bottom - panelHeight + offsetY;
|
|
353
|
+
left2 = targetSide === "left" ? anchorRect.left - panelWidth - offsetX : anchorRect.right + offsetX;
|
|
354
|
+
}
|
|
355
|
+
return { left: left2, top: top2 };
|
|
356
|
+
};
|
|
357
|
+
const availableSpace = (targetSide) => {
|
|
358
|
+
if (targetSide === "top") return anchorRect.top - offsetY - gutter;
|
|
359
|
+
if (targetSide === "bottom") return viewportHeight - anchorRect.bottom - offsetY - gutter;
|
|
360
|
+
if (targetSide === "left") return anchorRect.left - offsetX - gutter;
|
|
361
|
+
return viewportWidth - anchorRect.right - offsetX - gutter;
|
|
362
|
+
};
|
|
363
|
+
const preferredSpace = availableSpace(side);
|
|
364
|
+
const oppositeSpace = availableSpace(oppositeSide);
|
|
365
|
+
const requiredSpace = isVertical ? panelHeight : panelWidth;
|
|
366
|
+
const resolvedSide = requiredSpace > preferredSpace && oppositeSpace > preferredSpace ? oppositeSide : side;
|
|
367
|
+
const resolvedPosition = computePosition(resolvedSide);
|
|
368
|
+
const minLeft = viewportOffsetLeft + gutter;
|
|
369
|
+
const minTop = viewportOffsetTop + gutter;
|
|
370
|
+
const maxLeft = Math.max(minLeft, viewportOffsetLeft + viewportWidth - panelWidth - gutter);
|
|
371
|
+
const maxTop = Math.max(minTop, viewportOffsetTop + viewportHeight - panelHeight - gutter);
|
|
372
|
+
const left = clamp(resolvedPosition.left, minLeft, maxLeft);
|
|
373
|
+
const top = clamp(resolvedPosition.top, minTop, maxTop);
|
|
374
|
+
panel.style.position = "fixed";
|
|
375
|
+
panel.style.left = `${left}px`;
|
|
376
|
+
panel.style.top = `${top}px`;
|
|
377
|
+
}
|
|
378
|
+
return { ensureRoot, focusFirst, trapFocus, applyAnchoredPosition };
|
|
379
|
+
})();
|
|
380
|
+
JSswift.overlay = (() => {
|
|
381
|
+
let seq = 0;
|
|
382
|
+
const stack = /* @__PURE__ */ new Map();
|
|
383
|
+
let root = null;
|
|
384
|
+
const { ensureRoot: ensureOverlayRoot, focusFirst, trapFocus, applyAnchoredPosition } = JSswift._overlayShared;
|
|
385
|
+
const ensureRoot = () => ensureOverlayRoot(() => root, (nextRoot) => {
|
|
386
|
+
root = nextRoot;
|
|
387
|
+
});
|
|
388
|
+
let scrollLockCount = 0;
|
|
389
|
+
const lockScroll = () => {
|
|
390
|
+
scrollLockCount++;
|
|
391
|
+
if (scrollLockCount === 1) {
|
|
392
|
+
document.documentElement.classList.add("jss-scroll-locked");
|
|
393
|
+
}
|
|
394
|
+
};
|
|
395
|
+
const unlockScroll = () => {
|
|
396
|
+
scrollLockCount = Math.max(0, scrollLockCount - 1);
|
|
397
|
+
if (scrollLockCount === 0) {
|
|
398
|
+
document.documentElement.classList.remove("jss-scroll-locked");
|
|
399
|
+
}
|
|
400
|
+
};
|
|
401
|
+
const getTop = () => {
|
|
402
|
+
let top = null;
|
|
403
|
+
for (const e of stack.values()) top = e;
|
|
404
|
+
return top;
|
|
405
|
+
};
|
|
406
|
+
const isOverlayInteractivePortal = (target) => {
|
|
407
|
+
var _a2;
|
|
408
|
+
let node = target;
|
|
409
|
+
while (node && node !== document) {
|
|
410
|
+
if (node.nodeType === 1 && ((_a2 = node.getAttribute) == null ? void 0 : _a2.call(node, "data-jss-overlay-portal")) === "true") return true;
|
|
411
|
+
node = node.parentNode;
|
|
412
|
+
}
|
|
413
|
+
return false;
|
|
414
|
+
};
|
|
415
|
+
const open = (content, opts = {}) => {
|
|
416
|
+
var _a2, _b2, _c2, _d2;
|
|
417
|
+
const id = `ov_${++seq}`;
|
|
418
|
+
const entry = { id, opts, isOpen: true, anchorEl: opts.anchorEl || null, placement: opts.placement || "bottom-start", onClose: typeof opts.onClose === "function" ? opts.onClose : null, overlay: null, panel: null, backdrop: null, reposition: null, _positionCleanup: null, _positionFrame: null, _panelResizeObserver: null, _cleanup: null };
|
|
419
|
+
const overlay = document.createElement("div");
|
|
420
|
+
overlay.className = ["jss-overlay", opts.type ? `type-${opts.type}` : ""].filter(Boolean).join(" ");
|
|
421
|
+
overlay.dataset.id = id;
|
|
422
|
+
const backdrop = document.createElement("div");
|
|
423
|
+
backdrop.className = "jss-overlay-backdrop";
|
|
424
|
+
if (!opts.backdrop) backdrop.style.display = "none";
|
|
425
|
+
const panel = document.createElement("div");
|
|
426
|
+
panel.className = ["jss-overlay-panel", opts.className].filter(Boolean).join(" ");
|
|
427
|
+
panel.tabIndex = -1;
|
|
428
|
+
const node = typeof content === "function" ? content({ close: () => close(id) }) : content;
|
|
429
|
+
const normalized = JSswift.ui.slot(node);
|
|
430
|
+
if (Array.isArray(normalized)) normalized.forEach((n) => n && panel.appendChild(n));
|
|
431
|
+
else if (normalized) panel.appendChild(normalized);
|
|
432
|
+
overlay.appendChild(backdrop);
|
|
433
|
+
overlay.appendChild(panel);
|
|
434
|
+
const host = ensureRoot();
|
|
435
|
+
if (!host) return { id, close: () => close(id), panel, overlay };
|
|
436
|
+
host.appendChild(overlay);
|
|
437
|
+
entry.overlay = overlay;
|
|
438
|
+
entry.panel = panel;
|
|
439
|
+
entry.backdrop = backdrop;
|
|
440
|
+
stack.set(id, entry);
|
|
441
|
+
if (opts.lockScroll) lockScroll();
|
|
442
|
+
if (opts.autoFocus !== false) setTimeout(() => {
|
|
443
|
+
if (panel.isConnected && stack.get(id) === entry && !panel.contains(document.activeElement)) focusFirst(panel);
|
|
444
|
+
}, 0);
|
|
445
|
+
const position = (nextOpts = null) => {
|
|
446
|
+
if (nextOpts && typeof nextOpts === "object") Object.assign(entry.opts, nextOpts);
|
|
447
|
+
applyAnchoredPosition(panel, entry.opts);
|
|
448
|
+
};
|
|
449
|
+
const schedulePosition = () => {
|
|
450
|
+
if (!opts.anchorEl) return;
|
|
451
|
+
const raf = globalThis.requestAnimationFrame || ((fn) => setTimeout(() => fn(Date.now()), 0));
|
|
452
|
+
if (entry._positionFrame != null) return;
|
|
453
|
+
entry._positionFrame = raf(() => {
|
|
454
|
+
entry._positionFrame = null;
|
|
455
|
+
position();
|
|
456
|
+
});
|
|
457
|
+
};
|
|
458
|
+
entry.reposition = position;
|
|
459
|
+
if (opts.anchorEl) {
|
|
460
|
+
position();
|
|
461
|
+
schedulePosition();
|
|
462
|
+
const onResize = schedulePosition;
|
|
463
|
+
window.addEventListener("resize", onResize);
|
|
464
|
+
window.addEventListener("scroll", onResize, true);
|
|
465
|
+
(_b2 = (_a2 = window.visualViewport) == null ? void 0 : _a2.addEventListener) == null ? void 0 : _b2.call(_a2, "resize", onResize);
|
|
466
|
+
(_d2 = (_c2 = window.visualViewport) == null ? void 0 : _c2.addEventListener) == null ? void 0 : _d2.call(_c2, "scroll", onResize);
|
|
467
|
+
if (typeof ResizeObserver === "function") {
|
|
468
|
+
const panelResizeObserver = new ResizeObserver(schedulePosition);
|
|
469
|
+
panelResizeObserver.observe(panel);
|
|
470
|
+
entry._panelResizeObserver = panelResizeObserver;
|
|
471
|
+
}
|
|
472
|
+
entry._positionCleanup = () => {
|
|
473
|
+
var _a3, _b3, _c3, _d3, _e2, _f2;
|
|
474
|
+
const caf = globalThis.cancelAnimationFrame || clearTimeout;
|
|
475
|
+
if (entry._positionFrame != null) {
|
|
476
|
+
caf(entry._positionFrame);
|
|
477
|
+
entry._positionFrame = null;
|
|
478
|
+
}
|
|
479
|
+
(_b3 = (_a3 = entry._panelResizeObserver) == null ? void 0 : _a3.disconnect) == null ? void 0 : _b3.call(_a3);
|
|
480
|
+
entry._panelResizeObserver = null;
|
|
481
|
+
window.removeEventListener("resize", onResize);
|
|
482
|
+
window.removeEventListener("scroll", onResize, true);
|
|
483
|
+
(_d3 = (_c3 = window.visualViewport) == null ? void 0 : _c3.removeEventListener) == null ? void 0 : _d3.call(_c3, "resize", onResize);
|
|
484
|
+
(_f2 = (_e2 = window.visualViewport) == null ? void 0 : _e2.removeEventListener) == null ? void 0 : _f2.call(_e2, "scroll", onResize);
|
|
485
|
+
};
|
|
486
|
+
}
|
|
487
|
+
const onDocClick = (e) => {
|
|
488
|
+
const top = getTop();
|
|
489
|
+
if (!top || top.id !== id) return;
|
|
490
|
+
if (opts.closeOnOutside === false) return;
|
|
491
|
+
if (isOverlayInteractivePortal(e.target)) return;
|
|
492
|
+
if (!panel.contains(e.target)) close(id);
|
|
493
|
+
};
|
|
494
|
+
document.addEventListener("mousedown", onDocClick, true);
|
|
495
|
+
const onKeyDown = (e) => {
|
|
496
|
+
const top = getTop();
|
|
497
|
+
if (!top || top.id !== id) return;
|
|
498
|
+
if (opts.trapFocus) trapFocus(e, panel);
|
|
499
|
+
if (e.key === "Escape" && opts.closeOnEsc !== false) {
|
|
500
|
+
e.preventDefault();
|
|
501
|
+
close(id);
|
|
502
|
+
}
|
|
503
|
+
};
|
|
504
|
+
document.addEventListener("keydown", onKeyDown, true);
|
|
505
|
+
backdrop.addEventListener("click", () => {
|
|
506
|
+
if (opts.closeOnBackdrop === false) return;
|
|
507
|
+
close(id);
|
|
508
|
+
});
|
|
509
|
+
entry._cleanup = () => {
|
|
510
|
+
var _a3;
|
|
511
|
+
document.removeEventListener("mousedown", onDocClick, true);
|
|
512
|
+
document.removeEventListener("keydown", onKeyDown, true);
|
|
513
|
+
(_a3 = entry._positionCleanup) == null ? void 0 : _a3.call(entry);
|
|
514
|
+
};
|
|
515
|
+
const z = 1e3 + stack.size * 10;
|
|
516
|
+
overlay.style.zIndex = String(z);
|
|
517
|
+
return { id, close: () => close(id), panel, overlay, reposition: position };
|
|
518
|
+
};
|
|
519
|
+
const close = (id) => {
|
|
520
|
+
var _a2, _b2, _c2;
|
|
521
|
+
const entry = stack.get(id);
|
|
522
|
+
if (!entry) return;
|
|
523
|
+
stack.delete(id);
|
|
524
|
+
(_a2 = entry._cleanup) == null ? void 0 : _a2.call(entry);
|
|
525
|
+
if (entry.opts.lockScroll) unlockScroll();
|
|
526
|
+
if (typeof cleanupNodeTree === "function") cleanupNodeTree(entry.overlay);
|
|
527
|
+
(_b2 = entry.overlay) == null ? void 0 : _b2.remove();
|
|
528
|
+
(_c2 = entry.onClose) == null ? void 0 : _c2.call(entry);
|
|
529
|
+
};
|
|
530
|
+
const closeTop = () => {
|
|
531
|
+
const top = getTop();
|
|
532
|
+
if (top) close(top.id);
|
|
533
|
+
};
|
|
534
|
+
return { open, close, closeTop, root, _stack: stack };
|
|
535
|
+
})();
|
|
536
|
+
function createDomPropBridge(el, options = {}) {
|
|
537
|
+
const { isSVG = false, normalizeClass: normalizeClassValue = (value) => String(value != null ? value : ""), isContentProp: isContent = () => false, autoValueTags = /* @__PURE__ */ new Set(["INPUT", "TEXTAREA", "SELECT"]) } = options;
|
|
538
|
+
function isBooleanDomProp(name) {
|
|
539
|
+
if (isSVG || !(name in el)) return false;
|
|
540
|
+
try {
|
|
541
|
+
return typeof el[name] === "boolean";
|
|
542
|
+
} catch (e) {
|
|
543
|
+
return false;
|
|
544
|
+
}
|
|
545
|
+
}
|
|
546
|
+
function isAttributeOnlyProp(name) {
|
|
547
|
+
return isSVG || name.startsWith("data-") || name.startsWith("aria-") || !(name in el);
|
|
548
|
+
}
|
|
549
|
+
function setAttributeValue(name, value) {
|
|
550
|
+
if (value == null) {
|
|
551
|
+
el.removeAttribute(name);
|
|
552
|
+
return;
|
|
553
|
+
}
|
|
554
|
+
if (value === false) {
|
|
555
|
+
if (name.startsWith("aria-")) {
|
|
556
|
+
el.setAttribute(name, "false");
|
|
557
|
+
return;
|
|
558
|
+
}
|
|
559
|
+
el.removeAttribute(name);
|
|
560
|
+
return;
|
|
561
|
+
}
|
|
562
|
+
if (value === true && !isSVG && !name.startsWith("aria-") && !name.startsWith("data-")) {
|
|
563
|
+
el.setAttribute(name, "");
|
|
564
|
+
return;
|
|
565
|
+
}
|
|
566
|
+
el.setAttribute(name, String(value));
|
|
567
|
+
}
|
|
568
|
+
function setStyleEntry(name, value) {
|
|
569
|
+
if (name == null) return;
|
|
570
|
+
const styleName = String(name);
|
|
571
|
+
const isCssProperty = styleName.startsWith("--") || styleName.includes("-");
|
|
572
|
+
if (value == null || value === false || value === "") {
|
|
573
|
+
if (isCssProperty) el.style.removeProperty(styleName);
|
|
574
|
+
else el.style[styleName] = "";
|
|
575
|
+
return;
|
|
576
|
+
}
|
|
577
|
+
if (isCssProperty) {
|
|
578
|
+
el.style.setProperty(styleName, String(value));
|
|
579
|
+
return;
|
|
580
|
+
}
|
|
581
|
+
el.style[styleName] = value;
|
|
582
|
+
}
|
|
583
|
+
function setClassValue(value) {
|
|
584
|
+
const normalized = normalizeClassValue(value);
|
|
585
|
+
if (normalized == null || normalized === false || normalized === "") {
|
|
586
|
+
el.removeAttribute("class");
|
|
587
|
+
return;
|
|
588
|
+
}
|
|
589
|
+
el.setAttribute("class", String(normalized));
|
|
590
|
+
}
|
|
591
|
+
function setBooleanProp(name, value) {
|
|
592
|
+
const next = !!value;
|
|
593
|
+
el[name] = next;
|
|
594
|
+
if (next) el.setAttribute(name, "");
|
|
595
|
+
else el.removeAttribute(name);
|
|
596
|
+
}
|
|
597
|
+
function removeProp(name) {
|
|
598
|
+
if (isContent(name)) {
|
|
599
|
+
el[name] = "";
|
|
600
|
+
return;
|
|
601
|
+
}
|
|
602
|
+
if (name === "class") {
|
|
603
|
+
el.removeAttribute("class");
|
|
604
|
+
return;
|
|
605
|
+
}
|
|
606
|
+
if (name === "style") {
|
|
607
|
+
el.removeAttribute("style");
|
|
608
|
+
return;
|
|
609
|
+
}
|
|
610
|
+
if (isBooleanDomProp(name)) {
|
|
611
|
+
try {
|
|
612
|
+
el[name] = false;
|
|
613
|
+
} catch (e) {
|
|
614
|
+
}
|
|
615
|
+
el.removeAttribute(name);
|
|
616
|
+
return;
|
|
617
|
+
}
|
|
618
|
+
el.removeAttribute(name);
|
|
619
|
+
if (!isAttributeOnlyProp(name)) {
|
|
620
|
+
try {
|
|
621
|
+
if (typeof el[name] === "string") el[name] = "";
|
|
622
|
+
} catch (e) {
|
|
623
|
+
}
|
|
624
|
+
}
|
|
625
|
+
}
|
|
626
|
+
function applyStyleObject(styleObj) {
|
|
627
|
+
if (!styleObj || typeof styleObj !== "object") return;
|
|
628
|
+
Object.entries(styleObj).forEach(([styleName, styleValue]) => {
|
|
629
|
+
setStyleEntry(styleName, styleValue);
|
|
630
|
+
});
|
|
631
|
+
}
|
|
632
|
+
function setProp(name, value) {
|
|
633
|
+
if (isContent(name)) {
|
|
634
|
+
el[name] = value != null ? value : "";
|
|
635
|
+
return;
|
|
636
|
+
}
|
|
637
|
+
if (name === "class") {
|
|
638
|
+
setClassValue(value);
|
|
639
|
+
return;
|
|
640
|
+
}
|
|
641
|
+
if (name === "style") {
|
|
642
|
+
if (value == null || value === false) {
|
|
643
|
+
removeProp(name);
|
|
644
|
+
return;
|
|
645
|
+
}
|
|
646
|
+
if (typeof value === "object") {
|
|
647
|
+
applyStyleObject(value);
|
|
648
|
+
return;
|
|
649
|
+
}
|
|
650
|
+
}
|
|
651
|
+
if (isBooleanDomProp(name)) {
|
|
652
|
+
setBooleanProp(name, value);
|
|
653
|
+
return;
|
|
654
|
+
}
|
|
655
|
+
if (value == null || value === false) {
|
|
656
|
+
removeProp(name);
|
|
657
|
+
return;
|
|
658
|
+
}
|
|
659
|
+
if (isAttributeOnlyProp(name)) {
|
|
660
|
+
setAttributeValue(name, value);
|
|
661
|
+
return;
|
|
662
|
+
}
|
|
663
|
+
el[name] = value;
|
|
664
|
+
}
|
|
665
|
+
function setPathValue(path, value) {
|
|
666
|
+
const parts = String(path).split(".");
|
|
667
|
+
let obj = el;
|
|
668
|
+
for (let i = 0; i < parts.length - 1; i++) {
|
|
669
|
+
obj = obj == null ? void 0 : obj[parts[i]];
|
|
670
|
+
if (!obj) return;
|
|
671
|
+
}
|
|
672
|
+
obj[parts[parts.length - 1]] = value;
|
|
673
|
+
}
|
|
674
|
+
function isBindingValueKey(name) {
|
|
675
|
+
if (!name) return false;
|
|
676
|
+
return name === "auto" || name.startsWith("attr:") || name.startsWith("@") || name.startsWith("style.") || name.includes(".");
|
|
677
|
+
}
|
|
678
|
+
function applyBindingValue(name, value) {
|
|
679
|
+
if (!name || name === "auto") {
|
|
680
|
+
if (autoValueTags.has(el.tagName)) {
|
|
681
|
+
setProp("value", value);
|
|
682
|
+
} else {
|
|
683
|
+
el.textContent = value != null ? value : "";
|
|
684
|
+
}
|
|
685
|
+
return;
|
|
686
|
+
}
|
|
687
|
+
if (name.startsWith("attr:")) {
|
|
688
|
+
setAttributeValue(name.slice(5), value);
|
|
689
|
+
return;
|
|
690
|
+
}
|
|
691
|
+
if (name.startsWith("@")) {
|
|
692
|
+
setAttributeValue(name.slice(1), value);
|
|
693
|
+
return;
|
|
694
|
+
}
|
|
695
|
+
if (name.startsWith("style.")) {
|
|
696
|
+
setStyleEntry(name.slice(6), value);
|
|
697
|
+
return;
|
|
698
|
+
}
|
|
699
|
+
if (name.includes(".")) {
|
|
700
|
+
setPathValue(name, value);
|
|
701
|
+
return;
|
|
702
|
+
}
|
|
703
|
+
setProp(name, value);
|
|
704
|
+
}
|
|
705
|
+
return { isBooleanDomProp, isAttributeOnlyProp, isBindingValueKey, setAttributeValue, setStyleEntry, setClassValue, setBooleanProp, removeProp, applyStyleObject, setProp, setPathValue, applyBindingValue };
|
|
706
|
+
}
|
|
707
|
+
function normalizeClass(v) {
|
|
708
|
+
const tokens = [];
|
|
709
|
+
const add = (value) => {
|
|
710
|
+
if (value == null || value === false) return;
|
|
711
|
+
if (typeof value === "function") {
|
|
712
|
+
add(value());
|
|
713
|
+
return;
|
|
714
|
+
}
|
|
715
|
+
if (value && value.type === "rod") {
|
|
716
|
+
add(value.value);
|
|
717
|
+
return;
|
|
718
|
+
}
|
|
719
|
+
if (Array.isArray(value)) {
|
|
720
|
+
value.forEach(add);
|
|
721
|
+
return;
|
|
722
|
+
}
|
|
723
|
+
if (typeof value === "object") {
|
|
724
|
+
Object.entries(value).forEach(([className, enabled]) => {
|
|
725
|
+
let resolved = enabled;
|
|
726
|
+
if (typeof resolved === "function") resolved = resolved();
|
|
727
|
+
else if (resolved && resolved.type === "rod") resolved = resolved.value;
|
|
728
|
+
add(resolved ? className : null);
|
|
729
|
+
});
|
|
730
|
+
return;
|
|
731
|
+
}
|
|
732
|
+
if (value === true) return;
|
|
733
|
+
String(value).split(/\s+/).forEach((token) => {
|
|
734
|
+
if (token) tokens.push(token);
|
|
735
|
+
});
|
|
736
|
+
};
|
|
737
|
+
add(v);
|
|
738
|
+
return tokens.length ? Array.from(new Set(tokens)).join(" ") : "";
|
|
739
|
+
}
|
|
740
|
+
function hasDynamicClassValue(value) {
|
|
741
|
+
if (value == null || value === false) return false;
|
|
742
|
+
if (typeof value === "function") return true;
|
|
743
|
+
if (value && value.type === "rod") return true;
|
|
744
|
+
if (Array.isArray(value)) return value.some(hasDynamicClassValue);
|
|
745
|
+
if (typeof value === "object") return Object.values(value).some(hasDynamicClassValue);
|
|
746
|
+
return false;
|
|
747
|
+
}
|
|
748
|
+
function hasDynamicStyleValue(value, isRod) {
|
|
749
|
+
if (value == null || value === false) return false;
|
|
750
|
+
if (typeof value === "function") return true;
|
|
751
|
+
if (isRod(value)) return true;
|
|
752
|
+
if (typeof value !== "object" || Array.isArray(value) || value.nodeType) return false;
|
|
753
|
+
return Object.values(value).some((entry) => hasDynamicStyleValue(entry, isRod));
|
|
754
|
+
}
|
|
755
|
+
function resolveStyleObject(value, isRod) {
|
|
756
|
+
let next = value;
|
|
757
|
+
if (typeof next === "function") next = next();
|
|
758
|
+
else if (isRod(next)) next = next.value;
|
|
759
|
+
if (!next || typeof next !== "object" || Array.isArray(next) || next.nodeType) {
|
|
760
|
+
return null;
|
|
761
|
+
}
|
|
762
|
+
const out = {};
|
|
763
|
+
Object.entries(next).forEach(([styleName, styleValue]) => {
|
|
764
|
+
let resolved = styleValue;
|
|
765
|
+
if (typeof resolved === "function") resolved = resolved();
|
|
766
|
+
else if (isRod(resolved)) resolved = resolved.value;
|
|
767
|
+
out[styleName] = resolved;
|
|
768
|
+
});
|
|
769
|
+
return out;
|
|
770
|
+
}
|
|
771
|
+
function createStyleObjectApplier(setStyleEntry) {
|
|
772
|
+
let previousKeys = /* @__PURE__ */ new Set();
|
|
773
|
+
function clearMissing(nextKeys) {
|
|
774
|
+
previousKeys.forEach((styleName) => {
|
|
775
|
+
if (!nextKeys.has(styleName)) setStyleEntry(styleName, null);
|
|
776
|
+
});
|
|
777
|
+
previousKeys = nextKeys;
|
|
778
|
+
}
|
|
779
|
+
function apply(value, isRod) {
|
|
780
|
+
const nextStyle = resolveStyleObject(value, isRod);
|
|
781
|
+
if (!nextStyle) {
|
|
782
|
+
clearMissing(/* @__PURE__ */ new Set());
|
|
783
|
+
return;
|
|
784
|
+
}
|
|
785
|
+
const nextKeys = new Set(Object.keys(nextStyle));
|
|
786
|
+
clearMissing(nextKeys);
|
|
787
|
+
Object.entries(nextStyle).forEach(([styleName, styleValue]) => {
|
|
788
|
+
setStyleEntry(styleName, styleValue);
|
|
789
|
+
});
|
|
790
|
+
}
|
|
791
|
+
return { apply };
|
|
792
|
+
}
|
|
793
|
+
function isEventProp(key) {
|
|
794
|
+
return typeof key === "string" && (key.startsWith("on:") || key.startsWith("on") && key.length > 2);
|
|
795
|
+
}
|
|
796
|
+
const ROD_INTERPOLATION_BUFFER_LIMIT = 128;
|
|
797
|
+
const rodInterpolationBuffer = [];
|
|
798
|
+
function trackRodInterpolation(rod, value) {
|
|
799
|
+
rodInterpolationBuffer.push({ rod, value: String(value != null ? value : "") });
|
|
800
|
+
if (rodInterpolationBuffer.length > ROD_INTERPOLATION_BUFFER_LIMIT) {
|
|
801
|
+
rodInterpolationBuffer.splice(0, rodInterpolationBuffer.length - ROD_INTERPOLATION_BUFFER_LIMIT);
|
|
802
|
+
}
|
|
803
|
+
}
|
|
804
|
+
function createRodInterpolationCursor() {
|
|
805
|
+
if (!rodInterpolationBuffer.length) return null;
|
|
806
|
+
const items = rodInterpolationBuffer.slice();
|
|
807
|
+
rodInterpolationBuffer.length = 0;
|
|
808
|
+
return { items, index: 0 };
|
|
809
|
+
}
|
|
810
|
+
function takeInterpolatedSegments(text, cursor) {
|
|
811
|
+
if (!cursor || !text || cursor.index >= cursor.items.length) return null;
|
|
812
|
+
const segments = [];
|
|
813
|
+
const source = String(text);
|
|
814
|
+
let offset = 0;
|
|
815
|
+
let nextIndex = cursor.index;
|
|
816
|
+
let matched = false;
|
|
817
|
+
while (nextIndex < cursor.items.length) {
|
|
818
|
+
const entry = cursor.items[nextIndex];
|
|
819
|
+
const needle = entry.value;
|
|
820
|
+
if (!needle) break;
|
|
821
|
+
const pos = source.indexOf(needle, offset);
|
|
822
|
+
if (pos === -1) break;
|
|
823
|
+
if (pos > offset) segments.push(source.slice(offset, pos));
|
|
824
|
+
segments.push(entry.rod);
|
|
825
|
+
offset = pos + needle.length;
|
|
826
|
+
nextIndex++;
|
|
827
|
+
matched = true;
|
|
828
|
+
}
|
|
829
|
+
if (!matched) return null;
|
|
830
|
+
if (offset < source.length) segments.push(source.slice(offset));
|
|
831
|
+
cursor.index = nextIndex;
|
|
832
|
+
return segments;
|
|
833
|
+
}
|
|
834
|
+
function renderInterpolatedSegments(segments) {
|
|
835
|
+
var _a2;
|
|
836
|
+
let out = "";
|
|
837
|
+
for (const part of segments) {
|
|
838
|
+
if (typeof part === "string") out += part;
|
|
839
|
+
else out += (_a2 = part == null ? void 0 : part.value) != null ? _a2 : "";
|
|
840
|
+
}
|
|
841
|
+
return out;
|
|
842
|
+
}
|
|
843
|
+
function getEventName(key) {
|
|
844
|
+
if (key.startsWith("on:")) return key.slice(3);
|
|
845
|
+
const raw = key.slice(2);
|
|
846
|
+
if (!raw) return "";
|
|
847
|
+
if (raw === "DoubleClick") return "dblclick";
|
|
848
|
+
return raw.toLowerCase();
|
|
849
|
+
}
|
|
850
|
+
function normalizeEventOptions(options) {
|
|
851
|
+
if (options == null || options === false) return false;
|
|
852
|
+
if (options === true) return { capture: true };
|
|
853
|
+
if (typeof options === "boolean") return options;
|
|
854
|
+
if (typeof options === "object") {
|
|
855
|
+
return { capture: !!options.capture, passive: !!options.passive, once: !!options.once };
|
|
856
|
+
}
|
|
857
|
+
return false;
|
|
858
|
+
}
|
|
859
|
+
function eventOptionsEqual(a, b) {
|
|
860
|
+
const left = normalizeEventOptions(a);
|
|
861
|
+
const right = normalizeEventOptions(b);
|
|
862
|
+
if (typeof left === "boolean" || typeof right === "boolean") return left === right;
|
|
863
|
+
return !!left && !!right && left.capture === right.capture && left.passive === right.passive && left.once === right.once;
|
|
864
|
+
}
|
|
865
|
+
function hasDynamicEventValue(value, isRod) {
|
|
866
|
+
if (Array.isArray(value)) return value.some((entry) => hasDynamicEventValue(entry, isRod));
|
|
867
|
+
if (isRod(value)) return true;
|
|
868
|
+
if (!value || typeof value !== "object" || Array.isArray(value) || value.nodeType) return false;
|
|
869
|
+
if (Array.isArray(value.handlers)) return value.handlers.some((entry) => hasDynamicEventValue(entry, isRod)) || isRod(value.options) || typeof value.options === "function";
|
|
870
|
+
if (Array.isArray(value.listeners)) return value.listeners.some((entry) => hasDynamicEventValue(entry, isRod)) || isRod(value.options) || typeof value.options === "function";
|
|
871
|
+
return isRod(value.handler) || isRod(value.listener) || isRod(value.fn) || isRod(value.options) || typeof value.options === "function";
|
|
872
|
+
}
|
|
873
|
+
function normalizeEventEntry(value, isRod, inheritedOptions) {
|
|
874
|
+
var _a2, _b2, _c2, _d2, _e2, _f2, _g2;
|
|
875
|
+
if (value == null || value === false) {
|
|
876
|
+
return [];
|
|
877
|
+
}
|
|
878
|
+
if (typeof value === "function") {
|
|
879
|
+
return [{ handler: value, options: normalizeEventOptions(inheritedOptions != null ? inheritedOptions : false) }];
|
|
880
|
+
}
|
|
881
|
+
if (isRod(value)) {
|
|
882
|
+
return normalizeEventEntry(value.value, isRod, inheritedOptions);
|
|
883
|
+
}
|
|
884
|
+
if (Array.isArray(value)) {
|
|
885
|
+
return value.flatMap((entry) => normalizeEventEntry(entry, isRod, inheritedOptions));
|
|
886
|
+
}
|
|
887
|
+
if (typeof value === "object" && !Array.isArray(value) && !value.nodeType) {
|
|
888
|
+
if (Array.isArray(value.handlers) || Array.isArray(value.listeners)) {
|
|
889
|
+
const handlers = value.handlers || value.listeners || [];
|
|
890
|
+
const sharedOptions = (_b2 = (_a2 = value.options) != null ? _a2 : inheritedOptions) != null ? _b2 : false;
|
|
891
|
+
return handlers.flatMap((entry) => normalizeEventEntry(entry, isRod, sharedOptions));
|
|
892
|
+
}
|
|
893
|
+
let handler = (_e2 = (_d2 = (_c2 = value.handler) != null ? _c2 : value.listener) != null ? _d2 : value.fn) != null ? _e2 : null;
|
|
894
|
+
let options = (_g2 = (_f2 = value.options) != null ? _f2 : inheritedOptions) != null ? _g2 : false;
|
|
895
|
+
if (isRod(handler)) handler = handler.value;
|
|
896
|
+
if (isRod(options)) options = options.value;
|
|
897
|
+
if (typeof options === "function") options = options();
|
|
898
|
+
return typeof handler === "function" ? [{ handler, options: normalizeEventOptions(options) }] : [];
|
|
899
|
+
}
|
|
900
|
+
return [];
|
|
901
|
+
}
|
|
902
|
+
function normalizeEventValue(value, isRod) {
|
|
903
|
+
return normalizeEventEntry(value, isRod);
|
|
904
|
+
}
|
|
905
|
+
function bindEventProp(el, key, value, isRod) {
|
|
906
|
+
const eventName = getEventName(key);
|
|
907
|
+
if (!eventName) return;
|
|
908
|
+
const state = { listeners: [] };
|
|
909
|
+
const detach = () => {
|
|
910
|
+
state.listeners.forEach((entry) => {
|
|
911
|
+
el.removeEventListener(eventName, entry.dispatch, entry.options);
|
|
912
|
+
});
|
|
913
|
+
state.listeners = [];
|
|
914
|
+
};
|
|
915
|
+
const apply = (nextValue) => {
|
|
916
|
+
const nextListeners = normalizeEventValue(nextValue, isRod);
|
|
917
|
+
const isSameShape = state.listeners.length === nextListeners.length && state.listeners.every((entry, index) => {
|
|
918
|
+
const next = nextListeners[index];
|
|
919
|
+
return entry.handler === next.handler && eventOptionsEqual(entry.options, next.options);
|
|
920
|
+
});
|
|
921
|
+
if (isSameShape) return;
|
|
922
|
+
detach();
|
|
923
|
+
nextListeners.forEach((listener) => {
|
|
924
|
+
const entry = { handler: listener.handler, options: listener.options, dispatch: null };
|
|
925
|
+
entry.dispatch = (event) => {
|
|
926
|
+
if (typeof entry.handler === "function") {
|
|
927
|
+
const result = entry.handler.call(el, event);
|
|
928
|
+
if (entry.options && typeof entry.options === "object" && entry.options.once) {
|
|
929
|
+
el.removeEventListener(eventName, entry.dispatch, entry.options);
|
|
930
|
+
state.listeners = state.listeners.filter((item) => item !== entry);
|
|
931
|
+
}
|
|
932
|
+
return result;
|
|
933
|
+
}
|
|
934
|
+
};
|
|
935
|
+
el.addEventListener(eventName, entry.dispatch, entry.options);
|
|
936
|
+
state.listeners.push(entry);
|
|
937
|
+
});
|
|
938
|
+
};
|
|
939
|
+
JSswift._registerCleanup(el, detach);
|
|
940
|
+
if (isRod(value)) {
|
|
941
|
+
const stop = JSswift.reactive.effect(() => {
|
|
942
|
+
apply(value.value);
|
|
943
|
+
});
|
|
944
|
+
JSswift._registerCleanup(el, stop);
|
|
945
|
+
return;
|
|
946
|
+
}
|
|
947
|
+
if (hasDynamicEventValue(value, isRod)) {
|
|
948
|
+
const stop = JSswift.reactive.effect(() => {
|
|
949
|
+
apply(value);
|
|
950
|
+
});
|
|
951
|
+
JSswift._registerCleanup(el, stop);
|
|
952
|
+
return;
|
|
953
|
+
}
|
|
954
|
+
apply(value);
|
|
955
|
+
}
|
|
956
|
+
function createRendererChildHelpers(el, options = {}) {
|
|
957
|
+
const { isRod = () => false, interpolationCursor = null } = options;
|
|
958
|
+
function createRodTextNode(rod) {
|
|
959
|
+
const t = document.createTextNode("");
|
|
960
|
+
const unbind = JSswift.rodBind(t, rod);
|
|
961
|
+
JSswift._registerCleanup(t, unbind);
|
|
962
|
+
return t;
|
|
963
|
+
}
|
|
964
|
+
function appendRodText(rod) {
|
|
965
|
+
el.appendChild(createRodTextNode(rod));
|
|
966
|
+
}
|
|
967
|
+
function appendInterpolatedText(segments) {
|
|
968
|
+
const t = document.createTextNode("");
|
|
969
|
+
el.appendChild(t);
|
|
970
|
+
const stop = JSswift.reactive.effect(() => {
|
|
971
|
+
t.textContent = renderInterpolatedSegments(segments);
|
|
972
|
+
});
|
|
973
|
+
JSswift._registerCleanup(t, stop);
|
|
974
|
+
}
|
|
975
|
+
function normalizeDynamicChildNodes(value) {
|
|
976
|
+
const out = [];
|
|
977
|
+
const add = (item) => {
|
|
978
|
+
if (item == null || item === false) return;
|
|
979
|
+
if (Array.isArray(item)) {
|
|
980
|
+
item.forEach(add);
|
|
981
|
+
return;
|
|
982
|
+
}
|
|
983
|
+
if (typeof item === "string") {
|
|
984
|
+
out.push(document.createTextNode(item));
|
|
985
|
+
return;
|
|
986
|
+
}
|
|
987
|
+
if (typeof item === "number") {
|
|
988
|
+
out.push(document.createTextNode(String(item)));
|
|
989
|
+
return;
|
|
990
|
+
}
|
|
991
|
+
if (typeof item === "boolean") return;
|
|
992
|
+
if (isRod(item)) {
|
|
993
|
+
out.push(createRodTextNode(item));
|
|
994
|
+
return;
|
|
995
|
+
}
|
|
996
|
+
if (item == null ? void 0 : item.nodeType) {
|
|
997
|
+
out.push(item);
|
|
998
|
+
return;
|
|
999
|
+
}
|
|
1000
|
+
out.push(document.createTextNode(String(item)));
|
|
1001
|
+
};
|
|
1002
|
+
add(value);
|
|
1003
|
+
return out;
|
|
1004
|
+
}
|
|
1005
|
+
function appendDynamicChild(renderFn) {
|
|
1006
|
+
const anchor = document.createComment("dyn");
|
|
1007
|
+
el.appendChild(anchor);
|
|
1008
|
+
let currentNodes = [];
|
|
1009
|
+
const stop = JSswift.reactive.effect(() => {
|
|
1010
|
+
currentNodes.forEach((node) => {
|
|
1011
|
+
cleanupNodeTree(node);
|
|
1012
|
+
if (node.parentNode) node.parentNode.removeChild(node);
|
|
1013
|
+
});
|
|
1014
|
+
currentNodes = [];
|
|
1015
|
+
const parent = anchor.parentNode;
|
|
1016
|
+
if (!parent) return;
|
|
1017
|
+
const nextNodes = normalizeDynamicChildNodes(renderFn());
|
|
1018
|
+
if (!nextNodes.length) return;
|
|
1019
|
+
const frag = document.createDocumentFragment();
|
|
1020
|
+
nextNodes.forEach((node) => frag.appendChild(node));
|
|
1021
|
+
parent.insertBefore(frag, anchor.nextSibling);
|
|
1022
|
+
currentNodes = nextNodes;
|
|
1023
|
+
});
|
|
1024
|
+
JSswift._registerCleanup(anchor, () => {
|
|
1025
|
+
stop();
|
|
1026
|
+
currentNodes.forEach((node) => cleanupNodeTree(node));
|
|
1027
|
+
currentNodes = [];
|
|
1028
|
+
});
|
|
1029
|
+
}
|
|
1030
|
+
function appendChildValue(value) {
|
|
1031
|
+
if (value == null) return;
|
|
1032
|
+
if (Array.isArray(value)) {
|
|
1033
|
+
for (const item of value) appendChildValue(item);
|
|
1034
|
+
return;
|
|
1035
|
+
}
|
|
1036
|
+
if (typeof value === "string") {
|
|
1037
|
+
const segments = takeInterpolatedSegments(value, interpolationCursor);
|
|
1038
|
+
if (segments) appendInterpolatedText(segments);
|
|
1039
|
+
else el.appendChild(document.createTextNode(value));
|
|
1040
|
+
return;
|
|
1041
|
+
}
|
|
1042
|
+
if (typeof value === "number") {
|
|
1043
|
+
el.appendChild(document.createTextNode(String(value)));
|
|
1044
|
+
return;
|
|
1045
|
+
}
|
|
1046
|
+
if (typeof value === "function") {
|
|
1047
|
+
appendDynamicChild(value);
|
|
1048
|
+
return;
|
|
1049
|
+
}
|
|
1050
|
+
if (isRod(value)) {
|
|
1051
|
+
appendRodText(value);
|
|
1052
|
+
return;
|
|
1053
|
+
}
|
|
1054
|
+
if (value.nodeType) {
|
|
1055
|
+
el.appendChild(value);
|
|
1056
|
+
}
|
|
1057
|
+
}
|
|
1058
|
+
return { appendRodText, appendInterpolatedText, appendDynamicChild, appendChildValue };
|
|
1059
|
+
}
|
|
1060
|
+
function applyRendererArgs(args, options = {}) {
|
|
1061
|
+
const { el, isRod = () => false, interpolationCursor = null, appendChildValue = () => {
|
|
1062
|
+
}, appendRodText = () => {
|
|
1063
|
+
}, appendInterpolatedText = () => {
|
|
1064
|
+
}, appendDynamicChild = () => {
|
|
1065
|
+
}, bindProp = () => {
|
|
1066
|
+
} } = options;
|
|
1067
|
+
for (const arg of args) {
|
|
1068
|
+
if (arg == null) continue;
|
|
1069
|
+
if (Array.isArray(arg)) {
|
|
1070
|
+
appendChildValue(arg);
|
|
1071
|
+
continue;
|
|
1072
|
+
}
|
|
1073
|
+
if (typeof arg === "string") {
|
|
1074
|
+
const segments = takeInterpolatedSegments(arg, interpolationCursor);
|
|
1075
|
+
if (segments) appendInterpolatedText(segments);
|
|
1076
|
+
else el.appendChild(document.createTextNode(arg));
|
|
1077
|
+
continue;
|
|
1078
|
+
}
|
|
1079
|
+
if (typeof arg === "number") {
|
|
1080
|
+
el.appendChild(document.createTextNode(String(arg)));
|
|
1081
|
+
continue;
|
|
1082
|
+
}
|
|
1083
|
+
if (typeof arg === "function") {
|
|
1084
|
+
appendDynamicChild(arg);
|
|
1085
|
+
continue;
|
|
1086
|
+
}
|
|
1087
|
+
if (isRod(arg)) {
|
|
1088
|
+
appendRodText(arg);
|
|
1089
|
+
continue;
|
|
1090
|
+
}
|
|
1091
|
+
if (arg.nodeType) {
|
|
1092
|
+
el.appendChild(arg);
|
|
1093
|
+
continue;
|
|
1094
|
+
}
|
|
1095
|
+
if (typeof arg === "object") {
|
|
1096
|
+
for (const [key, value] of Object.entries(arg)) {
|
|
1097
|
+
if (typeof value === "string") {
|
|
1098
|
+
const segments = takeInterpolatedSegments(value, interpolationCursor);
|
|
1099
|
+
if (segments) {
|
|
1100
|
+
bindProp(key, () => renderInterpolatedSegments(segments));
|
|
1101
|
+
continue;
|
|
1102
|
+
}
|
|
1103
|
+
}
|
|
1104
|
+
if (key === "class") {
|
|
1105
|
+
bindProp(key, value);
|
|
1106
|
+
continue;
|
|
1107
|
+
}
|
|
1108
|
+
if (key === "style" && typeof value === "object" && value !== null && !isRod(value)) {
|
|
1109
|
+
bindProp(key, value);
|
|
1110
|
+
continue;
|
|
1111
|
+
}
|
|
1112
|
+
bindProp(key, value);
|
|
1113
|
+
}
|
|
1114
|
+
}
|
|
1115
|
+
}
|
|
1116
|
+
}
|
|
1117
|
+
const SVG_NS = "http://www.w3.org/2000/svg";
|
|
1118
|
+
const SVG_TAGS = /* @__PURE__ */ new Set(["svg", "g", "path", "circle", "ellipse", "line", "rect", "polygon", "polyline", "text", "tspan", "defs", "linearGradient", "radialGradient", "stop", "use", "symbol", "clipPath", "mask", "pattern", "filter", "feGaussianBlur", "feOffset", "feBlend", "feColorMatrix"]);
|
|
1119
|
+
function isContentProp(key) {
|
|
1120
|
+
return key === "innerHTML" || key === "innerText" || key === "textContent" || key === "value";
|
|
1121
|
+
}
|
|
1122
|
+
function createElement(tag, ...args) {
|
|
1123
|
+
const isSVG = SVG_TAGS.has(tag);
|
|
1124
|
+
const el = tag === "text" && !isSVG ? document.createTextNode("") : isSVG ? document.createElementNS(SVG_NS, tag) : document.createElement(tag);
|
|
1125
|
+
const isRod = (v) => !!v && v.type === "rod";
|
|
1126
|
+
const interpolationCursor = createRodInterpolationCursor();
|
|
1127
|
+
const childHelpers = createRendererChildHelpers(el, { isRod, interpolationCursor });
|
|
1128
|
+
const { appendRodText, appendInterpolatedText, appendDynamicChild, appendChildValue } = childHelpers;
|
|
1129
|
+
const domBridge = createDomPropBridge(el, { isSVG, normalizeClass, isContentProp });
|
|
1130
|
+
const { isBooleanDomProp, isBindingValueKey, applyBindingValue, setStyleEntry, setProp, setClassValue } = domBridge;
|
|
1131
|
+
function registerNodeEffect(run) {
|
|
1132
|
+
const stop = JSswift.reactive.effect(run);
|
|
1133
|
+
JSswift._registerCleanup(el, stop);
|
|
1134
|
+
return stop;
|
|
1135
|
+
}
|
|
1136
|
+
function isNumericValueControl(control) {
|
|
1137
|
+
if (String((control == null ? void 0 : control.tagName) || "").toLowerCase() !== "input") return false;
|
|
1138
|
+
const inputType = String((control == null ? void 0 : control.type) || "").toLowerCase();
|
|
1139
|
+
return inputType === "range" || inputType === "number";
|
|
1140
|
+
}
|
|
1141
|
+
function getValueControlType(control) {
|
|
1142
|
+
if (String((control == null ? void 0 : control.tagName) || "").toLowerCase() !== "input") return "";
|
|
1143
|
+
return String((control == null ? void 0 : control.type) || "").toLowerCase();
|
|
1144
|
+
}
|
|
1145
|
+
function isSingleSelectControl(control) {
|
|
1146
|
+
return String((control == null ? void 0 : control.tagName) || "").toLowerCase() === "select" && !control.multiple;
|
|
1147
|
+
}
|
|
1148
|
+
function isFileInputControl(control) {
|
|
1149
|
+
return String((control == null ? void 0 : control.tagName) || "").toLowerCase() === "input" && String((control == null ? void 0 : control.type) || "").toLowerCase() === "file";
|
|
1150
|
+
}
|
|
1151
|
+
function bindRodValueControl(control, rodValue, eventName) {
|
|
1152
|
+
registerNodeEffect(() => {
|
|
1153
|
+
var _a2;
|
|
1154
|
+
const next = (_a2 = rodValue.value) != null ? _a2 : "";
|
|
1155
|
+
if (control.value !== String(next)) setProp("value", next);
|
|
1156
|
+
});
|
|
1157
|
+
const onValueChange = () => {
|
|
1158
|
+
let next = control.value;
|
|
1159
|
+
const controlType = getValueControlType(control);
|
|
1160
|
+
const isNumericRod = typeof rodValue.value === "number" || rodValue.value == null;
|
|
1161
|
+
if (isSingleSelectControl(control) && control.value === "") {
|
|
1162
|
+
next = null;
|
|
1163
|
+
} else if (isNumericValueControl(control) && isNumericRod) {
|
|
1164
|
+
if (controlType === "number" && control.value === "") {
|
|
1165
|
+
next = null;
|
|
1166
|
+
} else {
|
|
1167
|
+
const numeric = typeof control.valueAsNumber === "number" && !Number.isNaN(control.valueAsNumber) ? control.valueAsNumber : Number(control.value);
|
|
1168
|
+
next = Number.isNaN(numeric) ? controlType === "number" ? null : 0 : numeric;
|
|
1169
|
+
}
|
|
1170
|
+
}
|
|
1171
|
+
if (rodValue.value !== next) rodValue.value = next;
|
|
1172
|
+
};
|
|
1173
|
+
control.addEventListener(eventName, onValueChange);
|
|
1174
|
+
JSswift._registerCleanup(control, () => {
|
|
1175
|
+
control.removeEventListener(eventName, onValueChange);
|
|
1176
|
+
});
|
|
1177
|
+
}
|
|
1178
|
+
function bindRodCheckedControl(control, rodValue) {
|
|
1179
|
+
registerNodeEffect(() => {
|
|
1180
|
+
const next = !!rodValue.value;
|
|
1181
|
+
if (!!control.checked !== next) setProp("checked", next);
|
|
1182
|
+
});
|
|
1183
|
+
const onCheckedChange = () => {
|
|
1184
|
+
const next = !!control.checked;
|
|
1185
|
+
if (rodValue.value !== next) rodValue.value = next;
|
|
1186
|
+
};
|
|
1187
|
+
control.addEventListener("change", onCheckedChange);
|
|
1188
|
+
JSswift._registerCleanup(control, () => {
|
|
1189
|
+
control.removeEventListener("change", onCheckedChange);
|
|
1190
|
+
});
|
|
1191
|
+
}
|
|
1192
|
+
function bindRodRadioControl(control, rodValue) {
|
|
1193
|
+
const readOwnValue = () => {
|
|
1194
|
+
var _a2;
|
|
1195
|
+
return String((_a2 = control.value) != null ? _a2 : "on");
|
|
1196
|
+
};
|
|
1197
|
+
registerNodeEffect(() => {
|
|
1198
|
+
const next = rodValue.value == null ? null : String(rodValue.value);
|
|
1199
|
+
const shouldCheck = next === readOwnValue();
|
|
1200
|
+
if (!!control.checked !== shouldCheck) setProp("checked", shouldCheck);
|
|
1201
|
+
});
|
|
1202
|
+
const onCheckedChange = () => {
|
|
1203
|
+
if (!control.checked) return;
|
|
1204
|
+
const next = readOwnValue();
|
|
1205
|
+
if (rodValue.value !== next) rodValue.value = next;
|
|
1206
|
+
};
|
|
1207
|
+
control.addEventListener("change", onCheckedChange);
|
|
1208
|
+
JSswift._registerCleanup(control, () => {
|
|
1209
|
+
control.removeEventListener("change", onCheckedChange);
|
|
1210
|
+
});
|
|
1211
|
+
}
|
|
1212
|
+
function bindRodFilesControl(control, rodValue) {
|
|
1213
|
+
const readFiles = () => {
|
|
1214
|
+
const files = control.files;
|
|
1215
|
+
if (files == null) return [];
|
|
1216
|
+
if (Array.isArray(files)) return files.slice();
|
|
1217
|
+
if (typeof files.length === "number") return Array.from(files);
|
|
1218
|
+
return [files];
|
|
1219
|
+
};
|
|
1220
|
+
registerNodeEffect(() => {
|
|
1221
|
+
const next = rodValue.value;
|
|
1222
|
+
const shouldClear = next == null || Array.isArray(next) && next.length === 0;
|
|
1223
|
+
if (!shouldClear) return;
|
|
1224
|
+
if (control.value !== "") setProp("value", "");
|
|
1225
|
+
if (Array.isArray(control.files)) control.files = [];
|
|
1226
|
+
});
|
|
1227
|
+
const onFilesChange = () => {
|
|
1228
|
+
const next = readFiles();
|
|
1229
|
+
rodValue.value = next;
|
|
1230
|
+
};
|
|
1231
|
+
control.addEventListener("change", onFilesChange);
|
|
1232
|
+
JSswift._registerCleanup(control, () => {
|
|
1233
|
+
control.removeEventListener("change", onFilesChange);
|
|
1234
|
+
});
|
|
1235
|
+
}
|
|
1236
|
+
function bindRodSelectMultiple(control, rodValue) {
|
|
1237
|
+
const readValues = () => Array.from(control.childNodes || []).filter((node) => (node == null ? void 0 : node.tagName) === "OPTION" && node.selected).map((node) => {
|
|
1238
|
+
var _a2;
|
|
1239
|
+
return String((_a2 = node.value) != null ? _a2 : "");
|
|
1240
|
+
});
|
|
1241
|
+
const applyFromRod = () => {
|
|
1242
|
+
const nextValues = Array.isArray(rodValue.value) ? rodValue.value.map((item) => String(item)) : [];
|
|
1243
|
+
const nextSet = new Set(nextValues);
|
|
1244
|
+
Array.from(control.childNodes || []).forEach((node) => {
|
|
1245
|
+
var _a2;
|
|
1246
|
+
if ((node == null ? void 0 : node.tagName) !== "OPTION") return;
|
|
1247
|
+
const shouldSelect = nextSet.has(String((_a2 = node.value) != null ? _a2 : ""));
|
|
1248
|
+
if (!!node.selected === shouldSelect) return;
|
|
1249
|
+
node.selected = shouldSelect;
|
|
1250
|
+
if (shouldSelect) node.setAttribute("selected", "");
|
|
1251
|
+
else node.removeAttribute("selected");
|
|
1252
|
+
});
|
|
1253
|
+
};
|
|
1254
|
+
registerNodeEffect(applyFromRod);
|
|
1255
|
+
control._jssApplySelectedValues = applyFromRod;
|
|
1256
|
+
queueMicrotask(applyFromRod);
|
|
1257
|
+
const onValueChange = () => {
|
|
1258
|
+
const next = readValues();
|
|
1259
|
+
const current = Array.isArray(rodValue.value) ? rodValue.value.map((item) => String(item)) : [];
|
|
1260
|
+
if (current.length === next.length && current.every((item, index) => item === next[index])) return;
|
|
1261
|
+
rodValue.value = next;
|
|
1262
|
+
};
|
|
1263
|
+
control.addEventListener("change", onValueChange);
|
|
1264
|
+
JSswift._registerCleanup(control, () => {
|
|
1265
|
+
control.removeEventListener("change", onValueChange);
|
|
1266
|
+
});
|
|
1267
|
+
}
|
|
1268
|
+
function bindRodOptionSelected(optionEl, rodValue) {
|
|
1269
|
+
registerNodeEffect(() => {
|
|
1270
|
+
const next = !!rodValue.value;
|
|
1271
|
+
if (!!optionEl.selected !== next) setProp("selected", next);
|
|
1272
|
+
});
|
|
1273
|
+
let parentSelect = null;
|
|
1274
|
+
const onParentChange = () => {
|
|
1275
|
+
const next = !!optionEl.selected;
|
|
1276
|
+
if (rodValue.value !== next) rodValue.value = next;
|
|
1277
|
+
};
|
|
1278
|
+
const attachParent = () => {
|
|
1279
|
+
var _a2;
|
|
1280
|
+
if (parentSelect || ((_a2 = optionEl.parentNode) == null ? void 0 : _a2.tagName) !== "SELECT") return;
|
|
1281
|
+
parentSelect = optionEl.parentNode;
|
|
1282
|
+
parentSelect.addEventListener("change", onParentChange);
|
|
1283
|
+
};
|
|
1284
|
+
queueMicrotask(attachParent);
|
|
1285
|
+
JSswift._registerCleanup(optionEl, () => {
|
|
1286
|
+
parentSelect == null ? void 0 : parentSelect.removeEventListener("change", onParentChange);
|
|
1287
|
+
});
|
|
1288
|
+
}
|
|
1289
|
+
function bindProp(key, value) {
|
|
1290
|
+
if (isEventProp(key)) {
|
|
1291
|
+
bindEventProp(el, key, value, isRod);
|
|
1292
|
+
return;
|
|
1293
|
+
}
|
|
1294
|
+
if (key === "class") {
|
|
1295
|
+
if (hasDynamicClassValue(value)) {
|
|
1296
|
+
registerNodeEffect(() => {
|
|
1297
|
+
setClassValue(normalizeClass(value));
|
|
1298
|
+
});
|
|
1299
|
+
return;
|
|
1300
|
+
}
|
|
1301
|
+
setClassValue(normalizeClass(value));
|
|
1302
|
+
return;
|
|
1303
|
+
}
|
|
1304
|
+
if (key === "style" && value && typeof value === "object") {
|
|
1305
|
+
const styleApplier = createStyleObjectApplier(setStyleEntry);
|
|
1306
|
+
if (hasDynamicStyleValue(value, isRod)) {
|
|
1307
|
+
registerNodeEffect(() => {
|
|
1308
|
+
styleApplier.apply(value, isRod);
|
|
1309
|
+
});
|
|
1310
|
+
return;
|
|
1311
|
+
}
|
|
1312
|
+
styleApplier.apply(value, isRod);
|
|
1313
|
+
return;
|
|
1314
|
+
}
|
|
1315
|
+
if (key === "style" && (typeof value === "function" || isRod(value))) {
|
|
1316
|
+
const styleApplier = createStyleObjectApplier(setStyleEntry);
|
|
1317
|
+
registerNodeEffect(() => {
|
|
1318
|
+
styleApplier.apply(value, isRod);
|
|
1319
|
+
});
|
|
1320
|
+
return;
|
|
1321
|
+
}
|
|
1322
|
+
if (isBindingValueKey(key) && typeof value === "function") {
|
|
1323
|
+
registerNodeEffect(() => {
|
|
1324
|
+
applyBindingValue(key, value());
|
|
1325
|
+
});
|
|
1326
|
+
return;
|
|
1327
|
+
}
|
|
1328
|
+
if (isBindingValueKey(key) && !isRod(value)) {
|
|
1329
|
+
applyBindingValue(key, value);
|
|
1330
|
+
return;
|
|
1331
|
+
}
|
|
1332
|
+
if (typeof value === "function") {
|
|
1333
|
+
registerNodeEffect(() => {
|
|
1334
|
+
setProp(key, value());
|
|
1335
|
+
});
|
|
1336
|
+
return;
|
|
1337
|
+
}
|
|
1338
|
+
if (key === "value" && isRod(value) && (tag === "input" || tag === "textarea" || tag === "select")) {
|
|
1339
|
+
if (tag === "select" && !!el.multiple) {
|
|
1340
|
+
bindRodSelectMultiple(el, value);
|
|
1341
|
+
return;
|
|
1342
|
+
}
|
|
1343
|
+
bindRodValueControl(el, value, tag === "select" ? "change" : "input");
|
|
1344
|
+
return;
|
|
1345
|
+
}
|
|
1346
|
+
if (key === "checked" && isRod(value) && tag === "input") {
|
|
1347
|
+
if (String(el.type || "").toLowerCase() === "radio") {
|
|
1348
|
+
bindRodRadioControl(el, value);
|
|
1349
|
+
return;
|
|
1350
|
+
}
|
|
1351
|
+
bindRodCheckedControl(el, value);
|
|
1352
|
+
return;
|
|
1353
|
+
}
|
|
1354
|
+
if (key === "files" && isRod(value) && isFileInputControl(el)) {
|
|
1355
|
+
bindRodFilesControl(el, value);
|
|
1356
|
+
return;
|
|
1357
|
+
}
|
|
1358
|
+
if (key === "selected" && isRod(value) && tag === "option") {
|
|
1359
|
+
bindRodOptionSelected(el, value);
|
|
1360
|
+
return;
|
|
1361
|
+
}
|
|
1362
|
+
if (isContentProp(key) && isRod(value)) {
|
|
1363
|
+
registerNodeEffect(() => {
|
|
1364
|
+
setProp(key, value.value);
|
|
1365
|
+
});
|
|
1366
|
+
return;
|
|
1367
|
+
}
|
|
1368
|
+
if (isRod(value)) {
|
|
1369
|
+
const unbind = JSswift.rodBind(el, value, { key });
|
|
1370
|
+
JSswift._registerCleanup(el, unbind);
|
|
1371
|
+
return;
|
|
1372
|
+
}
|
|
1373
|
+
setProp(key, value);
|
|
1374
|
+
}
|
|
1375
|
+
applyRendererArgs(args, { el, isRod, interpolationCursor, appendChildValue, appendRodText, appendInterpolatedText, appendDynamicChild, bindProp });
|
|
1376
|
+
if (tag === "select" && typeof el._jssApplySelectedValues === "function") {
|
|
1377
|
+
el._jssApplySelectedValues();
|
|
1378
|
+
}
|
|
1379
|
+
return el;
|
|
1380
|
+
}
|
|
1381
|
+
const _h = {};
|
|
1382
|
+
const DOM_ELEMENTS = ["html", "head", "title", "base", "link", "meta", "style", "body", "header", "footer", "main", "section", "article", "aside", "nav", "h1", "h2", "h3", "h4", "h5", "h6", "p", "div", "span", "pre", "blockquote", "hr", "br", "address", "a", "abbr", "b", "bdi", "bdo", "cite", "code", "data", "dfn", "em", "i", "kbd", "mark", "q", "rp", "rt", "ruby", "s", "samp", "small", "strong", "sub", "sup", "time", "u", "var", "wbr", "ul", "ol", "li", "dl", "dt", "dd", "table", "caption", "colgroup", "col", "thead", "tbody", "tfoot", "tr", "th", "td", "form", "label", "input", "textarea", "button", "select", "option", "optgroup", "fieldset", "legend", "datalist", "output", "progress", "meter", "img", "picture", "source", "iframe", "embed", "object", "param", "audio", "video", "track", "details", "summary", "dialog", "script", "noscript", "canvas", "template", "slot", "svg", "g", "path", "circle", "ellipse", "line", "rect", "polygon", "polyline", "text", "tspan", "defs", "linearGradient", "radialGradient", "stop", "use", "symbol", "clipPath", "mask", "pattern", "filter", "feGaussianBlur", "feOffset", "feBlend", "feColorMatrix"];
|
|
1383
|
+
DOM_ELEMENTS.forEach((tag) => {
|
|
1384
|
+
window._[tag] = (...args) => createElement(tag, ...args);
|
|
1385
|
+
});
|
|
1386
|
+
_.DOM_ELEMENTS = DOM_ELEMENTS;
|
|
1387
|
+
_.fragment = (...children) => children;
|
|
1388
|
+
_.dynamic = function(renderFn) {
|
|
1389
|
+
const anchor = document.createComment("dyn");
|
|
1390
|
+
const parent = document.createDocumentFragment();
|
|
1391
|
+
parent.appendChild(anchor);
|
|
1392
|
+
let current = null;
|
|
1393
|
+
JSswift.reactive.effect(() => {
|
|
1394
|
+
const next = renderFn();
|
|
1395
|
+
if (current && current.parentNode) current.parentNode.removeChild(current);
|
|
1396
|
+
current = null;
|
|
1397
|
+
if (next == null) return;
|
|
1398
|
+
let node = next;
|
|
1399
|
+
if (typeof next === "string" || typeof next === "number") {
|
|
1400
|
+
node = document.createTextNode(String(next));
|
|
1401
|
+
}
|
|
1402
|
+
const p = anchor.parentNode;
|
|
1403
|
+
if (p && node && node.nodeType) {
|
|
1404
|
+
p.insertBefore(node, anchor.nextSibling);
|
|
1405
|
+
current = node;
|
|
1406
|
+
}
|
|
1407
|
+
});
|
|
1408
|
+
return parent;
|
|
1409
|
+
};
|
|
1410
|
+
function createMicrotaskScheduler() {
|
|
1411
|
+
let queued = false;
|
|
1412
|
+
const queue = /* @__PURE__ */ new Set();
|
|
1413
|
+
function flush() {
|
|
1414
|
+
var _a2, _b2, _c2;
|
|
1415
|
+
queued = false;
|
|
1416
|
+
(_a2 = JSswift.debug) == null ? void 0 : _a2.inc("rodFlushes");
|
|
1417
|
+
(_b2 = JSswift.perf) == null ? void 0 : _b2.inc("rodFlushes");
|
|
1418
|
+
(_c2 = JSswift.perf) == null ? void 0 : _c2.mark("rod:flush");
|
|
1419
|
+
const jobs = Array.from(queue);
|
|
1420
|
+
queue.clear();
|
|
1421
|
+
for (const fn of jobs) {
|
|
1422
|
+
try {
|
|
1423
|
+
fn();
|
|
1424
|
+
} catch (e) {
|
|
1425
|
+
console.error("[rod] flush error:", e);
|
|
1426
|
+
}
|
|
1427
|
+
}
|
|
1428
|
+
}
|
|
1429
|
+
return function schedule(fn) {
|
|
1430
|
+
queue.add(fn);
|
|
1431
|
+
if (!queued) {
|
|
1432
|
+
queued = true;
|
|
1433
|
+
queueMicrotask(flush);
|
|
1434
|
+
}
|
|
1435
|
+
};
|
|
1436
|
+
}
|
|
1437
|
+
const rodSchedule = createMicrotaskScheduler();
|
|
1438
|
+
function rodApplyBinding(el, key, value) {
|
|
1439
|
+
if (!el) return;
|
|
1440
|
+
if (el.nodeType === 3) {
|
|
1441
|
+
el.textContent = value != null ? value : "";
|
|
1442
|
+
return;
|
|
1443
|
+
}
|
|
1444
|
+
const domBridge = createDomPropBridge(el, { isSVG: el.namespaceURI === SVG_NS, normalizeClass, isContentProp });
|
|
1445
|
+
domBridge.applyBindingValue(key, value);
|
|
1446
|
+
}
|
|
1447
|
+
function rodCreateComponent(initialValue) {
|
|
1448
|
+
const comp = { type: "rod", value: initialValue, _bindings: [], _actions: [], _disposeHooks: [], _disposed: false, _silentDepth: 0, _pending: false, bindings() {
|
|
1449
|
+
return this._bindings.map((b) => ({ el: b.el, key: b.key }));
|
|
1450
|
+
}, onDispose(fn) {
|
|
1451
|
+
if (typeof fn === "function") this._disposeHooks.push(fn);
|
|
1452
|
+
return this;
|
|
1453
|
+
}, _bind(el, config = { key: "auto" }) {
|
|
1454
|
+
var _a2;
|
|
1455
|
+
if (this._disposed) return () => {
|
|
1456
|
+
};
|
|
1457
|
+
if (!el) return () => {
|
|
1458
|
+
};
|
|
1459
|
+
const b = { el, key: (_a2 = config == null ? void 0 : config.key) != null ? _a2 : "auto" };
|
|
1460
|
+
const exists = this._bindings.some((x) => x.el === b.el && x.key === b.key);
|
|
1461
|
+
if (!exists) this._bindings.push(b);
|
|
1462
|
+
rodApplyBinding(b.el, b.key, this.value);
|
|
1463
|
+
return () => {
|
|
1464
|
+
this._bindings = this._bindings.filter((x) => !(x.el === b.el && x.key === b.key));
|
|
1465
|
+
};
|
|
1466
|
+
}, action(fn) {
|
|
1467
|
+
if (!this._disposed && typeof fn === "function") this._actions.push(fn);
|
|
1468
|
+
return this;
|
|
1469
|
+
}, _setSilent(v) {
|
|
1470
|
+
this._silentDepth++;
|
|
1471
|
+
try {
|
|
1472
|
+
this.value = v;
|
|
1473
|
+
} finally {
|
|
1474
|
+
this._silentDepth--;
|
|
1475
|
+
}
|
|
1476
|
+
return this;
|
|
1477
|
+
}, _scheduleNotify() {
|
|
1478
|
+
if (this._disposed || this._pending) return;
|
|
1479
|
+
this._pending = true;
|
|
1480
|
+
rodSchedule(() => {
|
|
1481
|
+
this._pending = false;
|
|
1482
|
+
this._notifyNow();
|
|
1483
|
+
});
|
|
1484
|
+
}, _notifyNow() {
|
|
1485
|
+
var _a2, _b2, _c2, _d2, _e2;
|
|
1486
|
+
if (this._disposed) return;
|
|
1487
|
+
const __t0 = performance.now();
|
|
1488
|
+
(_a2 = JSswift.perf) == null ? void 0 : _a2.inc("rodNotifies");
|
|
1489
|
+
this._bindings = this._bindings.filter((b) => {
|
|
1490
|
+
const el = b.el;
|
|
1491
|
+
if (!el) return false;
|
|
1492
|
+
if (el.nodeType === 3) return !!el.parentNode;
|
|
1493
|
+
return el.isConnected !== false;
|
|
1494
|
+
});
|
|
1495
|
+
for (const b of this._bindings) {
|
|
1496
|
+
rodApplyBinding(b.el, b.key, this.value);
|
|
1497
|
+
}
|
|
1498
|
+
if (this._silentDepth > 0) return;
|
|
1499
|
+
for (const fn of this._actions) {
|
|
1500
|
+
try {
|
|
1501
|
+
fn(this.value);
|
|
1502
|
+
} catch (e) {
|
|
1503
|
+
console.error("[rod] action error:", e);
|
|
1504
|
+
}
|
|
1505
|
+
}
|
|
1506
|
+
(_b2 = JSswift.debug) == null ? void 0 : _b2.inc("rodNotifies");
|
|
1507
|
+
(_e2 = JSswift.perf) == null ? void 0 : _e2.tick("rod:notify", performance.now() - __t0, { bindings: ((_c2 = this._bindings) == null ? void 0 : _c2.length) || 0, actions: ((_d2 = this._actions) == null ? void 0 : _d2.length) || 0 });
|
|
1508
|
+
}, dispose() {
|
|
1509
|
+
if (this._disposed) return;
|
|
1510
|
+
this._disposed = true;
|
|
1511
|
+
for (const fn of this._disposeHooks) {
|
|
1512
|
+
try {
|
|
1513
|
+
fn();
|
|
1514
|
+
} catch (e) {
|
|
1515
|
+
console.error("[rod] dispose hook error:", e);
|
|
1516
|
+
}
|
|
1517
|
+
}
|
|
1518
|
+
this._bindings.length = 0;
|
|
1519
|
+
this._actions.length = 0;
|
|
1520
|
+
this._disposeHooks.length = 0;
|
|
1521
|
+
} };
|
|
1522
|
+
return comp;
|
|
1523
|
+
}
|
|
1524
|
+
function rodMakeReactive(obj, key = "value") {
|
|
1525
|
+
var _a2;
|
|
1526
|
+
let _val = obj[key];
|
|
1527
|
+
let _get = null;
|
|
1528
|
+
let _set = null;
|
|
1529
|
+
let _disposeSignal = null;
|
|
1530
|
+
if ((_a2 = JSswift == null ? void 0 : JSswift.reactive) == null ? void 0 : _a2.signal) {
|
|
1531
|
+
[_get, _set, _disposeSignal] = JSswift.reactive.signal(_val);
|
|
1532
|
+
if (_disposeSignal && typeof obj.onDispose === "function") {
|
|
1533
|
+
obj.onDispose(_disposeSignal);
|
|
1534
|
+
}
|
|
1535
|
+
}
|
|
1536
|
+
Object.defineProperty(obj, key, { get() {
|
|
1537
|
+
return _get ? _get() : _val;
|
|
1538
|
+
}, set(v) {
|
|
1539
|
+
if (_set) _set(v);
|
|
1540
|
+
else _val = v;
|
|
1541
|
+
obj._scheduleNotify();
|
|
1542
|
+
}, configurable: true });
|
|
1543
|
+
if (typeof obj.val !== "function") {
|
|
1544
|
+
Object.defineProperty(obj, "val", { value(v) {
|
|
1545
|
+
if (arguments.length) {
|
|
1546
|
+
this.value = v;
|
|
1547
|
+
return v;
|
|
1548
|
+
}
|
|
1549
|
+
return this.value;
|
|
1550
|
+
}, configurable: true });
|
|
1551
|
+
}
|
|
1552
|
+
if (!obj[Symbol.toPrimitive]) {
|
|
1553
|
+
Object.defineProperty(obj, Symbol.toPrimitive, { value(hint) {
|
|
1554
|
+
const v = this.value;
|
|
1555
|
+
if (v == null) return v;
|
|
1556
|
+
const t = typeof v;
|
|
1557
|
+
if (hint === "number") return Number(v);
|
|
1558
|
+
if (t !== "symbol") trackRodInterpolation(this, v);
|
|
1559
|
+
if (t === "string" || t === "number" || t === "boolean" || t === "bigint" || t === "symbol") return v;
|
|
1560
|
+
const out = String(v);
|
|
1561
|
+
return out;
|
|
1562
|
+
}, configurable: true });
|
|
1563
|
+
}
|
|
1564
|
+
if (!obj.valueOf) {
|
|
1565
|
+
Object.defineProperty(obj, "valueOf", { value() {
|
|
1566
|
+
return this.value;
|
|
1567
|
+
}, configurable: true });
|
|
1568
|
+
}
|
|
1569
|
+
if (!obj.toString) {
|
|
1570
|
+
Object.defineProperty(obj, "toString", { value() {
|
|
1571
|
+
return String(this.value);
|
|
1572
|
+
}, configurable: true });
|
|
1573
|
+
}
|
|
1574
|
+
return obj;
|
|
1575
|
+
}
|
|
1576
|
+
_.rod = function _rod(data, key = "value") {
|
|
1577
|
+
const comp = rodCreateComponent(data);
|
|
1578
|
+
rodMakeReactive(comp, key);
|
|
1579
|
+
JSswift.rod._all = JSswift.rod._all || /* @__PURE__ */ new Set();
|
|
1580
|
+
JSswift.rod._all.add(comp);
|
|
1581
|
+
if (typeof comp.onDispose === "function") {
|
|
1582
|
+
comp.onDispose(() => JSswift.rod._all.delete(comp));
|
|
1583
|
+
}
|
|
1584
|
+
return comp;
|
|
1585
|
+
};
|
|
1586
|
+
JSswift.rodBind = function(el, react, config = { key: "auto" }) {
|
|
1587
|
+
var _a2, _b2, _c2;
|
|
1588
|
+
(_a2 = JSswift.debug) == null ? void 0 : _a2.inc("rodBinds");
|
|
1589
|
+
if (!el) {
|
|
1590
|
+
(_b2 = JSswift.debug) == null ? void 0 : _b2.inc("rodBindNull");
|
|
1591
|
+
(_c2 = JSswift.debug) == null ? void 0 : _c2.warn("rodBind: target element \xE8 null/undefined", config);
|
|
1592
|
+
return () => {
|
|
1593
|
+
};
|
|
1594
|
+
}
|
|
1595
|
+
if (!react || react.type !== "rod") {
|
|
1596
|
+
throw new Error("JSswift.rodBind: react must be a rod object");
|
|
1597
|
+
}
|
|
1598
|
+
return react._bind(el, config);
|
|
1599
|
+
};
|
|
1600
|
+
JSswift.rodFromSignal = function(get, set) {
|
|
1601
|
+
const r = _.rod(get());
|
|
1602
|
+
let syncing = false;
|
|
1603
|
+
const stopRodToSignal = JSswift.reactive.effect(() => {
|
|
1604
|
+
const v = r.value;
|
|
1605
|
+
const currentSignal = JSswift.reactive.untracked(() => get());
|
|
1606
|
+
if (syncing) return;
|
|
1607
|
+
if (currentSignal === v) return;
|
|
1608
|
+
syncing = true;
|
|
1609
|
+
try {
|
|
1610
|
+
set(v);
|
|
1611
|
+
} finally {
|
|
1612
|
+
syncing = false;
|
|
1613
|
+
}
|
|
1614
|
+
});
|
|
1615
|
+
const stopEffect = JSswift.reactive.effect(() => {
|
|
1616
|
+
const v = get();
|
|
1617
|
+
const currentRod = JSswift.reactive.untracked(() => r.value);
|
|
1618
|
+
if (currentRod === v) return;
|
|
1619
|
+
syncing = true;
|
|
1620
|
+
try {
|
|
1621
|
+
r._setSilent(v);
|
|
1622
|
+
} finally {
|
|
1623
|
+
syncing = false;
|
|
1624
|
+
}
|
|
1625
|
+
});
|
|
1626
|
+
if (typeof r.onDispose === "function") {
|
|
1627
|
+
r.onDispose(stopRodToSignal);
|
|
1628
|
+
r.onDispose(stopEffect);
|
|
1629
|
+
}
|
|
1630
|
+
return r;
|
|
1631
|
+
};
|
|
1632
|
+
function getInputKind(el) {
|
|
1633
|
+
if (!el || !el.tagName) return null;
|
|
1634
|
+
const tag = el.tagName.toLowerCase();
|
|
1635
|
+
if (tag === "input") {
|
|
1636
|
+
const type = (el.type || "text").toLowerCase();
|
|
1637
|
+
if (type === "checkbox") return "checkbox";
|
|
1638
|
+
if (type === "radio") return "radio";
|
|
1639
|
+
if (type === "number") return "number";
|
|
1640
|
+
return "text";
|
|
1641
|
+
}
|
|
1642
|
+
if (tag === "select") {
|
|
1643
|
+
return el.multiple ? "select-multiple" : "select";
|
|
1644
|
+
}
|
|
1645
|
+
return null;
|
|
1646
|
+
}
|
|
1647
|
+
JSswift.rodModel = function(target, rodObj, opts = {}) {
|
|
1648
|
+
var _a2, _b2, _c2;
|
|
1649
|
+
if (!rodObj || rodObj.type !== "rod") {
|
|
1650
|
+
throw new Error("[JSswift.rodModel] rodObj deve essere un rod");
|
|
1651
|
+
}
|
|
1652
|
+
const elements = Array.isArray(target) || target instanceof NodeList ? Array.from(target) : [target];
|
|
1653
|
+
const eventName = (_a2 = opts.event) != null ? _a2 : "change";
|
|
1654
|
+
const parse = (_b2 = opts.parse) != null ? _b2 : ((v) => v);
|
|
1655
|
+
const format = (_c2 = opts.format) != null ? _c2 : ((v) => v);
|
|
1656
|
+
let syncing = false;
|
|
1657
|
+
const disposers = [];
|
|
1658
|
+
const updateDOM = (value) => {
|
|
1659
|
+
for (const el of elements) {
|
|
1660
|
+
if (!el) continue;
|
|
1661
|
+
const kind = getInputKind(el);
|
|
1662
|
+
switch (kind) {
|
|
1663
|
+
case "checkbox":
|
|
1664
|
+
el.checked = !!value;
|
|
1665
|
+
break;
|
|
1666
|
+
case "radio":
|
|
1667
|
+
el.checked = el.value == value;
|
|
1668
|
+
break;
|
|
1669
|
+
case "select":
|
|
1670
|
+
el.value = value != null ? value : "";
|
|
1671
|
+
break;
|
|
1672
|
+
case "select-multiple":
|
|
1673
|
+
const arr = Array.isArray(value) ? value.map(String) : [];
|
|
1674
|
+
for (const opt of el.options) {
|
|
1675
|
+
opt.selected = arr.includes(opt.value);
|
|
1676
|
+
}
|
|
1677
|
+
break;
|
|
1678
|
+
case "number":
|
|
1679
|
+
case "text":
|
|
1680
|
+
default:
|
|
1681
|
+
el.value = value != null ? value : "";
|
|
1682
|
+
}
|
|
1683
|
+
}
|
|
1684
|
+
};
|
|
1685
|
+
const onValue = (v) => {
|
|
1686
|
+
if (syncing) return;
|
|
1687
|
+
syncing = true;
|
|
1688
|
+
try {
|
|
1689
|
+
updateDOM(format(v));
|
|
1690
|
+
} finally {
|
|
1691
|
+
syncing = false;
|
|
1692
|
+
}
|
|
1693
|
+
};
|
|
1694
|
+
rodObj.action(onValue);
|
|
1695
|
+
updateDOM(format(rodObj.value));
|
|
1696
|
+
for (const el of elements) {
|
|
1697
|
+
if (!el) continue;
|
|
1698
|
+
const kind = getInputKind(el);
|
|
1699
|
+
const handler = () => {
|
|
1700
|
+
if (syncing) return;
|
|
1701
|
+
let next;
|
|
1702
|
+
switch (kind) {
|
|
1703
|
+
case "checkbox":
|
|
1704
|
+
next = el.checked;
|
|
1705
|
+
break;
|
|
1706
|
+
case "radio":
|
|
1707
|
+
if (!el.checked) return;
|
|
1708
|
+
next = el.value;
|
|
1709
|
+
break;
|
|
1710
|
+
case "select":
|
|
1711
|
+
next = el.value;
|
|
1712
|
+
break;
|
|
1713
|
+
case "select-multiple":
|
|
1714
|
+
next = Array.from(el.selectedOptions).map((o) => o.value);
|
|
1715
|
+
break;
|
|
1716
|
+
case "number":
|
|
1717
|
+
next = parse(el.value === "" ? null : Number(el.value));
|
|
1718
|
+
break;
|
|
1719
|
+
case "text":
|
|
1720
|
+
default:
|
|
1721
|
+
next = parse(el.value);
|
|
1722
|
+
}
|
|
1723
|
+
syncing = true;
|
|
1724
|
+
try {
|
|
1725
|
+
if (rodObj.value !== next) rodObj.value = next;
|
|
1726
|
+
} finally {
|
|
1727
|
+
syncing = false;
|
|
1728
|
+
}
|
|
1729
|
+
};
|
|
1730
|
+
el.addEventListener(eventName, handler);
|
|
1731
|
+
disposers.push(() => el.removeEventListener(eventName, handler));
|
|
1732
|
+
}
|
|
1733
|
+
let disposed = false;
|
|
1734
|
+
const dispose = () => {
|
|
1735
|
+
var _a3, _b3, _c3, _d2;
|
|
1736
|
+
if (disposed) return;
|
|
1737
|
+
disposed = true;
|
|
1738
|
+
disposers.splice(0).forEach((fn) => fn());
|
|
1739
|
+
const actionIndex = (_b3 = (_a3 = rodObj._actions) == null ? void 0 : _a3.indexOf(onValue)) != null ? _b3 : -1;
|
|
1740
|
+
if (actionIndex >= 0) rodObj._actions.splice(actionIndex, 1);
|
|
1741
|
+
const hookIndex = (_d2 = (_c3 = rodObj._disposeHooks) == null ? void 0 : _c3.indexOf(dispose)) != null ? _d2 : -1;
|
|
1742
|
+
if (hookIndex >= 0 && !rodObj._disposed) rodObj._disposeHooks.splice(hookIndex, 1);
|
|
1743
|
+
};
|
|
1744
|
+
if (typeof rodObj.onDispose === "function") rodObj.onDispose(dispose);
|
|
1745
|
+
return dispose;
|
|
1746
|
+
};
|
|
1747
|
+
JSswift.signalModel = function(inputEl, get, set, opts = {}) {
|
|
1748
|
+
const r = JSswift.rodFromSignal(get, set);
|
|
1749
|
+
const stopBinding = JSswift.rodModel(inputEl, r, opts);
|
|
1750
|
+
return () => {
|
|
1751
|
+
stopBinding();
|
|
1752
|
+
r.dispose();
|
|
1753
|
+
};
|
|
1754
|
+
};
|
|
1755
|
+
function cleanupNodeTree(node) {
|
|
1756
|
+
var _a2;
|
|
1757
|
+
if (!node) return;
|
|
1758
|
+
const children = ((_a2 = node.childNodes) == null ? void 0 : _a2.length) ? Array.from(node.childNodes) : [];
|
|
1759
|
+
const disposers = JSswift._cleanupRegistry.get(node);
|
|
1760
|
+
if (disposers) {
|
|
1761
|
+
JSswift._cleanupRegistry.delete(node);
|
|
1762
|
+
for (const d of disposers) {
|
|
1763
|
+
try {
|
|
1764
|
+
d();
|
|
1765
|
+
} catch (e) {
|
|
1766
|
+
console.error("[cleanup] error:", e);
|
|
1767
|
+
}
|
|
1768
|
+
}
|
|
1769
|
+
}
|
|
1770
|
+
for (const child of children) cleanupNodeTree(child);
|
|
1771
|
+
}
|
|
1772
|
+
function toMountTargets(target) {
|
|
1773
|
+
const toEl = (t) => typeof t === "string" ? JSswift.dom.q(t) : t;
|
|
1774
|
+
return Array.isArray(target) ? target.map(toEl).filter(Boolean) : [toEl(target)].filter(Boolean);
|
|
1775
|
+
}
|
|
1776
|
+
function normalizeMountContent(content) {
|
|
1777
|
+
const nodes = [];
|
|
1778
|
+
const disposers = [];
|
|
1779
|
+
const add = (value) => {
|
|
1780
|
+
if (value == null) return;
|
|
1781
|
+
if (typeof value === "function") {
|
|
1782
|
+
add(value());
|
|
1783
|
+
return;
|
|
1784
|
+
}
|
|
1785
|
+
if (value && typeof value === "object" && "node" in value) {
|
|
1786
|
+
if (typeof value.dispose === "function") disposers.push(value.dispose);
|
|
1787
|
+
add(value.node);
|
|
1788
|
+
return;
|
|
1789
|
+
}
|
|
1790
|
+
if (Array.isArray(value)) {
|
|
1791
|
+
for (const item of value) add(item);
|
|
1792
|
+
return;
|
|
1793
|
+
}
|
|
1794
|
+
if (typeof value === "string" || typeof value === "number") {
|
|
1795
|
+
nodes.push(document.createTextNode(String(value)));
|
|
1796
|
+
return;
|
|
1797
|
+
}
|
|
1798
|
+
if (value.nodeType) {
|
|
1799
|
+
nodes.push(value);
|
|
1800
|
+
return;
|
|
1801
|
+
}
|
|
1802
|
+
console.warn("[JSswift.mount] contenuto non supportato:", value);
|
|
1803
|
+
};
|
|
1804
|
+
add(content);
|
|
1805
|
+
return { nodes, disposers };
|
|
1806
|
+
}
|
|
1807
|
+
function createOnceDisposer(disposers = [], label = "[JSswift.mount] dispose error:") {
|
|
1808
|
+
let done = false;
|
|
1809
|
+
return () => {
|
|
1810
|
+
if (done) return;
|
|
1811
|
+
done = true;
|
|
1812
|
+
for (const d of disposers) {
|
|
1813
|
+
try {
|
|
1814
|
+
d();
|
|
1815
|
+
} catch (e) {
|
|
1816
|
+
console.error(label, e);
|
|
1817
|
+
}
|
|
1818
|
+
}
|
|
1819
|
+
};
|
|
1820
|
+
}
|
|
1821
|
+
function createComponentDisposer(disposers = [], userDispose = null) {
|
|
1822
|
+
return createOnceDisposer([...userDispose ? [userDispose] : [], ...disposers], "[component] dispose error:");
|
|
1823
|
+
}
|
|
1824
|
+
JSswift.mount = function(target, content, opts = {}) {
|
|
1825
|
+
var _a2;
|
|
1826
|
+
const targets = toMountTargets(target);
|
|
1827
|
+
const clear = (_a2 = opts.clear) != null ? _a2 : true;
|
|
1828
|
+
const isMulti = targets.length > 1;
|
|
1829
|
+
if (targets.length === 0) {
|
|
1830
|
+
console.warn("[JSswift.mount] nessun target valido:", target);
|
|
1831
|
+
return () => {
|
|
1832
|
+
};
|
|
1833
|
+
}
|
|
1834
|
+
const mounted = [];
|
|
1835
|
+
for (const root of targets) {
|
|
1836
|
+
if (clear) {
|
|
1837
|
+
while (root.firstChild) {
|
|
1838
|
+
cleanupNodeTree(root.firstChild);
|
|
1839
|
+
root.removeChild(root.firstChild);
|
|
1840
|
+
}
|
|
1841
|
+
}
|
|
1842
|
+
const { nodes: rawNodes, disposers } = normalizeMountContent(content);
|
|
1843
|
+
let nodes = rawNodes;
|
|
1844
|
+
if (isMulti && typeof content !== "function") {
|
|
1845
|
+
nodes = rawNodes.map((n) => n.cloneNode(true));
|
|
1846
|
+
if (disposers.length) {
|
|
1847
|
+
console.warn("[JSswift.mount] multi-target con dispose: usa content come function per istanze separate.");
|
|
1848
|
+
}
|
|
1849
|
+
}
|
|
1850
|
+
const disposeMounted = createOnceDisposer(disposers);
|
|
1851
|
+
for (const n of nodes) root.appendChild(n);
|
|
1852
|
+
mounted.push({ root, nodes, dispose: disposeMounted });
|
|
1853
|
+
for (const n of nodes) {
|
|
1854
|
+
if (!n || !n.nodeType) continue;
|
|
1855
|
+
JSswift._registerCleanup(n, disposeMounted);
|
|
1856
|
+
}
|
|
1857
|
+
}
|
|
1858
|
+
const unmount = () => {
|
|
1859
|
+
var _a3;
|
|
1860
|
+
for (const m of mounted) {
|
|
1861
|
+
for (const n of m.nodes) {
|
|
1862
|
+
if (!n) continue;
|
|
1863
|
+
cleanupNodeTree(n);
|
|
1864
|
+
if (n.parentNode === m.root) m.root.removeChild(n);
|
|
1865
|
+
}
|
|
1866
|
+
(_a3 = m.dispose) == null ? void 0 : _a3.call(m);
|
|
1867
|
+
}
|
|
1868
|
+
};
|
|
1869
|
+
return unmount;
|
|
1870
|
+
};
|
|
1871
|
+
JSswift.component = function(renderFn) {
|
|
1872
|
+
if (typeof renderFn !== "function") throw new Error("JSswift.component: renderFn must be a function");
|
|
1873
|
+
return function ComponentInstance(props = {}) {
|
|
1874
|
+
const disposers = [];
|
|
1875
|
+
const ctx = { onDispose(fn) {
|
|
1876
|
+
if (typeof fn === "function") disposers.push(fn);
|
|
1877
|
+
} };
|
|
1878
|
+
const out = renderFn(props, ctx);
|
|
1879
|
+
if (out && typeof out === "object" && "node" in out) {
|
|
1880
|
+
return { node: out.node, dispose: createComponentDisposer(disposers, typeof out.dispose === "function" ? out.dispose : null) };
|
|
1881
|
+
}
|
|
1882
|
+
return { node: out, dispose: createComponentDisposer(disposers) };
|
|
1883
|
+
};
|
|
1884
|
+
};
|
|
1885
|
+
JSswift.enableAutoCleanup = function() {
|
|
1886
|
+
if (JSswift._autoCleanupEnabled) return;
|
|
1887
|
+
JSswift._autoCleanupEnabled = true;
|
|
1888
|
+
const observer = new MutationObserver((mutations) => {
|
|
1889
|
+
for (const m of mutations) {
|
|
1890
|
+
for (const node of m.removedNodes) {
|
|
1891
|
+
cleanupNodeTree(node);
|
|
1892
|
+
}
|
|
1893
|
+
}
|
|
1894
|
+
});
|
|
1895
|
+
observer.observe(document.body, { childList: true, subtree: true });
|
|
1896
|
+
JSswift._cleanupObserver = observer;
|
|
1897
|
+
};
|
|
1898
|
+
JSswift.debug = /* @__PURE__ */ (() => {
|
|
1899
|
+
const counters = { rodFlushes: 0, rodNotifies: 0, rodBinds: 0, rodBindNull: 0, rodBindDup: 0 };
|
|
1900
|
+
function enabled() {
|
|
1901
|
+
return !!JSswift.config.debug;
|
|
1902
|
+
}
|
|
1903
|
+
function log(...a) {
|
|
1904
|
+
if (enabled()) console.log("[JSswift]", ...a);
|
|
1905
|
+
}
|
|
1906
|
+
function warn(...a) {
|
|
1907
|
+
if (enabled()) console.warn("[JSswift]", ...a);
|
|
1908
|
+
}
|
|
1909
|
+
function error(...a) {
|
|
1910
|
+
console.error("[JSswift]", ...a);
|
|
1911
|
+
}
|
|
1912
|
+
function inc(name, by = 1) {
|
|
1913
|
+
if (!counters[name]) counters[name] = 0;
|
|
1914
|
+
counters[name] += by;
|
|
1915
|
+
}
|
|
1916
|
+
function stats() {
|
|
1917
|
+
return { ...counters };
|
|
1918
|
+
}
|
|
1919
|
+
function reset() {
|
|
1920
|
+
for (const k of Object.keys(counters)) counters[k] = 0;
|
|
1921
|
+
}
|
|
1922
|
+
return { enabled, log, warn, error, inc, stats, reset };
|
|
1923
|
+
})();
|
|
1924
|
+
JSswift.perf = /* @__PURE__ */ (() => {
|
|
1925
|
+
let enabled = false;
|
|
1926
|
+
const counters = { effectsRun: 0, effectsSlow: 0, rodNotifies: 0, rodFlushes: 0, domWrites: 0, httpRequests: 0 };
|
|
1927
|
+
const timeline = [];
|
|
1928
|
+
const slow = [];
|
|
1929
|
+
const cfg = { timelineMax: 200, slowEffectMs: 8 };
|
|
1930
|
+
function now2() {
|
|
1931
|
+
return typeof performance !== "undefined" && performance.now ? performance.now() : Date.now();
|
|
1932
|
+
}
|
|
1933
|
+
function pushEvent(type, ms = 0, meta = null) {
|
|
1934
|
+
if (!enabled) return;
|
|
1935
|
+
timeline.push({ t: Date.now(), type, ms, meta });
|
|
1936
|
+
if (timeline.length > cfg.timelineMax) timeline.shift();
|
|
1937
|
+
}
|
|
1938
|
+
function inc(name, by = 1) {
|
|
1939
|
+
if (!enabled) return;
|
|
1940
|
+
counters[name] = (counters[name] || 0) + by;
|
|
1941
|
+
}
|
|
1942
|
+
function trackSlow(ms, meta) {
|
|
1943
|
+
if (!enabled) return;
|
|
1944
|
+
slow.push({ t: Date.now(), ms, meta });
|
|
1945
|
+
if (slow.length > 100) slow.shift();
|
|
1946
|
+
}
|
|
1947
|
+
let _origEffect = null;
|
|
1948
|
+
function patchEffect() {
|
|
1949
|
+
if (_origEffect) return;
|
|
1950
|
+
_origEffect = JSswift.reactive.effect;
|
|
1951
|
+
JSswift.reactive.effect = function(fn, meta) {
|
|
1952
|
+
const wrapped = (...args) => {
|
|
1953
|
+
const t0 = now2();
|
|
1954
|
+
inc("effectsRun");
|
|
1955
|
+
try {
|
|
1956
|
+
return fn(...args);
|
|
1957
|
+
} finally {
|
|
1958
|
+
const dt = now2() - t0;
|
|
1959
|
+
if (dt >= cfg.slowEffectMs) {
|
|
1960
|
+
inc("effectsSlow");
|
|
1961
|
+
trackSlow(dt, meta || fn.name || "effect");
|
|
1962
|
+
pushEvent("effect:slow", dt, meta || fn.name || null);
|
|
1963
|
+
} else {
|
|
1964
|
+
pushEvent("effect", dt, meta || fn.name || null);
|
|
1965
|
+
}
|
|
1966
|
+
}
|
|
1967
|
+
};
|
|
1968
|
+
return _origEffect(wrapped);
|
|
1969
|
+
};
|
|
1970
|
+
}
|
|
1971
|
+
function unpatchEffect() {
|
|
1972
|
+
if (_origEffect) {
|
|
1973
|
+
JSswift.reactive.effect = _origEffect;
|
|
1974
|
+
_origEffect = null;
|
|
1975
|
+
}
|
|
1976
|
+
}
|
|
1977
|
+
function enable(on = true) {
|
|
1978
|
+
enabled = !!on;
|
|
1979
|
+
if (enabled) patchEffect();
|
|
1980
|
+
else unpatchEffect();
|
|
1981
|
+
return enabled;
|
|
1982
|
+
}
|
|
1983
|
+
function reset() {
|
|
1984
|
+
for (const k of Object.keys(counters)) counters[k] = 0;
|
|
1985
|
+
timeline.length = 0;
|
|
1986
|
+
slow.length = 0;
|
|
1987
|
+
}
|
|
1988
|
+
function stats() {
|
|
1989
|
+
return { enabled, ...counters, slowEffectMs: cfg.slowEffectMs, timelineSize: timeline.length };
|
|
1990
|
+
}
|
|
1991
|
+
function getTimeline() {
|
|
1992
|
+
return timeline.slice();
|
|
1993
|
+
}
|
|
1994
|
+
function slowEffects(minMs = cfg.slowEffectMs) {
|
|
1995
|
+
return slow.filter((x) => x.ms >= minMs).slice().sort((a, b) => b.ms - a.ms);
|
|
1996
|
+
}
|
|
1997
|
+
function time(label, fn) {
|
|
1998
|
+
const t0 = now2();
|
|
1999
|
+
try {
|
|
2000
|
+
return fn();
|
|
2001
|
+
} finally {
|
|
2002
|
+
const dt = now2() - t0;
|
|
2003
|
+
pushEvent("time:" + label, dt, label);
|
|
2004
|
+
}
|
|
2005
|
+
}
|
|
2006
|
+
function mark(type, meta) {
|
|
2007
|
+
pushEvent(type, 0, meta);
|
|
2008
|
+
}
|
|
2009
|
+
function tick(type, ms, meta) {
|
|
2010
|
+
pushEvent(type, ms, meta);
|
|
2011
|
+
}
|
|
2012
|
+
return { enable, reset, stats, timeline: getTimeline, slowEffects, time, mark, tick, inc, cfg };
|
|
2013
|
+
})();
|
|
2014
|
+
JSswift.rod = JSswift.rod || {};
|
|
2015
|
+
JSswift.rod.inspect = function(r, label = "rod") {
|
|
2016
|
+
if (!r || r.type !== "rod") {
|
|
2017
|
+
console.warn("[JSswift.rod.inspect] non \xE8 un rod:", r);
|
|
2018
|
+
return null;
|
|
2019
|
+
}
|
|
2020
|
+
const bindings = typeof r.bindings === "function" ? r.bindings() : [];
|
|
2021
|
+
const info = { label, value: r.value, bindingsCount: bindings.length, bindings: bindings.map((b) => {
|
|
2022
|
+
var _a2, _b2, _c2, _d2, _e2, _f2, _g2;
|
|
2023
|
+
return { key: b.key, el: ((_a2 = b.el) == null ? void 0 : _a2.nodeType) === 3 ? "#text" : (_b2 = b.el) == null ? void 0 : _b2.tagName, id: ((_c2 = b.el) == null ? void 0 : _c2.id) || null, className: ((_d2 = b.el) == null ? void 0 : _d2.className) || null, isConnected: ((_e2 = b.el) == null ? void 0 : _e2.nodeType) === 3 ? !!b.el.parentNode : (_g2 = (_f2 = b.el) == null ? void 0 : _f2.isConnected) != null ? _g2 : null };
|
|
2024
|
+
}), actionsCount: Array.isArray(r._actions) ? r._actions.length : null, disposed: !!r._disposed };
|
|
2025
|
+
if (JSswift.config.debug) {
|
|
2026
|
+
console.groupCollapsed(`[JSswift.rod.inspect] ${label}`);
|
|
2027
|
+
console.log(info);
|
|
2028
|
+
console.groupEnd();
|
|
2029
|
+
} else {
|
|
2030
|
+
console.log(info);
|
|
2031
|
+
}
|
|
2032
|
+
return info;
|
|
2033
|
+
};
|
|
2034
|
+
JSswift.rod.inspectAll = function() {
|
|
2035
|
+
const all = JSswift.rod._all ? Array.from(JSswift.rod._all) : [];
|
|
2036
|
+
all.forEach((r, i) => JSswift.rod.inspect(r, `rod#${i + 1}`));
|
|
2037
|
+
return all.length;
|
|
2038
|
+
};
|
|
2039
|
+
JSswift._storeShared = /* @__PURE__ */ (() => {
|
|
2040
|
+
function scopeId(scope) {
|
|
2041
|
+
return `${scope.storage}::${scope.prefix}`;
|
|
2042
|
+
}
|
|
2043
|
+
function createScopeTools(config, knownScopes) {
|
|
2044
|
+
function getScope(opts = {}) {
|
|
2045
|
+
var _a2, _b2;
|
|
2046
|
+
const scope = { storage: (_a2 = opts.storage) != null ? _a2 : config.storage, prefix: (_b2 = opts.prefix) != null ? _b2 : config.prefix };
|
|
2047
|
+
knownScopes.set(scopeId(scope), scope);
|
|
2048
|
+
return scope;
|
|
2049
|
+
}
|
|
2050
|
+
function getStorage(scope = null) {
|
|
2051
|
+
var _a2;
|
|
2052
|
+
const mode = typeof scope === "string" ? scope : (_a2 = scope == null ? void 0 : scope.storage) != null ? _a2 : config.storage;
|
|
2053
|
+
return mode === "session" ? window.sessionStorage : window.localStorage;
|
|
2054
|
+
}
|
|
2055
|
+
function fullKey(key, scope = null) {
|
|
2056
|
+
const resolved = scope || getScope();
|
|
2057
|
+
return resolved.prefix + key;
|
|
2058
|
+
}
|
|
2059
|
+
function channelKey(key, scope = null) {
|
|
2060
|
+
const resolved = scope || getScope();
|
|
2061
|
+
return `${scopeId(resolved)}::${key}`;
|
|
2062
|
+
}
|
|
2063
|
+
return { getScope, getStorage, fullKey, channelKey };
|
|
2064
|
+
}
|
|
2065
|
+
function safeParse(str) {
|
|
2066
|
+
try {
|
|
2067
|
+
return JSON.parse(str);
|
|
2068
|
+
} catch (e) {
|
|
2069
|
+
return void 0;
|
|
2070
|
+
}
|
|
2071
|
+
}
|
|
2072
|
+
function safeStringify(value) {
|
|
2073
|
+
try {
|
|
2074
|
+
return JSON.stringify(value);
|
|
2075
|
+
} catch (e) {
|
|
2076
|
+
return void 0;
|
|
2077
|
+
}
|
|
2078
|
+
}
|
|
2079
|
+
function clearScopedMapEntries(scope, ...maps) {
|
|
2080
|
+
const prefix = `${scope.storage}::${scope.prefix}::`;
|
|
2081
|
+
for (const map of maps) {
|
|
2082
|
+
for (const id of Array.from(map.keys())) {
|
|
2083
|
+
if (id.startsWith(prefix)) map.delete(id);
|
|
2084
|
+
}
|
|
2085
|
+
}
|
|
2086
|
+
}
|
|
2087
|
+
return { createScopeTools, safeParse, safeStringify, clearScopedMapEntries };
|
|
2088
|
+
})();
|
|
2089
|
+
JSswift.store = (() => {
|
|
2090
|
+
const config = { prefix: "JSswift:", storage: "local", syncTabs: true, writeDelay: 0 };
|
|
2091
|
+
const mem = /* @__PURE__ */ new Map();
|
|
2092
|
+
const watchers = /* @__PURE__ */ new Map();
|
|
2093
|
+
const pendingWrites = /* @__PURE__ */ new Map();
|
|
2094
|
+
const knownScopes = /* @__PURE__ */ new Map();
|
|
2095
|
+
let writeQueued = false;
|
|
2096
|
+
const { createScopeTools, safeParse, safeStringify, clearScopedMapEntries } = JSswift._storeShared;
|
|
2097
|
+
const { getScope, getStorage, fullKey, channelKey } = createScopeTools(config, knownScopes);
|
|
2098
|
+
function emit(key, value, scope = null) {
|
|
2099
|
+
var _a2;
|
|
2100
|
+
const resolved = scope || getScope();
|
|
2101
|
+
const set2 = watchers.get(channelKey(key, resolved));
|
|
2102
|
+
if (!set2) return;
|
|
2103
|
+
for (const fn of set2) {
|
|
2104
|
+
try {
|
|
2105
|
+
if ((_a2 = JSswift.reactive) == null ? void 0 : _a2.untracked) {
|
|
2106
|
+
JSswift.reactive.untracked(() => fn(value));
|
|
2107
|
+
} else {
|
|
2108
|
+
fn(value);
|
|
2109
|
+
}
|
|
2110
|
+
} catch (e) {
|
|
2111
|
+
console.error("[store] watcher error:", e);
|
|
2112
|
+
}
|
|
2113
|
+
}
|
|
2114
|
+
}
|
|
2115
|
+
function flushWrites() {
|
|
2116
|
+
writeQueued = false;
|
|
2117
|
+
for (const [id, pending] of Array.from(pendingWrites.entries())) {
|
|
2118
|
+
pendingWrites.delete(id);
|
|
2119
|
+
const v = mem.get(id);
|
|
2120
|
+
const s = safeStringify(v);
|
|
2121
|
+
if (s === void 0) continue;
|
|
2122
|
+
const st = getStorage(pending.scope);
|
|
2123
|
+
try {
|
|
2124
|
+
st.setItem(fullKey(pending.key, pending.scope), s);
|
|
2125
|
+
} catch (e) {
|
|
2126
|
+
console.error("[store] write error:", e);
|
|
2127
|
+
}
|
|
2128
|
+
}
|
|
2129
|
+
}
|
|
2130
|
+
function scheduleWrite(key, scope = null) {
|
|
2131
|
+
const resolved = scope || getScope();
|
|
2132
|
+
pendingWrites.set(channelKey(key, resolved), { key, scope: resolved });
|
|
2133
|
+
if (writeQueued) return;
|
|
2134
|
+
writeQueued = true;
|
|
2135
|
+
if (config.writeDelay > 0) {
|
|
2136
|
+
setTimeout(flushWrites, config.writeDelay);
|
|
2137
|
+
} else {
|
|
2138
|
+
queueMicrotask(flushWrites);
|
|
2139
|
+
}
|
|
2140
|
+
}
|
|
2141
|
+
function readFromStorage(key, scope = null) {
|
|
2142
|
+
const resolved = scope || getScope();
|
|
2143
|
+
const st = getStorage(resolved);
|
|
2144
|
+
const raw = st.getItem(fullKey(key, resolved));
|
|
2145
|
+
if (raw == null) return void 0;
|
|
2146
|
+
return safeParse(raw);
|
|
2147
|
+
}
|
|
2148
|
+
function get(key, fallback, scope = null) {
|
|
2149
|
+
const resolved = scope || getScope();
|
|
2150
|
+
const id = channelKey(key, resolved);
|
|
2151
|
+
if (mem.has(id)) return mem.get(id);
|
|
2152
|
+
const v = readFromStorage(key, resolved);
|
|
2153
|
+
if (v !== void 0) {
|
|
2154
|
+
mem.set(id, v);
|
|
2155
|
+
return v;
|
|
2156
|
+
}
|
|
2157
|
+
return fallback;
|
|
2158
|
+
}
|
|
2159
|
+
function set(key, value, scope = null) {
|
|
2160
|
+
const resolved = scope || getScope();
|
|
2161
|
+
mem.set(channelKey(key, resolved), value);
|
|
2162
|
+
scheduleWrite(key, resolved);
|
|
2163
|
+
emit(key, value, resolved);
|
|
2164
|
+
return value;
|
|
2165
|
+
}
|
|
2166
|
+
function remove(key, scope = null) {
|
|
2167
|
+
const resolved = scope || getScope();
|
|
2168
|
+
const id = channelKey(key, resolved);
|
|
2169
|
+
mem.delete(id);
|
|
2170
|
+
pendingWrites.delete(id);
|
|
2171
|
+
try {
|
|
2172
|
+
getStorage(resolved).removeItem(fullKey(key, resolved));
|
|
2173
|
+
} catch (e) {
|
|
2174
|
+
}
|
|
2175
|
+
emit(key, void 0, resolved);
|
|
2176
|
+
}
|
|
2177
|
+
function clear(opts = {}) {
|
|
2178
|
+
const scope = getScope(opts);
|
|
2179
|
+
const st = getStorage(scope);
|
|
2180
|
+
const keys = [];
|
|
2181
|
+
for (let i = 0; i < st.length; i++) {
|
|
2182
|
+
const k = st.key(i);
|
|
2183
|
+
if (k && k.startsWith(scope.prefix)) keys.push(k);
|
|
2184
|
+
}
|
|
2185
|
+
for (const k of keys) st.removeItem(k);
|
|
2186
|
+
clearScopedMapEntries(scope, mem, pendingWrites, watchers);
|
|
2187
|
+
}
|
|
2188
|
+
function watch(key, fn, scope = null) {
|
|
2189
|
+
if (typeof fn !== "function") return () => {
|
|
2190
|
+
};
|
|
2191
|
+
const resolved = scope || getScope();
|
|
2192
|
+
const id = channelKey(key, resolved);
|
|
2193
|
+
if (!watchers.has(id)) watchers.set(id, /* @__PURE__ */ new Set());
|
|
2194
|
+
watchers.get(id).add(fn);
|
|
2195
|
+
return () => {
|
|
2196
|
+
var _a2;
|
|
2197
|
+
return (_a2 = watchers.get(id)) == null ? void 0 : _a2.delete(fn);
|
|
2198
|
+
};
|
|
2199
|
+
}
|
|
2200
|
+
function signal(key, initial, opts = {}) {
|
|
2201
|
+
const scope = getScope({ storage: opts.storage, prefix: opts.prefix });
|
|
2202
|
+
const hydrated = get(key, initial, scope);
|
|
2203
|
+
const [getSig, setSig, disposeSignal] = JSswift.reactive.signal(hydrated);
|
|
2204
|
+
const stopEffect = JSswift.reactive.effect(() => {
|
|
2205
|
+
const v = getSig();
|
|
2206
|
+
set(key, v, scope);
|
|
2207
|
+
});
|
|
2208
|
+
const unwatch = watch(key, (v) => {
|
|
2209
|
+
if (getSig() !== v) setSig(v);
|
|
2210
|
+
}, scope);
|
|
2211
|
+
const dispose = () => {
|
|
2212
|
+
try {
|
|
2213
|
+
stopEffect == null ? void 0 : stopEffect();
|
|
2214
|
+
} catch (e) {
|
|
2215
|
+
}
|
|
2216
|
+
try {
|
|
2217
|
+
unwatch == null ? void 0 : unwatch();
|
|
2218
|
+
} catch (e) {
|
|
2219
|
+
}
|
|
2220
|
+
try {
|
|
2221
|
+
disposeSignal == null ? void 0 : disposeSignal();
|
|
2222
|
+
} catch (e) {
|
|
2223
|
+
}
|
|
2224
|
+
};
|
|
2225
|
+
return [getSig, setSig, dispose];
|
|
2226
|
+
}
|
|
2227
|
+
getScope();
|
|
2228
|
+
if (config.syncTabs) {
|
|
2229
|
+
window.addEventListener("storage", (e) => {
|
|
2230
|
+
if (!e.key || !e.storageArea) return;
|
|
2231
|
+
for (const scope of knownScopes.values()) {
|
|
2232
|
+
if (getStorage(scope) !== e.storageArea) continue;
|
|
2233
|
+
if (!e.key.startsWith(scope.prefix)) continue;
|
|
2234
|
+
const key = e.key.slice(scope.prefix.length);
|
|
2235
|
+
const v = e.newValue == null ? void 0 : safeParse(e.newValue);
|
|
2236
|
+
const id = channelKey(key, scope);
|
|
2237
|
+
if (v === void 0) mem.delete(id);
|
|
2238
|
+
else mem.set(id, v);
|
|
2239
|
+
emit(key, v, scope);
|
|
2240
|
+
}
|
|
2241
|
+
});
|
|
2242
|
+
}
|
|
2243
|
+
function configure(next = {}) {
|
|
2244
|
+
Object.assign(config, next);
|
|
2245
|
+
getScope();
|
|
2246
|
+
}
|
|
2247
|
+
function stats() {
|
|
2248
|
+
return { prefix: config.prefix, storage: config.storage, syncTabs: config.syncTabs, cacheSize: mem.size, scopeCount: knownScopes.size };
|
|
2249
|
+
}
|
|
2250
|
+
return { configure, stats, get, set, remove, clear, watch, signal };
|
|
2251
|
+
})();
|
|
2252
|
+
JSswift.store.computed = function(fn) {
|
|
2253
|
+
if (typeof fn !== "function") throw new Error("store.computed: fn must be a function");
|
|
2254
|
+
return JSswift.reactive.computed(fn);
|
|
2255
|
+
};
|
|
2256
|
+
JSswift.store.migrate = function(targetVersion, steps, opts = {}) {
|
|
2257
|
+
var _a2, _b2;
|
|
2258
|
+
const versionKey = (_a2 = opts.versionKey) != null ? _a2 : "__version";
|
|
2259
|
+
const from = Number((_b2 = JSswift.store.get(versionKey, 0)) != null ? _b2 : 0);
|
|
2260
|
+
const to = Number(targetVersion != null ? targetVersion : 0);
|
|
2261
|
+
if (!Number.isFinite(to) || to < 0) throw new Error("store.migrate: targetVersion non valido");
|
|
2262
|
+
if (from >= to) return { from, to, applied: 0 };
|
|
2263
|
+
let applied = 0;
|
|
2264
|
+
for (let v = from; v < to; v++) {
|
|
2265
|
+
const step = steps == null ? void 0 : steps[v];
|
|
2266
|
+
if (typeof step === "function") {
|
|
2267
|
+
try {
|
|
2268
|
+
step(JSswift.store);
|
|
2269
|
+
} catch (e) {
|
|
2270
|
+
console.error("[store.migrate] step error at version", v, e);
|
|
2271
|
+
}
|
|
2272
|
+
}
|
|
2273
|
+
applied++;
|
|
2274
|
+
JSswift.store.set(versionKey, v + 1);
|
|
2275
|
+
}
|
|
2276
|
+
return { from, to, applied };
|
|
2277
|
+
};
|
|
2278
|
+
JSswift.store.model = function(elOrList, key, initial, opts = {}) {
|
|
2279
|
+
if (typeof key !== "string" || !key) throw new Error("store.model: key deve essere una stringa non vuota");
|
|
2280
|
+
const [get, set, disposeSignal] = JSswift.store.signal(key, initial, { storage: opts.storage, prefix: opts.prefix });
|
|
2281
|
+
const r = JSswift.rodFromSignal(get, set);
|
|
2282
|
+
const unbindModel = JSswift.rodModel(elOrList, r, { event: opts.event, parse: opts.parse, format: opts.format });
|
|
2283
|
+
return () => {
|
|
2284
|
+
var _a2;
|
|
2285
|
+
try {
|
|
2286
|
+
unbindModel == null ? void 0 : unbindModel();
|
|
2287
|
+
} catch (e) {
|
|
2288
|
+
}
|
|
2289
|
+
try {
|
|
2290
|
+
disposeSignal == null ? void 0 : disposeSignal();
|
|
2291
|
+
} catch (e) {
|
|
2292
|
+
}
|
|
2293
|
+
try {
|
|
2294
|
+
(_a2 = r.dispose) == null ? void 0 : _a2.call(r);
|
|
2295
|
+
} catch (e) {
|
|
2296
|
+
}
|
|
2297
|
+
};
|
|
2298
|
+
};
|
|
2299
|
+
JSswift.store.bind = function(selectorOrEl, key, initial, opts = {}) {
|
|
2300
|
+
var _a2;
|
|
2301
|
+
let target = selectorOrEl;
|
|
2302
|
+
if (typeof selectorOrEl === "string") {
|
|
2303
|
+
if (selectorOrEl.includes("[") || selectorOrEl.includes(" ")) {
|
|
2304
|
+
target = document.querySelectorAll(selectorOrEl);
|
|
2305
|
+
} else {
|
|
2306
|
+
target = JSswift.dom.q(selectorOrEl);
|
|
2307
|
+
}
|
|
2308
|
+
}
|
|
2309
|
+
if (!target) {
|
|
2310
|
+
if ((_a2 = JSswift.config) == null ? void 0 : _a2.debug) {
|
|
2311
|
+
console.warn("[store.bind] target non trovato:", selectorOrEl);
|
|
2312
|
+
}
|
|
2313
|
+
return () => {
|
|
2314
|
+
};
|
|
2315
|
+
}
|
|
2316
|
+
return JSswift.store.model(target, key, initial, opts);
|
|
2317
|
+
};
|
|
2318
|
+
JSswift.store.bindAll = function(formEl, schema = {}, opts = {}) {
|
|
2319
|
+
if (typeof formEl === "string") formEl = JSswift.dom.q(formEl);
|
|
2320
|
+
if (!formEl || !formEl.querySelectorAll) {
|
|
2321
|
+
console.warn("[store.bindAll] form non valido:", formEl);
|
|
2322
|
+
return () => {
|
|
2323
|
+
};
|
|
2324
|
+
}
|
|
2325
|
+
const disposers = [];
|
|
2326
|
+
for (const [name, initial] of Object.entries(schema)) {
|
|
2327
|
+
const els = formEl.querySelectorAll(`[name="${name}"]`);
|
|
2328
|
+
if (!els.length) continue;
|
|
2329
|
+
const unbind = JSswift.store.model(els.length === 1 ? els[0] : els, name, initial, opts);
|
|
2330
|
+
disposers.push(unbind);
|
|
2331
|
+
}
|
|
2332
|
+
return () => disposers.forEach((fn) => fn());
|
|
2333
|
+
};
|
|
2334
|
+
JSswift.store.inspect = function() {
|
|
2335
|
+
const info = { ...JSswift.store.stats(), keys: Array.from((function() {
|
|
2336
|
+
const st = JSswift.store.stats().storage === "session" ? sessionStorage : localStorage;
|
|
2337
|
+
const out = [];
|
|
2338
|
+
for (let i = 0; i < st.length; i++) {
|
|
2339
|
+
const k = st.key(i);
|
|
2340
|
+
if (k && k.startsWith(JSswift.store.stats().prefix)) {
|
|
2341
|
+
out.push(k.replace(JSswift.store.stats().prefix, ""));
|
|
2342
|
+
}
|
|
2343
|
+
}
|
|
2344
|
+
return out;
|
|
2345
|
+
})()) };
|
|
2346
|
+
console.groupCollapsed("[JSswift.store.inspect]");
|
|
2347
|
+
console.table(info.keys);
|
|
2348
|
+
console.log(info);
|
|
2349
|
+
console.groupEnd();
|
|
2350
|
+
return info;
|
|
2351
|
+
};
|
|
2352
|
+
JSswift.store.autoForm = function(formEl, schema = {}, opts = {}) {
|
|
2353
|
+
var _a2;
|
|
2354
|
+
const unbind = JSswift.store.bindAll(formEl, schema, opts);
|
|
2355
|
+
if ((_a2 = JSswift.config) == null ? void 0 : _a2.debug) {
|
|
2356
|
+
console.log("[store.autoForm] autosave attivo:", schema);
|
|
2357
|
+
}
|
|
2358
|
+
return unbind;
|
|
2359
|
+
};
|
|
2360
|
+
JSswift.useStore = function(key, initial, ctx, opts = {}) {
|
|
2361
|
+
const [get, set, dispose] = JSswift.store.signal(key, initial, opts);
|
|
2362
|
+
if (ctx && typeof ctx.onDispose === "function" && typeof dispose === "function") {
|
|
2363
|
+
ctx.onDispose(dispose);
|
|
2364
|
+
}
|
|
2365
|
+
return [get, set];
|
|
2366
|
+
};
|
|
2367
|
+
JSswift._plugins = /* @__PURE__ */ new Set();
|
|
2368
|
+
JSswift.usePlugin = function(plugin, options) {
|
|
2369
|
+
var _a2;
|
|
2370
|
+
if (!plugin) return;
|
|
2371
|
+
if (JSswift._plugins.has(plugin)) {
|
|
2372
|
+
if ((_a2 = JSswift.config) == null ? void 0 : _a2.debug) {
|
|
2373
|
+
console.warn("[JSswift.usePlugin] plugin gi\xE0 installato:", plugin.name || plugin);
|
|
2374
|
+
}
|
|
2375
|
+
return;
|
|
2376
|
+
}
|
|
2377
|
+
if (typeof plugin === "function") {
|
|
2378
|
+
plugin(JSswift, options);
|
|
2379
|
+
JSswift._plugins.add(plugin);
|
|
2380
|
+
return;
|
|
2381
|
+
}
|
|
2382
|
+
if (plugin && typeof plugin.install === "function") {
|
|
2383
|
+
plugin.install(JSswift, options);
|
|
2384
|
+
JSswift._plugins.add(plugin);
|
|
2385
|
+
return;
|
|
2386
|
+
}
|
|
2387
|
+
console.warn("[JSswift.usePlugin] plugin non valido:", plugin);
|
|
2388
|
+
};
|
|
2389
|
+
JSswift.plugins = JSswift.plugins || {};
|
|
2390
|
+
JSswift.plugins.debug = { install(app) {
|
|
2391
|
+
app.config.debug = true;
|
|
2392
|
+
console.log("[JSswift] Debug mode ON");
|
|
2393
|
+
} };
|
|
2394
|
+
JSswift.plugins.forms = { install(app) {
|
|
2395
|
+
const forms = {};
|
|
2396
|
+
app.forms = { validate(formEl, rules = {}, opts = {}) {
|
|
2397
|
+
var _a2, _b2;
|
|
2398
|
+
if (typeof formEl === "string") formEl = app.dom.q(formEl);
|
|
2399
|
+
if (!formEl || !formEl.querySelectorAll) {
|
|
2400
|
+
console.warn("[forms.validate] form non valido:", formEl);
|
|
2401
|
+
return null;
|
|
2402
|
+
}
|
|
2403
|
+
const options = { mode: opts.mode || "input", showMessages: (_a2 = opts.showMessages) != null ? _a2 : true, submitGuard: (_b2 = opts.submitGuard) != null ? _b2 : true, errorClass: opts.errorClass || "is-invalid", validClass: opts.validClass || "is-valid", messageClass: opts.messageClass || "form-error" };
|
|
2404
|
+
const fieldState = {};
|
|
2405
|
+
for (const [name, rule] of Object.entries(rules)) {
|
|
2406
|
+
const els = formEl.querySelectorAll(`[name="${name}"]`);
|
|
2407
|
+
if (!els.length) continue;
|
|
2408
|
+
const [getErr, setErr] = app.reactive.signal(null);
|
|
2409
|
+
fieldState[name] = { getErr, setErr, els };
|
|
2410
|
+
let msgNode = null;
|
|
2411
|
+
if (options.showMessages) {
|
|
2412
|
+
msgNode = document.createElement("div");
|
|
2413
|
+
msgNode.className = options.messageClass;
|
|
2414
|
+
els[els.length - 1].after(msgNode);
|
|
2415
|
+
}
|
|
2416
|
+
const validateValue = () => {
|
|
2417
|
+
let value;
|
|
2418
|
+
const el = els[0];
|
|
2419
|
+
if (el.type === "checkbox") value = el.checked;
|
|
2420
|
+
else if (el.type === "radio") {
|
|
2421
|
+
const c = Array.from(els).find((r) => r.checked);
|
|
2422
|
+
value = c ? c.value : null;
|
|
2423
|
+
} else value = el.value;
|
|
2424
|
+
let res = true;
|
|
2425
|
+
try {
|
|
2426
|
+
res = rule(value);
|
|
2427
|
+
} catch (e) {
|
|
2428
|
+
res = e.message || "Errore di validazione";
|
|
2429
|
+
}
|
|
2430
|
+
const error = res === true ? null : typeof res === "string" ? res : "Valore non valido";
|
|
2431
|
+
setErr(error);
|
|
2432
|
+
return !error;
|
|
2433
|
+
};
|
|
2434
|
+
for (const el of els) {
|
|
2435
|
+
el.addEventListener(options.mode, validateValue);
|
|
2436
|
+
}
|
|
2437
|
+
app.reactive.effect(() => {
|
|
2438
|
+
const err = getErr();
|
|
2439
|
+
for (const el of els) {
|
|
2440
|
+
el.classList.toggle(options.errorClass, !!err);
|
|
2441
|
+
el.classList.toggle(options.validClass, !err);
|
|
2442
|
+
}
|
|
2443
|
+
if (msgNode) {
|
|
2444
|
+
msgNode.textContent = err || "";
|
|
2445
|
+
msgNode.style.display = err ? "block" : "none";
|
|
2446
|
+
}
|
|
2447
|
+
});
|
|
2448
|
+
validateValue();
|
|
2449
|
+
}
|
|
2450
|
+
const isValid = app.store.computed(() => {
|
|
2451
|
+
return Object.values(fieldState).every((f) => !f.getErr());
|
|
2452
|
+
});
|
|
2453
|
+
const onSubmit = (e) => {
|
|
2454
|
+
let ok = true;
|
|
2455
|
+
for (const f of Object.values(fieldState)) {
|
|
2456
|
+
const el = f.els[0];
|
|
2457
|
+
if (el) el.dispatchEvent(new Event(options.mode, { bubbles: true }));
|
|
2458
|
+
if (f.getErr()) ok = false;
|
|
2459
|
+
}
|
|
2460
|
+
if (!ok && options.submitGuard) {
|
|
2461
|
+
e.preventDefault();
|
|
2462
|
+
e.stopPropagation();
|
|
2463
|
+
}
|
|
2464
|
+
};
|
|
2465
|
+
if (options.submitGuard) {
|
|
2466
|
+
formEl.addEventListener("submit", onSubmit);
|
|
2467
|
+
}
|
|
2468
|
+
const dispose = () => {
|
|
2469
|
+
if (options.submitGuard) {
|
|
2470
|
+
formEl.removeEventListener("submit", onSubmit);
|
|
2471
|
+
}
|
|
2472
|
+
};
|
|
2473
|
+
forms[formEl] = { isValid, dispose };
|
|
2474
|
+
return { isValid, dispose };
|
|
2475
|
+
} };
|
|
2476
|
+
} };
|
|
2477
|
+
JSswift._authShared = /* @__PURE__ */ (() => {
|
|
2478
|
+
function createPermissionApi(getUser) {
|
|
2479
|
+
const roles = () => {
|
|
2480
|
+
var _a2;
|
|
2481
|
+
return ((_a2 = getUser()) == null ? void 0 : _a2.roles) || [];
|
|
2482
|
+
};
|
|
2483
|
+
const perms = () => {
|
|
2484
|
+
var _a2;
|
|
2485
|
+
return ((_a2 = getUser()) == null ? void 0 : _a2.permissions) || [];
|
|
2486
|
+
};
|
|
2487
|
+
return { roles, perms, hasRole: (role) => roles().includes(role), can: (permission) => perms().includes(permission), canAny: (list) => list.some((item) => roles().includes(item) || perms().includes(item)), canAll: (list) => list.every((item) => roles().includes(item) || perms().includes(item)) };
|
|
2488
|
+
}
|
|
2489
|
+
function matchesProtectedPath(path, protectedList) {
|
|
2490
|
+
return protectedList.some((entry) => typeof entry === "string" ? path.startsWith(entry) : entry instanceof RegExp ? entry.test(path) : false);
|
|
2491
|
+
}
|
|
2492
|
+
function attachDevTools(app, auth) {
|
|
2493
|
+
if (!app || !auth) return;
|
|
2494
|
+
let tracing = false;
|
|
2495
|
+
function safeNow() {
|
|
2496
|
+
return Date.now();
|
|
2497
|
+
}
|
|
2498
|
+
function decodeJWT(token) {
|
|
2499
|
+
if (!token || typeof token !== "string") return null;
|
|
2500
|
+
const parts = token.split(".");
|
|
2501
|
+
if (parts.length < 2) return null;
|
|
2502
|
+
try {
|
|
2503
|
+
const b64 = parts[1].replace(/-/g, "+").replace(/_/g, "/");
|
|
2504
|
+
const json = decodeURIComponent(atob(b64).split("").map((char) => "%" + char.charCodeAt(0).toString(16).padStart(2, "0")).join(""));
|
|
2505
|
+
return JSON.parse(json);
|
|
2506
|
+
} catch (e) {
|
|
2507
|
+
return null;
|
|
2508
|
+
}
|
|
2509
|
+
}
|
|
2510
|
+
function getAuthState() {
|
|
2511
|
+
var _a2, _b2, _c2, _d2;
|
|
2512
|
+
if (typeof auth._getState === "function") {
|
|
2513
|
+
const state = auth._getState();
|
|
2514
|
+
return state && typeof state === "object" ? state : { user: (_b2 = (_a2 = auth.user) == null ? void 0 : _a2.call(auth)) != null ? _b2 : null };
|
|
2515
|
+
}
|
|
2516
|
+
return { user: (_d2 = (_c2 = auth.user) == null ? void 0 : _c2.call(auth)) != null ? _d2 : null };
|
|
2517
|
+
}
|
|
2518
|
+
function status() {
|
|
2519
|
+
var _a2, _b2, _c2;
|
|
2520
|
+
const state = getAuthState() || {};
|
|
2521
|
+
const user = (_b2 = state.user) != null ? _b2 : (_a2 = auth.user) == null ? void 0 : _a2.call(auth);
|
|
2522
|
+
const name = (user == null ? void 0 : user.name) || (user == null ? void 0 : user.email) || (user == null ? void 0 : user.id) || "anon";
|
|
2523
|
+
const ok = !!((_c2 = auth.isAuth) == null ? void 0 : _c2.call(auth));
|
|
2524
|
+
const expiresAt = state.expiresAt || null;
|
|
2525
|
+
const left = expiresAt ? expiresAt - safeNow() : null;
|
|
2526
|
+
return ok ? `AUTH \u2705 user=${name}${expiresAt ? ` expiresIn=${Math.round(left / 1e3)}s` : ""}` : `AUTH \u274C user=${name}`;
|
|
2527
|
+
}
|
|
2528
|
+
function inspect(label = "auth") {
|
|
2529
|
+
var _a2, _b2, _c2;
|
|
2530
|
+
const state = getAuthState() || {};
|
|
2531
|
+
const user = (_b2 = state.user) != null ? _b2 : (_a2 = auth.user) == null ? void 0 : _a2.call(auth);
|
|
2532
|
+
const roles = (user == null ? void 0 : user.roles) || [];
|
|
2533
|
+
const perms = (user == null ? void 0 : user.permissions) || [];
|
|
2534
|
+
const accessToken = state.accessToken || null;
|
|
2535
|
+
const refreshToken = state.refreshToken || null;
|
|
2536
|
+
const jwt = accessToken ? decodeJWT(accessToken) : null;
|
|
2537
|
+
const expiresAt = state.expiresAt || ((jwt == null ? void 0 : jwt.exp) ? jwt.exp * 1e3 : null);
|
|
2538
|
+
const now2 = safeNow();
|
|
2539
|
+
const expiresInMs = expiresAt ? expiresAt - now2 : null;
|
|
2540
|
+
const info = { label, isAuth: !!((_c2 = auth.isAuth) == null ? void 0 : _c2.call(auth)), user, roles, permissions: perms, has: { role: (role) => {
|
|
2541
|
+
var _a3;
|
|
2542
|
+
return !!((_a3 = auth.hasRole) == null ? void 0 : _a3.call(auth, role));
|
|
2543
|
+
}, can: (permission) => {
|
|
2544
|
+
var _a3;
|
|
2545
|
+
return !!((_a3 = auth.can) == null ? void 0 : _a3.call(auth, permission));
|
|
2546
|
+
} }, token: { hasAccess: !!accessToken, hasRefresh: !!refreshToken, expiresAt, expiresInSec: expiresInMs == null ? null : Math.round(expiresInMs / 1e3), decodedJWT: jwt } };
|
|
2547
|
+
console.groupCollapsed(`[JSswift.auth.inspect] ${label}`);
|
|
2548
|
+
console.log("status:", status());
|
|
2549
|
+
console.log(info);
|
|
2550
|
+
if (expiresInMs != null) {
|
|
2551
|
+
if (expiresInMs <= 0) console.warn("\u26A0\uFE0F Access token scaduto.");
|
|
2552
|
+
else if (expiresInMs < 6e4) console.warn("\u26A0\uFE0F Access token in scadenza (<60s).");
|
|
2553
|
+
}
|
|
2554
|
+
console.groupEnd();
|
|
2555
|
+
return info;
|
|
2556
|
+
}
|
|
2557
|
+
function trace(on = true) {
|
|
2558
|
+
tracing = !!on;
|
|
2559
|
+
console.log("[JSswift.auth.trace]", tracing ? "ON" : "OFF");
|
|
2560
|
+
}
|
|
2561
|
+
if (typeof auth.fetch === "function" && !auth._fetchWrapped) {
|
|
2562
|
+
const originalFetch = auth.fetch.bind(auth);
|
|
2563
|
+
auth.fetch = async (...args) => {
|
|
2564
|
+
const res = await originalFetch(...args);
|
|
2565
|
+
if (tracing) {
|
|
2566
|
+
try {
|
|
2567
|
+
console.log("[auth.fetch]", res.status, args[0]);
|
|
2568
|
+
} catch (e) {
|
|
2569
|
+
}
|
|
2570
|
+
}
|
|
2571
|
+
return res;
|
|
2572
|
+
};
|
|
2573
|
+
auth._fetchWrapped = true;
|
|
2574
|
+
}
|
|
2575
|
+
auth.decodeJWT = decodeJWT;
|
|
2576
|
+
auth.status = status;
|
|
2577
|
+
auth.inspect = inspect;
|
|
2578
|
+
auth.trace = trace;
|
|
2579
|
+
}
|
|
2580
|
+
return { createPermissionApi, matchesProtectedPath, attachDevTools };
|
|
2581
|
+
})();
|
|
2582
|
+
JSswift.plugins.auth = { install(app, opts = {}) {
|
|
2583
|
+
var _a2;
|
|
2584
|
+
const { createPermissionApi, matchesProtectedPath, attachDevTools } = JSswift._authShared;
|
|
2585
|
+
const options = { key: opts.key || "auth", loginRoute: opts.loginRoute || "/login", protected: opts.protected || [], autoRedirect: (_a2 = opts.autoRedirect) != null ? _a2 : true, endpoints: opts.endpoints || { login: "/api/login", refresh: "/api/refresh", logout: "/api/logout" }, fetchOptions: opts.fetchOptions || {}, tokenHeader: opts.tokenHeader || "Authorization", tokenPrefix: opts.tokenPrefix || "Bearer ", refreshMargin: opts.refreshMargin || 3e4 };
|
|
2586
|
+
const [getAuth, setAuth] = app.store.signal(options.key, null);
|
|
2587
|
+
const getUser = () => {
|
|
2588
|
+
var _a3;
|
|
2589
|
+
return ((_a3 = getAuth()) == null ? void 0 : _a3.user) || null;
|
|
2590
|
+
};
|
|
2591
|
+
const isAuth = () => {
|
|
2592
|
+
var _a3;
|
|
2593
|
+
return !!((_a3 = getAuth()) == null ? void 0 : _a3.accessToken);
|
|
2594
|
+
};
|
|
2595
|
+
const { hasRole, can, canAny, canAll } = createPermissionApi(getUser);
|
|
2596
|
+
let refreshing = false;
|
|
2597
|
+
let refreshQueue = [];
|
|
2598
|
+
function needsRefresh() {
|
|
2599
|
+
const a = getAuth();
|
|
2600
|
+
if (!(a == null ? void 0 : a.expiresAt)) return false;
|
|
2601
|
+
return Date.now() > a.expiresAt - options.refreshMargin;
|
|
2602
|
+
}
|
|
2603
|
+
async function doRefresh() {
|
|
2604
|
+
var _a3;
|
|
2605
|
+
if (refreshing) {
|
|
2606
|
+
return new Promise((res, rej) => refreshQueue.push({ res, rej }));
|
|
2607
|
+
}
|
|
2608
|
+
refreshing = true;
|
|
2609
|
+
try {
|
|
2610
|
+
const r = await fetch(options.endpoints.refresh, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ refreshToken: (_a3 = getAuth()) == null ? void 0 : _a3.refreshToken }) });
|
|
2611
|
+
if (!r.ok) throw new Error("Refresh failed");
|
|
2612
|
+
const data = await r.json();
|
|
2613
|
+
setAuth({ ...getAuth(), ...data });
|
|
2614
|
+
refreshQueue.forEach((q) => q.res(true));
|
|
2615
|
+
refreshQueue = [];
|
|
2616
|
+
return true;
|
|
2617
|
+
} catch (e) {
|
|
2618
|
+
refreshQueue.forEach((q) => q.rej(e));
|
|
2619
|
+
refreshQueue = [];
|
|
2620
|
+
setAuth(null);
|
|
2621
|
+
if (options.autoRedirect) app.router.navigate(options.loginRoute);
|
|
2622
|
+
throw e;
|
|
2623
|
+
} finally {
|
|
2624
|
+
refreshing = false;
|
|
2625
|
+
}
|
|
2626
|
+
}
|
|
2627
|
+
async function loginAsync(credentials) {
|
|
2628
|
+
const r = await fetch(options.endpoints.login, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(credentials) });
|
|
2629
|
+
if (!r.ok) throw new Error("Login failed");
|
|
2630
|
+
const data = await r.json();
|
|
2631
|
+
setAuth(data);
|
|
2632
|
+
return data;
|
|
2633
|
+
}
|
|
2634
|
+
async function logoutAsync() {
|
|
2635
|
+
try {
|
|
2636
|
+
await fetch(options.endpoints.logout, { method: "POST" });
|
|
2637
|
+
} catch (e) {
|
|
2638
|
+
}
|
|
2639
|
+
setAuth(null);
|
|
2640
|
+
if (options.autoRedirect) app.router.navigate(options.loginRoute);
|
|
2641
|
+
}
|
|
2642
|
+
async function authFetch(url, opts2 = {}) {
|
|
2643
|
+
var _a3;
|
|
2644
|
+
if (needsRefresh()) await doRefresh();
|
|
2645
|
+
const a = getAuth();
|
|
2646
|
+
const headers = new Headers(opts2.headers || {});
|
|
2647
|
+
if (a == null ? void 0 : a.accessToken) {
|
|
2648
|
+
headers.set(options.tokenHeader, options.tokenPrefix + a.accessToken);
|
|
2649
|
+
}
|
|
2650
|
+
const r = await fetch(url, { ...options.fetchOptions, ...opts2, headers });
|
|
2651
|
+
if (r.status === 401 && ((_a3 = getAuth()) == null ? void 0 : _a3.refreshToken) && !opts2._authRetried) {
|
|
2652
|
+
await doRefresh();
|
|
2653
|
+
return authFetch(url, { ...opts2, _authRetried: true });
|
|
2654
|
+
}
|
|
2655
|
+
return r;
|
|
2656
|
+
}
|
|
2657
|
+
app.router.beforeEach((ctx) => {
|
|
2658
|
+
const path = ctx.path;
|
|
2659
|
+
const protectedMatch = matchesProtectedPath(path, options.protected);
|
|
2660
|
+
if (protectedMatch && !isAuth()) {
|
|
2661
|
+
return options.loginRoute;
|
|
2662
|
+
}
|
|
2663
|
+
return true;
|
|
2664
|
+
});
|
|
2665
|
+
app.auth = { user: getUser, isAuth, hasRole, can, canAny, canAll, loginAsync, logoutAsync, fetch: authFetch, _getState: getAuth };
|
|
2666
|
+
app.useAuth = function(ctx) {
|
|
2667
|
+
if (ctx && typeof ctx.onDispose === "function") {
|
|
2668
|
+
}
|
|
2669
|
+
return app.auth;
|
|
2670
|
+
};
|
|
2671
|
+
attachDevTools(JSswift, app.auth);
|
|
2672
|
+
} };
|
|
2673
|
+
JSswift._httpShared = /* @__PURE__ */ (() => {
|
|
2674
|
+
function createReactiveState(JSswift2) {
|
|
2675
|
+
var _a2;
|
|
2676
|
+
const reactive = JSswift2.reactive;
|
|
2677
|
+
const [getInFlight, setInFlight] = reactive.signal(0);
|
|
2678
|
+
const [getStatus, setStatus] = reactive.signal("idle");
|
|
2679
|
+
const [getLastRequest, setLastRequest] = reactive.signal(null);
|
|
2680
|
+
const [getLastResponse, setLastResponse] = reactive.signal(null);
|
|
2681
|
+
const [getLastError, setLastError] = reactive.signal(null);
|
|
2682
|
+
const [getLastDuration, setLastDuration] = reactive.signal(0);
|
|
2683
|
+
const [getLastUpdated, setLastUpdated] = reactive.signal(0);
|
|
2684
|
+
let lastStartId = 0;
|
|
2685
|
+
function markStart(req) {
|
|
2686
|
+
const id = ++lastStartId;
|
|
2687
|
+
const ts = Date.now();
|
|
2688
|
+
setInFlight(getInFlight() + 1);
|
|
2689
|
+
setStatus("pending");
|
|
2690
|
+
setLastError(null);
|
|
2691
|
+
setLastResponse(null);
|
|
2692
|
+
setLastRequest({ id, url: req.url, method: req.method, meta: req.meta, timeout: req.timeout, retry: req.retry, startedAt: ts });
|
|
2693
|
+
setLastUpdated(ts);
|
|
2694
|
+
return id;
|
|
2695
|
+
}
|
|
2696
|
+
function markEnd(id, res, err, durationMs) {
|
|
2697
|
+
const ts = Date.now();
|
|
2698
|
+
setInFlight(Math.max(0, getInFlight() - 1));
|
|
2699
|
+
if (id === lastStartId) {
|
|
2700
|
+
if (err) {
|
|
2701
|
+
setStatus("error");
|
|
2702
|
+
setLastError(err);
|
|
2703
|
+
setLastResponse(null);
|
|
2704
|
+
} else if (res) {
|
|
2705
|
+
setStatus("success");
|
|
2706
|
+
setLastError(null);
|
|
2707
|
+
setLastResponse({ id, status: res.status, ok: res.ok, headers: res.headers, url: res.url, receivedAt: ts, raw: res });
|
|
2708
|
+
} else {
|
|
2709
|
+
setStatus("idle");
|
|
2710
|
+
setLastError(null);
|
|
2711
|
+
setLastResponse(null);
|
|
2712
|
+
}
|
|
2713
|
+
setLastDuration(Math.max(0, Number(durationMs || 0)));
|
|
2714
|
+
}
|
|
2715
|
+
setLastUpdated(ts);
|
|
2716
|
+
}
|
|
2717
|
+
const state = { inFlight: getInFlight, status: getStatus, isLoading: ((_a2 = JSswift2.store) == null ? void 0 : _a2.computed) ? JSswift2.store.computed(() => getInFlight() > 0) : () => getInFlight() > 0, lastRequest: getLastRequest, lastResponse: getLastResponse, lastError: getLastError, lastDuration: getLastDuration, lastUpdated: getLastUpdated, reset() {
|
|
2718
|
+
setInFlight(0);
|
|
2719
|
+
setStatus("idle");
|
|
2720
|
+
setLastRequest(null);
|
|
2721
|
+
setLastResponse(null);
|
|
2722
|
+
setLastError(null);
|
|
2723
|
+
setLastDuration(0);
|
|
2724
|
+
setLastUpdated(Date.now());
|
|
2725
|
+
} };
|
|
2726
|
+
return { state, markStart, markEnd };
|
|
2727
|
+
}
|
|
2728
|
+
function joinURL(base, path) {
|
|
2729
|
+
if (!base) return path;
|
|
2730
|
+
if (/^https?:\/\//i.test(path)) return path;
|
|
2731
|
+
const normalizedBase = base.endsWith("/") ? base.slice(0, -1) : base;
|
|
2732
|
+
const normalizedPath = path.startsWith("/") ? path : "/" + path;
|
|
2733
|
+
return normalizedBase + normalizedPath;
|
|
2734
|
+
}
|
|
2735
|
+
function sleep2(ms) {
|
|
2736
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
2737
|
+
}
|
|
2738
|
+
function isRetryable2(err, res) {
|
|
2739
|
+
if (err) return true;
|
|
2740
|
+
if (!res) return false;
|
|
2741
|
+
return res.status === 429 || res.status >= 500 && res.status <= 599;
|
|
2742
|
+
}
|
|
2743
|
+
function makeAbort2(timeoutMs, externalSignal) {
|
|
2744
|
+
if (!timeoutMs && !externalSignal) return { signal: void 0, cancel: () => {
|
|
2745
|
+
} };
|
|
2746
|
+
const controller = new AbortController();
|
|
2747
|
+
const signal = controller.signal;
|
|
2748
|
+
let timeoutId = null;
|
|
2749
|
+
if (timeoutMs > 0) {
|
|
2750
|
+
timeoutId = setTimeout(() => controller.abort(new Error("timeout")), timeoutMs);
|
|
2751
|
+
}
|
|
2752
|
+
const onAbort = () => controller.abort(externalSignal.reason || new Error("aborted"));
|
|
2753
|
+
if (externalSignal) {
|
|
2754
|
+
if (externalSignal.aborted) onAbort();
|
|
2755
|
+
else externalSignal.addEventListener("abort", onAbort, { once: true });
|
|
2756
|
+
}
|
|
2757
|
+
const cancel = () => {
|
|
2758
|
+
var _a2;
|
|
2759
|
+
if (timeoutId) clearTimeout(timeoutId);
|
|
2760
|
+
if (externalSignal) (_a2 = externalSignal.removeEventListener) == null ? void 0 : _a2.call(externalSignal, "abort", onAbort);
|
|
2761
|
+
};
|
|
2762
|
+
return { signal, cancel };
|
|
2763
|
+
}
|
|
2764
|
+
function normalizeRequest2(configHTTP2, input, init = {}) {
|
|
2765
|
+
var _a2, _b2, _c2;
|
|
2766
|
+
const url = typeof input === "string" ? joinURL(configHTTP2.baseURL, input) : input;
|
|
2767
|
+
const method = (init.method || "GET").toUpperCase();
|
|
2768
|
+
const headers = new Headers(configHTTP2.headers);
|
|
2769
|
+
if (init.headers) new Headers(init.headers).forEach((value, key) => headers.set(key, value));
|
|
2770
|
+
return { url, method, headers, body: init.body, credentials: (_a2 = init.credentials) != null ? _a2 : configHTTP2.credentials, signal: init.signal, timeout: (_b2 = init.timeout) != null ? _b2 : configHTTP2.timeout, retry: (_c2 = init.retry) != null ? _c2 : configHTTP2.retry, meta: init.meta || {} };
|
|
2771
|
+
}
|
|
2772
|
+
async function runHooks2(set, ...args) {
|
|
2773
|
+
var _a2;
|
|
2774
|
+
let current = args[0];
|
|
2775
|
+
for (const fn of set) {
|
|
2776
|
+
current = (_a2 = await fn(current, ...args.slice(1))) != null ? _a2 : current;
|
|
2777
|
+
}
|
|
2778
|
+
return current;
|
|
2779
|
+
}
|
|
2780
|
+
function wrapResponse2(res, req) {
|
|
2781
|
+
return { ok: res.ok, status: res.status, headers: res.headers, raw: res, req, async json() {
|
|
2782
|
+
let data = null;
|
|
2783
|
+
let error = null;
|
|
2784
|
+
try {
|
|
2785
|
+
data = await res.json();
|
|
2786
|
+
} catch (e) {
|
|
2787
|
+
data = null;
|
|
2788
|
+
}
|
|
2789
|
+
if (!res.ok) error = data != null ? data : { status: res.status };
|
|
2790
|
+
return { data, error, ok: res.ok, status: res.status };
|
|
2791
|
+
}, async jsonStrict() {
|
|
2792
|
+
const { data, error } = await this.json();
|
|
2793
|
+
if (error) {
|
|
2794
|
+
const e = new Error(`HTTP error ${res.status} ${(req == null ? void 0 : req.method) || ""} ${(req == null ? void 0 : req.url) || ""}`.trim());
|
|
2795
|
+
e.status = res.status;
|
|
2796
|
+
e.data = data;
|
|
2797
|
+
e.req = { url: req == null ? void 0 : req.url, method: req == null ? void 0 : req.method, meta: req == null ? void 0 : req.meta };
|
|
2798
|
+
throw e;
|
|
2799
|
+
}
|
|
2800
|
+
return { data, ok: true, status: res.status };
|
|
2801
|
+
}, async text() {
|
|
2802
|
+
let text = "";
|
|
2803
|
+
try {
|
|
2804
|
+
text = await res.text();
|
|
2805
|
+
} catch (e) {
|
|
2806
|
+
}
|
|
2807
|
+
return { text, ok: res.ok, status: res.status };
|
|
2808
|
+
}, async textStrict() {
|
|
2809
|
+
const out = await this.text();
|
|
2810
|
+
if (!out.ok) {
|
|
2811
|
+
const e = new Error("HTTP error " + res.status);
|
|
2812
|
+
e.status = res.status;
|
|
2813
|
+
e.text = out.text;
|
|
2814
|
+
throw e;
|
|
2815
|
+
}
|
|
2816
|
+
return out;
|
|
2817
|
+
} };
|
|
2818
|
+
}
|
|
2819
|
+
function withJSON2(request2, method, url, body, init = {}) {
|
|
2820
|
+
const headers = new Headers(init.headers || {});
|
|
2821
|
+
if (!headers.has("Content-Type")) headers.set("Content-Type", "application/json");
|
|
2822
|
+
return request2(url, { ...init, method, headers, body: body == null ? void 0 : JSON.stringify(body) });
|
|
2823
|
+
}
|
|
2824
|
+
return { createReactiveState, joinURL, sleep: sleep2, isRetryable: isRetryable2, makeAbort: makeAbort2, normalizeRequest: normalizeRequest2, runHooks: runHooks2, wrapResponse: wrapResponse2, withJSON: withJSON2 };
|
|
2825
|
+
})();
|
|
2826
|
+
const { createReactiveState: createHttpReactiveState, sleep, isRetryable, makeAbort, normalizeRequest, runHooks, wrapResponse, withJSON } = JSswift._httpShared;
|
|
2827
|
+
const JSswiftHttpSetting = typeof globalThis !== "undefined" && globalThis.JSswift_setting ? globalThis.JSswift_setting : {};
|
|
2828
|
+
const configHTTP = { baseURL: ((_b = JSswiftHttpSetting.http) == null ? void 0 : _b.baseURL) || "", timeout: (_d = (_c = JSswiftHttpSetting.http) == null ? void 0 : _c.timeout) != null ? _d : 0, retry: ((_e = JSswiftHttpSetting.http) == null ? void 0 : _e.retry) || { attempts: 0, delay: 250, factor: 2 }, headers: ((_f = JSswiftHttpSetting.http) == null ? void 0 : _f.headers) || {}, credentials: (_g = JSswiftHttpSetting.http) == null ? void 0 : _g.credentials, debug: (_h2 = JSswiftHttpSetting.debug) != null ? _h2 : false };
|
|
2829
|
+
const httpState = createHttpReactiveState(JSswift);
|
|
2830
|
+
const now = () => typeof performance !== "undefined" && performance.now ? performance.now() : Date.now();
|
|
2831
|
+
const hooksHTTP = { beforeRequest: /* @__PURE__ */ new Set(), afterResponse: /* @__PURE__ */ new Set(), onError: /* @__PURE__ */ new Set() };
|
|
2832
|
+
async function coreFetch(req) {
|
|
2833
|
+
const f = JSswift.auth && typeof JSswift.auth.fetch === "function" ? JSswift.auth.fetch.bind(JSswift.auth) : fetch;
|
|
2834
|
+
const init = { method: req.method, headers: req.headers, body: req.body, credentials: req.credentials, signal: req.signal };
|
|
2835
|
+
return f(req.url, init);
|
|
2836
|
+
}
|
|
2837
|
+
async function request(input, init = {}) {
|
|
2838
|
+
var _a2, _b2, _c2;
|
|
2839
|
+
let req = normalizeRequest(configHTTP, input, init);
|
|
2840
|
+
req = await runHooks(hooksHTTP.beforeRequest, req);
|
|
2841
|
+
const requestId = httpState.markStart(req);
|
|
2842
|
+
const startAt = now();
|
|
2843
|
+
const retryCfg = req.retry || { attempts: 0, delay: 250, factor: 2 };
|
|
2844
|
+
const maxAttempts = Math.max(0, Number(retryCfg.attempts || 0));
|
|
2845
|
+
let delay = Math.max(0, Number(retryCfg.delay || 0));
|
|
2846
|
+
const factor = Math.max(1, Number(retryCfg.factor || 2));
|
|
2847
|
+
let lastErr = null;
|
|
2848
|
+
let lastRes = null;
|
|
2849
|
+
let finalRes = null;
|
|
2850
|
+
let finalErr = null;
|
|
2851
|
+
try {
|
|
2852
|
+
for (let attempt = 0; attempt <= maxAttempts; attempt++) {
|
|
2853
|
+
lastErr = null;
|
|
2854
|
+
lastRes = null;
|
|
2855
|
+
const { signal, cancel } = makeAbort(req.timeout, init.signal);
|
|
2856
|
+
const effectiveReq = { ...req, signal };
|
|
2857
|
+
const attemptAt = now();
|
|
2858
|
+
try {
|
|
2859
|
+
if (configHTTP.debug) console.log("[http.request]", effectiveReq.method, effectiveReq.url);
|
|
2860
|
+
(_a2 = JSswift.perf) == null ? void 0 : _a2.inc("httpRequests");
|
|
2861
|
+
(_b2 = JSswift.perf) == null ? void 0 : _b2.mark("http:req", { url: req.url, method: req.method });
|
|
2862
|
+
const res = await coreFetch(effectiveReq);
|
|
2863
|
+
lastRes = res;
|
|
2864
|
+
const outRes = await runHooks(hooksHTTP.afterResponse, res, effectiveReq);
|
|
2865
|
+
const dt = now() - attemptAt;
|
|
2866
|
+
(_c2 = JSswift.perf) == null ? void 0 : _c2.tick("http:res", dt, { status: res.status, url: req.url });
|
|
2867
|
+
if (attempt < maxAttempts && isRetryable(null, outRes)) {
|
|
2868
|
+
cancel();
|
|
2869
|
+
await sleep(delay);
|
|
2870
|
+
delay = Math.round(delay * factor);
|
|
2871
|
+
continue;
|
|
2872
|
+
}
|
|
2873
|
+
cancel();
|
|
2874
|
+
finalRes = outRes;
|
|
2875
|
+
return wrapResponse(outRes, effectiveReq);
|
|
2876
|
+
} catch (err) {
|
|
2877
|
+
lastErr = err;
|
|
2878
|
+
cancel();
|
|
2879
|
+
for (const fn of hooksHTTP.onError) {
|
|
2880
|
+
try {
|
|
2881
|
+
fn(err, effectiveReq);
|
|
2882
|
+
} catch (e) {
|
|
2883
|
+
}
|
|
2884
|
+
}
|
|
2885
|
+
if (attempt < maxAttempts && isRetryable(err, null)) {
|
|
2886
|
+
await sleep(delay);
|
|
2887
|
+
delay = Math.round(delay * factor);
|
|
2888
|
+
continue;
|
|
2889
|
+
}
|
|
2890
|
+
finalErr = err;
|
|
2891
|
+
throw err;
|
|
2892
|
+
}
|
|
2893
|
+
}
|
|
2894
|
+
} finally {
|
|
2895
|
+
const totalDt = now() - startAt;
|
|
2896
|
+
httpState.markEnd(requestId, finalRes, finalErr, totalDt);
|
|
2897
|
+
}
|
|
2898
|
+
if (lastErr) throw lastErr;
|
|
2899
|
+
return wrapResponse(lastRes, req);
|
|
2900
|
+
}
|
|
2901
|
+
JSswift.http = {};
|
|
2902
|
+
JSswift.http.request = request;
|
|
2903
|
+
JSswift.http.state = () => httpState.state;
|
|
2904
|
+
JSswift.http.get = (url, init) => request(url, { ...init, method: "GET" });
|
|
2905
|
+
JSswift.http.del = (url, init) => request(url, { ...init, method: "DELETE" });
|
|
2906
|
+
JSswift.http.post = (url, body, init) => withJSON(request, "POST", url, body, init);
|
|
2907
|
+
JSswift.http.put = (url, body, init) => withJSON(request, "PUT", url, body, init);
|
|
2908
|
+
JSswift.http.patch = (url, body, init) => withJSON(request, "PATCH", url, body, init);
|
|
2909
|
+
JSswift.http.getJSON = async (url, init) => (await request(url, { ...init, method: "GET" })).jsonStrict();
|
|
2910
|
+
JSswift.http.delJSON = async (url, init) => (await request(url, { ...init, method: "DELETE" })).jsonStrict();
|
|
2911
|
+
JSswift.http.postJSON = async (url, body, init) => (await withJSON(request, "POST", url, body, init)).jsonStrict();
|
|
2912
|
+
JSswift.http.putJSON = async (url, body, init) => (await withJSON(request, "PUT", url, body, init)).jsonStrict();
|
|
2913
|
+
JSswift.http.patchJSON = async (url, body, init) => (await withJSON(request, "PATCH", url, body, init)).jsonStrict();
|
|
2914
|
+
JSswift.http.onBefore = function(fn) {
|
|
2915
|
+
hooksHTTP.beforeRequest.add(fn);
|
|
2916
|
+
return () => hooksHTTP.beforeRequest.delete(fn);
|
|
2917
|
+
};
|
|
2918
|
+
JSswift.http.onAfter = function(fn) {
|
|
2919
|
+
hooksHTTP.afterResponse.add(fn);
|
|
2920
|
+
return () => hooksHTTP.afterResponse.delete(fn);
|
|
2921
|
+
};
|
|
2922
|
+
JSswift.http.onError = function(fn) {
|
|
2923
|
+
hooksHTTP.onError.add(fn);
|
|
2924
|
+
return () => hooksHTTP.onError.delete(fn);
|
|
2925
|
+
};
|
|
2926
|
+
window._http = JSswift.http;
|
|
2927
|
+
JSswift.useRouter = function(ctx) {
|
|
2928
|
+
const router = JSswift.router;
|
|
2929
|
+
if (ctx && typeof ctx.onDispose === "function") {
|
|
2930
|
+
}
|
|
2931
|
+
return { navigate: router.navigate, back: () => history.back(), forward: () => history.forward(), get current() {
|
|
2932
|
+
return router;
|
|
2933
|
+
} };
|
|
2934
|
+
};
|
|
2935
|
+
JSswift.useRoute = function(ctx) {
|
|
2936
|
+
const [getPath, setPath] = JSswift.reactive.signal("");
|
|
2937
|
+
const [getParams, setParams] = JSswift.reactive.signal({});
|
|
2938
|
+
const [getQuery, setQuery] = JSswift.reactive.signal({});
|
|
2939
|
+
const [getHash, setHash] = JSswift.reactive.signal("");
|
|
2940
|
+
const update = (routeCtx) => {
|
|
2941
|
+
setPath(routeCtx.path || "");
|
|
2942
|
+
setParams(routeCtx.params || {});
|
|
2943
|
+
setQuery(routeCtx.query || {});
|
|
2944
|
+
setHash(routeCtx.hash || "");
|
|
2945
|
+
};
|
|
2946
|
+
const unsubscribe = JSswift.router.subscribe(update);
|
|
2947
|
+
if (ctx && typeof ctx.onDispose === "function") {
|
|
2948
|
+
ctx.onDispose(unsubscribe);
|
|
2949
|
+
}
|
|
2950
|
+
return { path: getPath, params: getParams, query: getQuery, hash: getHash };
|
|
2951
|
+
};
|
|
2952
|
+
JSswift._routerShared = /* @__PURE__ */ (() => {
|
|
2953
|
+
function normalizePath(path) {
|
|
2954
|
+
if (!path) return "/";
|
|
2955
|
+
if (!path.startsWith("/")) path = "/" + path;
|
|
2956
|
+
if (path.length > 1 && path.endsWith("/")) path = path.slice(0, -1);
|
|
2957
|
+
return path;
|
|
2958
|
+
}
|
|
2959
|
+
function stripBase(path, base = "") {
|
|
2960
|
+
if (base && path.startsWith(base)) {
|
|
2961
|
+
const next = path.slice(base.length) || "/";
|
|
2962
|
+
return normalizePath(next);
|
|
2963
|
+
}
|
|
2964
|
+
return normalizePath(path);
|
|
2965
|
+
}
|
|
2966
|
+
function compilePattern(pattern) {
|
|
2967
|
+
pattern = normalizePath(pattern);
|
|
2968
|
+
const keys = [];
|
|
2969
|
+
const regexStr = pattern.replace(/([.+*?=^!${}()[\]|/\\])/g, "\\$1").replace(/\\\/:([A-Za-z0-9_]+)/g, (_2, key) => {
|
|
2970
|
+
keys.push(key);
|
|
2971
|
+
return "\\/([^\\/]+)";
|
|
2972
|
+
});
|
|
2973
|
+
return { pattern, regex: new RegExp("^" + regexStr + "$"), keys };
|
|
2974
|
+
}
|
|
2975
|
+
function parseQuery(search) {
|
|
2976
|
+
const query = {};
|
|
2977
|
+
const params = new URLSearchParams(search || "");
|
|
2978
|
+
params.forEach((value, key) => {
|
|
2979
|
+
if (query[key] === void 0) query[key] = value;
|
|
2980
|
+
else if (Array.isArray(query[key])) query[key].push(value);
|
|
2981
|
+
else query[key] = [query[key], value];
|
|
2982
|
+
});
|
|
2983
|
+
return query;
|
|
2984
|
+
}
|
|
2985
|
+
function flattenRoutes(routes) {
|
|
2986
|
+
const all = [];
|
|
2987
|
+
for (const route of routes) {
|
|
2988
|
+
all.push({ ...route, _parent: null });
|
|
2989
|
+
if (route.children && route.children.length) {
|
|
2990
|
+
for (const child of route.children) {
|
|
2991
|
+
all.push({ ...child, _parent: route });
|
|
2992
|
+
}
|
|
2993
|
+
}
|
|
2994
|
+
}
|
|
2995
|
+
all.sort((a, b) => b.path.length - a.path.length);
|
|
2996
|
+
return all;
|
|
2997
|
+
}
|
|
2998
|
+
function matchRoute(pathname, routes) {
|
|
2999
|
+
const path = normalizePath(pathname);
|
|
3000
|
+
const all = flattenRoutes(routes);
|
|
3001
|
+
for (const route of all) {
|
|
3002
|
+
const match = route._compiled.regex.exec(path);
|
|
3003
|
+
if (!match) continue;
|
|
3004
|
+
const params = {};
|
|
3005
|
+
route._compiled.keys.forEach((key, index) => {
|
|
3006
|
+
params[key] = decodeURIComponent(match[index + 1]);
|
|
3007
|
+
});
|
|
3008
|
+
return { route, params, parent: route._parent || null };
|
|
3009
|
+
}
|
|
3010
|
+
return null;
|
|
3011
|
+
}
|
|
3012
|
+
function pushHistoryEntry(history2, ctx, limit = 50) {
|
|
3013
|
+
history2.push({ at: Date.now(), path: ctx.path, params: ctx.params, query: ctx.query, hash: ctx.hash });
|
|
3014
|
+
if (history2.length > limit) history2.shift();
|
|
3015
|
+
}
|
|
3016
|
+
return { normalizePath, stripBase, compilePattern, parseQuery, flattenRoutes, matchRoute, pushHistoryEntry };
|
|
3017
|
+
})();
|
|
3018
|
+
JSswift._uiMetaShared = /* @__PURE__ */ (() => {
|
|
3019
|
+
function resolveDocComponents(_2) {
|
|
3020
|
+
return { hasTabPanel: typeof _2.TabPanel === "function", Card: typeof _2.Card === "function" ? _2.Card : (...children) => _2.div({ class: "jss-doc-card" }, ...children), Chip: typeof _2.Chip === "function" ? _2.Chip : (_props, label) => _2.span({ class: "jss-chip jss-chip-fallback" }, label) };
|
|
3021
|
+
}
|
|
3022
|
+
function formatMetaValues(values) {
|
|
3023
|
+
if (!values) return "\u2014";
|
|
3024
|
+
if (Array.isArray(values)) return values.join(" | ");
|
|
3025
|
+
return String(values);
|
|
3026
|
+
}
|
|
3027
|
+
function renderMetaItem(_2, item, Chip) {
|
|
3028
|
+
return _2.div({ class: "jss-doc-meta" }, _2.div({ class: "jss-doc-meta-head" }, _2.div({ class: "jss-doc-meta-name" }, item.name), _2.div({ class: "jss-doc-meta-types" }, ...item.type ? String(item.type).split("|").map((token) => Chip({ color: "secondary", dense: true, outline: true }, token)) : [_2.span("\u2014")])), _2.div({ class: "jss-doc-meta-grid" }, _2.div({ class: "jss-doc-meta-block" }, _2.div({ class: "jss-doc-meta-label" }, "Default"), _2.div({ class: "jss-doc-meta-value" }, item.default == null ? "\u2014" : String(item.default))), _2.div({ class: "jss-doc-meta-block" }, _2.div({ class: "jss-doc-meta-label" }, "Values"), _2.div({ class: "jss-doc-meta-value" }, formatMetaValues(item.values)))), _2.div({ class: "jss-doc-meta-block" }, _2.div({ class: "jss-doc-meta-label" }, "Description"), _2.div({ class: "jss-doc-meta-value" }, item.description || "\u2014")));
|
|
3029
|
+
}
|
|
3030
|
+
function renderTabGroupFallback(_2, rows) {
|
|
3031
|
+
return _2.div({ class: "jss-doc-fallback" }, rows.map((row) => _2.div({ class: "jss-doc-fallback-item" }, _2.h4({ class: "jss-doc-fallback-title" }, row.label || row.name), row.content)));
|
|
3032
|
+
}
|
|
3033
|
+
function normalizeEventRows(_2, events) {
|
|
3034
|
+
if (!events) return [];
|
|
3035
|
+
if (Array.isArray(events)) {
|
|
3036
|
+
return events.map((eventItem) => ({ name: eventItem.name, wrap: true, label: eventItem.name, content: _2.div({ class: "jss-p-md" }, eventItem.description) }));
|
|
3037
|
+
}
|
|
3038
|
+
return Object.entries(events).map(([key, value]) => ({ name: key, wrap: true, label: key, content: _2.div({ class: "jss-p-md" }, value) }));
|
|
3039
|
+
}
|
|
3040
|
+
function normalizeSlotRows(_2, slots) {
|
|
3041
|
+
if (!slots) return [];
|
|
3042
|
+
if (Array.isArray(slots)) {
|
|
3043
|
+
return slots.map((slot) => ({ name: slot.name, wrap: true, label: slot.type, content: slot.description }));
|
|
3044
|
+
}
|
|
3045
|
+
return Object.entries(slots).map(([key, value]) => ({ name: key, wrap: true, label: key, content: _2.div({ class: "jss-doc-meta" }, _2.div({ class: "jss-doc-meta-head" }, _2.div({ class: "jss-doc-meta-name" }, key)), _2.div({ class: "jss-doc-meta-grid" }, _2.div({ class: "jss-doc-meta-block" }, _2.div({ class: "jss-doc-meta-label" }, "Type"), _2.div({ class: "jss-doc-meta-value" }, value.type || "\u2014"))), _2.div({ class: "jss-doc-meta-block" }, _2.div({ class: "jss-doc-meta-label" }, "Description"), _2.div({ class: "jss-doc-meta-value" }, value.description || "\u2014"))) }));
|
|
3046
|
+
}
|
|
3047
|
+
return { resolveDocComponents, renderMetaItem, renderTabGroupFallback, normalizeEventRows, normalizeSlotRows };
|
|
3048
|
+
})();
|
|
3049
|
+
JSswift.ui = JSswift.ui || {};
|
|
3050
|
+
JSswift.ui.meta = JSswift.ui.meta || {};
|
|
3051
|
+
JSswift.ui.slot = function slot(value, opts = {}) {
|
|
3052
|
+
const UI = JSswift.ui;
|
|
3053
|
+
const { as = "node", wrap = "span", iconProps = null, filter = true } = opts;
|
|
3054
|
+
const makeTextNode = (v) => document.createTextNode(String(v));
|
|
3055
|
+
const makeSpan = (v) => _.span(String(v));
|
|
3056
|
+
const normalizeOne = (v) => {
|
|
3057
|
+
if (v == null || v === false) return null;
|
|
3058
|
+
if (typeof v === "function") return v;
|
|
3059
|
+
if (Array.isArray(v)) return v.flatMap(normalizeOne).filter(Boolean);
|
|
3060
|
+
if (v && typeof v === "object" && v.nodeType) return v;
|
|
3061
|
+
if (typeof v === "string" || typeof v === "number" || typeof v === "boolean") {
|
|
3062
|
+
const s = String(v);
|
|
3063
|
+
if (as === "icon" && UI.Icon) {
|
|
3064
|
+
return UI.Icon(s, { class: "jss-slot-icon", ...iconProps || {} });
|
|
3065
|
+
}
|
|
3066
|
+
if (wrap === "text") return makeTextNode(s);
|
|
3067
|
+
if (wrap === "span") return makeSpan(s);
|
|
3068
|
+
return s;
|
|
3069
|
+
}
|
|
3070
|
+
if (wrap === "text") return makeTextNode(v);
|
|
3071
|
+
if (wrap === "span") return makeSpan(v);
|
|
3072
|
+
return String(v);
|
|
3073
|
+
};
|
|
3074
|
+
const out = normalizeOne(value);
|
|
3075
|
+
if (!filter) return out;
|
|
3076
|
+
if (Array.isArray(out)) return out.filter(Boolean);
|
|
3077
|
+
return out;
|
|
3078
|
+
};
|
|
3079
|
+
JSswift.ui.slots = function slots(...values) {
|
|
3080
|
+
const out = [];
|
|
3081
|
+
for (const v of values) {
|
|
3082
|
+
const r = JSswift.ui.slot(v);
|
|
3083
|
+
if (!r) continue;
|
|
3084
|
+
if (Array.isArray(r)) out.push(...r);
|
|
3085
|
+
else out.push(r);
|
|
3086
|
+
}
|
|
3087
|
+
return out;
|
|
3088
|
+
};
|
|
3089
|
+
JSswift.docTable = (name) => {
|
|
3090
|
+
var _a2;
|
|
3091
|
+
if (!JSswift.isDev()) return _.div();
|
|
3092
|
+
const { resolveDocComponents, renderMetaItem, renderTabGroupFallback, normalizeEventRows, normalizeSlotRows } = JSswift._uiMetaShared;
|
|
3093
|
+
const meta = (_a2 = JSswift.ui.meta) == null ? void 0 : _a2[name];
|
|
3094
|
+
if (!meta) return _.div({ class: "jss-muted" }, `Meta non trovata: ${name}`);
|
|
3095
|
+
const { hasTabPanel, Card, Chip } = resolveDocComponents(_);
|
|
3096
|
+
const list = {};
|
|
3097
|
+
Object.entries(meta.props || {}).forEach(([k, v]) => {
|
|
3098
|
+
const cat = v.category || "general";
|
|
3099
|
+
if (!list[cat]) list[cat] = [];
|
|
3100
|
+
list[cat].push({ name: k, ...v });
|
|
3101
|
+
});
|
|
3102
|
+
const propsTab = Object.keys(list).map((v) => {
|
|
3103
|
+
const rows = list[v].map((p) => {
|
|
3104
|
+
return { name: p.name, wrap: true, label: p.name, content: renderMetaItem(_, p, Chip) };
|
|
3105
|
+
});
|
|
3106
|
+
return { name: v, wrap: true, label: v, content: hasTabPanel ? _.TabPanel({ orientation: "vertical", animated: true, radius: "0 0 0 var(--jss-r-default)", tabs: rows }) : renderTabGroupFallback(_, rows) };
|
|
3107
|
+
});
|
|
3108
|
+
const eventsRows = normalizeEventRows(_, meta.events);
|
|
3109
|
+
const slotsRows = normalizeSlotRows(_, meta.slots);
|
|
3110
|
+
const tabPanelModel = _.rod(null);
|
|
3111
|
+
const taps = [];
|
|
3112
|
+
if (slotsRows.length) taps.push({ name: "Slots", wrap: true, label: "Slots", content: hasTabPanel ? _.TabPanel({ animated: true, radius: "0 0 0 var(--jss-r-default)", orientation: "vertical", tabs: slotsRows }) : renderTabGroupFallback(_, slotsRows) });
|
|
3113
|
+
if (propsTab.length) taps.push(...propsTab);
|
|
3114
|
+
if (eventsRows.length) taps.push({ name: "Events", wrap: true, label: "Events", content: hasTabPanel ? _.TabPanel({ animated: true, radius: "0 0 0 var(--jss-r-default)", orientation: "vertical", tabs: eventsRows }) : renderTabGroupFallback(_, eventsRows) });
|
|
3115
|
+
const first = "general";
|
|
3116
|
+
taps.sort((a, b) => {
|
|
3117
|
+
if (a.name === first) return -1;
|
|
3118
|
+
if (b.name === first) return 1;
|
|
3119
|
+
return a.name.localeCompare(b.name);
|
|
3120
|
+
});
|
|
3121
|
+
return Card({ class: "jss-doc-table" }, _.div({ class: "jss-doc-table-header" }, _.div({ class: "jss-doc-table-title" }, `_.${name}`), meta.signature ? _.div({ class: "jss-doc-table-signature" }, String(meta.signature).replaceAll("UI.", "_.")) : null), taps.length ? _.h4({ class: "jss-doc-table-section-title" }, propsTab.length ? "Props" : "Documentation") : _.p({ class: "jss-muted" }, "Nessuna documentazione strutturata disponibile."), taps.length ? hasTabPanel ? _.TabPanel({ border: true, animated: true, orientation: "horizontal", tabs: taps, model: tabPanelModel }) : renderTabGroupFallback(_, taps) : null, meta.returns ? _.p({ class: "jss-muted", style: { marginTop: "14px" } }, `Returns: ${meta.returns}`) : null);
|
|
3122
|
+
};
|
|
3123
|
+
JSswift.ui.inspect = (name) => {
|
|
3124
|
+
var _a2;
|
|
3125
|
+
return console.log(((_a2 = JSswift.ui.meta) == null ? void 0 : _a2[name]) || "meta not found");
|
|
3126
|
+
};
|
|
3127
|
+
JSswift.ui.can = function(ctx, permOrRole, render, elseRender = null) {
|
|
3128
|
+
var _a2, _b2;
|
|
3129
|
+
const auth = JSswift.useAuth ? JSswift.useAuth(ctx) : JSswift.auth;
|
|
3130
|
+
if (!auth) return typeof elseRender === "function" ? elseRender() : null;
|
|
3131
|
+
const ok = typeof permOrRole === "function" ? !!permOrRole(auth) : ((_a2 = auth.can) == null ? void 0 : _a2.call(auth, permOrRole)) || ((_b2 = auth.hasRole) == null ? void 0 : _b2.call(auth, permOrRole));
|
|
3132
|
+
if (ok) return typeof render === "function" ? render() : render;
|
|
3133
|
+
return typeof elseRender === "function" ? elseRender() : elseRender;
|
|
3134
|
+
};
|
|
3135
|
+
JSswift.ui.canAny = function(ctx, list, render, elseRender = null) {
|
|
3136
|
+
var _a2;
|
|
3137
|
+
const auth = JSswift.useAuth ? JSswift.useAuth(ctx) : JSswift.auth;
|
|
3138
|
+
const ok = !!((_a2 = auth == null ? void 0 : auth.canAny) == null ? void 0 : _a2.call(auth, list));
|
|
3139
|
+
return ok ? typeof render === "function" ? render() : render : typeof elseRender === "function" ? elseRender() : elseRender;
|
|
3140
|
+
};
|
|
3141
|
+
JSswift.ui.canAll = function(ctx, list, render, elseRender = null) {
|
|
3142
|
+
var _a2;
|
|
3143
|
+
const auth = JSswift.useAuth ? JSswift.useAuth(ctx) : JSswift.auth;
|
|
3144
|
+
const ok = !!((_a2 = auth == null ? void 0 : auth.canAll) == null ? void 0 : _a2.call(auth, list));
|
|
3145
|
+
return ok ? typeof render === "function" ? render() : render : typeof elseRender === "function" ? elseRender() : elseRender;
|
|
3146
|
+
};
|
|
3147
|
+
JSswift.Can = function Can(props, ctx) {
|
|
3148
|
+
var _a2, _b2;
|
|
3149
|
+
const auth = JSswift.useAuth ? JSswift.useAuth(ctx) : JSswift.auth;
|
|
3150
|
+
const pred = () => {
|
|
3151
|
+
var _a3, _b3, _c2, _d2;
|
|
3152
|
+
if (!auth) return false;
|
|
3153
|
+
if (props.when && typeof props.when === "function") return !!props.when(auth);
|
|
3154
|
+
if (props.perm) return !!((_a3 = auth.can) == null ? void 0 : _a3.call(auth, props.perm));
|
|
3155
|
+
if (props.role) return !!((_b3 = auth.hasRole) == null ? void 0 : _b3.call(auth, props.role));
|
|
3156
|
+
if (props.any) return !!((_c2 = auth.canAny) == null ? void 0 : _c2.call(auth, props.any));
|
|
3157
|
+
if (props.all) return !!((_d2 = auth.canAll) == null ? void 0 : _d2.call(auth, props.all));
|
|
3158
|
+
return false;
|
|
3159
|
+
};
|
|
3160
|
+
return pred() ? typeof props.then === "function" ? props.then() : (_a2 = props.then) != null ? _a2 : null : typeof props.else === "function" ? props.else() : (_b2 = props.else) != null ? _b2 : null;
|
|
3161
|
+
};
|
|
3162
|
+
JSswift.router = (() => {
|
|
3163
|
+
let mode = "history";
|
|
3164
|
+
let routes = [];
|
|
3165
|
+
let outlet = null;
|
|
3166
|
+
let unmountCurrent = null;
|
|
3167
|
+
let base = "";
|
|
3168
|
+
let beforeHook = null;
|
|
3169
|
+
let _currentCtx = null;
|
|
3170
|
+
let _history = [];
|
|
3171
|
+
let _tracing = false;
|
|
3172
|
+
const { normalizePath, stripBase: stripBasePath, compilePattern, parseQuery, matchRoute, pushHistoryEntry } = JSswift._routerShared;
|
|
3173
|
+
const meta = { setOutlet: { description: "imposta il contenitore del router" }, setBase: { description: "imposta il base path del router" }, add: { description: "per aggiungere la route" }, notFound: { description: "imposta la view 404" }, beforeEach: { description: "hook prima di ogni navigazione" }, start: { description: "avvia il router" }, navigate: { description: "naviga verso una route" }, subscribe: { description: "sottoscrive agli aggiornamenti di route" }, current: { description: "ritorna l'URL corrente" }, isActive: { description: "verifica se una route e\u0300 attiva" }, status: { description: "stringa di stato router" }, inspect: { description: "stampa info diagnostiche" }, trace: { description: "abilita/disabilita tracing" }, history: { description: "ritorna la history interna" }, setURLOnly: { description: "aggiorna solo l'URL del browser senza render/meta" } };
|
|
3174
|
+
function setURLOnly(urlLike, { replace = false } = {}) {
|
|
3175
|
+
const url = typeof urlLike === "string" ? new URL(urlLike, window.location.origin) : urlLike;
|
|
3176
|
+
const fullPath = url.pathname + url.search + url.hash;
|
|
3177
|
+
if (mode === "hash" || mode === "auto") {
|
|
3178
|
+
if (history.pushState) {
|
|
3179
|
+
const h2 = "#" + fullPath;
|
|
3180
|
+
if (replace) history.replaceState({}, "", h2);
|
|
3181
|
+
else history.pushState({}, "", h2);
|
|
3182
|
+
return;
|
|
3183
|
+
}
|
|
3184
|
+
const h = "#" + fullPath;
|
|
3185
|
+
if (replace) window.location.replace(h);
|
|
3186
|
+
else window.location.hash = h;
|
|
3187
|
+
return;
|
|
3188
|
+
}
|
|
3189
|
+
if (replace) history.replaceState({}, "", base + fullPath);
|
|
3190
|
+
else history.pushState({}, "", base + fullPath);
|
|
3191
|
+
}
|
|
3192
|
+
function getCurrentURL() {
|
|
3193
|
+
if (mode === "hash") {
|
|
3194
|
+
const h = window.location.hash || "#/";
|
|
3195
|
+
const url = h.startsWith("#") ? h.slice(1) : h;
|
|
3196
|
+
return new URL(url, window.location.origin);
|
|
3197
|
+
}
|
|
3198
|
+
if (mode === "auto") {
|
|
3199
|
+
if (window.location.hash.startsWith("#/")) {
|
|
3200
|
+
const url = window.location.hash.slice(1);
|
|
3201
|
+
return new URL(url, window.location.origin);
|
|
3202
|
+
}
|
|
3203
|
+
}
|
|
3204
|
+
return new URL(window.location.href);
|
|
3205
|
+
}
|
|
3206
|
+
function updateURL(url, replace = false) {
|
|
3207
|
+
if (mode === "hash" || mode === "auto" && !history.pushState) {
|
|
3208
|
+
const h = "#" + (url.pathname + url.search + url.hash);
|
|
3209
|
+
if (replace) {
|
|
3210
|
+
window.location.replace(h);
|
|
3211
|
+
} else {
|
|
3212
|
+
window.location.hash = h;
|
|
3213
|
+
}
|
|
3214
|
+
return;
|
|
3215
|
+
}
|
|
3216
|
+
if (replace) history.replaceState({}, "", base + url.pathname + url.search + url.hash);
|
|
3217
|
+
else history.pushState({}, "", base + url.pathname + url.search + url.hash);
|
|
3218
|
+
}
|
|
3219
|
+
function setMode(m) {
|
|
3220
|
+
mode = m || "history";
|
|
3221
|
+
}
|
|
3222
|
+
function stripBase(path) {
|
|
3223
|
+
return stripBasePath(path, base);
|
|
3224
|
+
}
|
|
3225
|
+
function isActive(path) {
|
|
3226
|
+
if (!path) return false;
|
|
3227
|
+
const currentPath = _currentCtx ? _currentCtx.path : stripBase(getCurrentURL().pathname);
|
|
3228
|
+
let pathname = path;
|
|
3229
|
+
if (typeof pathname === "string" && (pathname.includes("?") || pathname.includes("#") || pathname.includes("://"))) {
|
|
3230
|
+
pathname = new URL(pathname, window.location.origin).pathname;
|
|
3231
|
+
}
|
|
3232
|
+
const target = stripBase(pathname);
|
|
3233
|
+
if (!target.includes(":")) return target === currentPath;
|
|
3234
|
+
const compiled = compilePattern(target);
|
|
3235
|
+
return compiled.regex.test(currentPath);
|
|
3236
|
+
}
|
|
3237
|
+
async function render(urlLike, { replace = false } = {}) {
|
|
3238
|
+
if (!outlet) {
|
|
3239
|
+
console.warn("[router] outlet non impostato. Usa JSswift.router.setOutlet('#app').");
|
|
3240
|
+
return;
|
|
3241
|
+
}
|
|
3242
|
+
const url = typeof urlLike === "string" ? new URL(urlLike, window.location.origin) : urlLike;
|
|
3243
|
+
if (_tracing) {
|
|
3244
|
+
console.log("[router.navigate]", url.toString());
|
|
3245
|
+
}
|
|
3246
|
+
const fullPath = url.pathname + url.search + url.hash;
|
|
3247
|
+
updateURL(url, replace);
|
|
3248
|
+
const pathname = stripBase(url.pathname);
|
|
3249
|
+
const m = matchRoute(pathname, routes);
|
|
3250
|
+
const ctx = { path: pathname, fullPath, params: m ? m.params : {}, query: parseQuery(url.search), hash: url.hash || "", navigate, outlet };
|
|
3251
|
+
if (beforeHook) {
|
|
3252
|
+
const res = await beforeHook(ctx);
|
|
3253
|
+
if (res === false) return;
|
|
3254
|
+
if (typeof res === "string") {
|
|
3255
|
+
navigate(res, { replace: true });
|
|
3256
|
+
return;
|
|
3257
|
+
}
|
|
3258
|
+
}
|
|
3259
|
+
if (typeof unmountCurrent === "function") {
|
|
3260
|
+
try {
|
|
3261
|
+
unmountCurrent();
|
|
3262
|
+
} catch (e) {
|
|
3263
|
+
console.error("[router] unmount error:", e);
|
|
3264
|
+
}
|
|
3265
|
+
unmountCurrent = null;
|
|
3266
|
+
}
|
|
3267
|
+
if (!m) {
|
|
3268
|
+
const view404 = routes._notFound;
|
|
3269
|
+
if (view404) {
|
|
3270
|
+
unmountCurrent = JSswift.mount(outlet, () => view404(ctx), { clear: true });
|
|
3271
|
+
} else {
|
|
3272
|
+
unmountCurrent = JSswift.mount(outlet, _.div("404"), { clear: true });
|
|
3273
|
+
}
|
|
3274
|
+
notifyRoute(ctx);
|
|
3275
|
+
_currentCtx = ctx;
|
|
3276
|
+
pushHistoryEntry(_history, ctx);
|
|
3277
|
+
return;
|
|
3278
|
+
}
|
|
3279
|
+
const view = m.route.view;
|
|
3280
|
+
const parent = m.parent;
|
|
3281
|
+
let childMounted = null;
|
|
3282
|
+
ctx.renderChild = (target) => {
|
|
3283
|
+
if (!ctx._child) return null;
|
|
3284
|
+
const childOutlet = typeof target === "string" ? ctx.outlet.querySelector(target) : target;
|
|
3285
|
+
if (!childOutlet) {
|
|
3286
|
+
console.warn("[router] child outlet non trovato:", target);
|
|
3287
|
+
return null;
|
|
3288
|
+
}
|
|
3289
|
+
childMounted = JSswift.mount(childOutlet, () => ctx._child.view(ctx._child.ctx), { clear: true });
|
|
3290
|
+
return childMounted;
|
|
3291
|
+
};
|
|
3292
|
+
if (parent) {
|
|
3293
|
+
const childRoute = m.route;
|
|
3294
|
+
const childCtx = { ...ctx, params: { ...ctx.params } };
|
|
3295
|
+
ctx._child = { view: childRoute.view, ctx: childCtx };
|
|
3296
|
+
unmountCurrent = JSswift.mount(outlet, () => parent.view(ctx), { clear: true });
|
|
3297
|
+
} else {
|
|
3298
|
+
ctx._child = null;
|
|
3299
|
+
unmountCurrent = JSswift.mount(outlet, () => view(ctx), { clear: true });
|
|
3300
|
+
}
|
|
3301
|
+
notifyRoute(ctx);
|
|
3302
|
+
_currentCtx = ctx;
|
|
3303
|
+
pushHistoryEntry(_history, ctx);
|
|
3304
|
+
}
|
|
3305
|
+
function navigate(to, opts = {}) {
|
|
3306
|
+
const url = new URL(to, window.location.origin);
|
|
3307
|
+
render(url, opts);
|
|
3308
|
+
}
|
|
3309
|
+
function start() {
|
|
3310
|
+
window.addEventListener("popstate", () => {
|
|
3311
|
+
render(getCurrentURL(), { replace: true });
|
|
3312
|
+
});
|
|
3313
|
+
window.addEventListener("hashchange", () => {
|
|
3314
|
+
if (mode === "hash" || mode === "auto") {
|
|
3315
|
+
render(getCurrentURL(), { replace: true });
|
|
3316
|
+
}
|
|
3317
|
+
});
|
|
3318
|
+
document.addEventListener("click", (e) => {
|
|
3319
|
+
var _a2, _b2;
|
|
3320
|
+
const a = (_b2 = (_a2 = e.target) == null ? void 0 : _a2.closest) == null ? void 0 : _b2.call(_a2, "a");
|
|
3321
|
+
if (!a) return;
|
|
3322
|
+
if (e.defaultPrevented) return;
|
|
3323
|
+
if (e.button !== 0) return;
|
|
3324
|
+
if (e.metaKey || e.ctrlKey || e.shiftKey || e.altKey) return;
|
|
3325
|
+
if (a.hasAttribute("download")) return;
|
|
3326
|
+
if (a.target && a.target !== "_self") return;
|
|
3327
|
+
const href = a.getAttribute("href");
|
|
3328
|
+
if (!href || href.startsWith("mailto:") || href.startsWith("tel:")) return;
|
|
3329
|
+
const url = new URL(href, window.location.origin);
|
|
3330
|
+
if (url.origin !== window.location.origin) return;
|
|
3331
|
+
const current = new URL(window.location.href);
|
|
3332
|
+
const samePath = url.pathname + url.search === current.pathname + current.search;
|
|
3333
|
+
if (samePath && url.hash) return;
|
|
3334
|
+
e.preventDefault();
|
|
3335
|
+
navigate(url.pathname + url.search + url.hash);
|
|
3336
|
+
});
|
|
3337
|
+
render(getCurrentURL(), { replace: true });
|
|
3338
|
+
}
|
|
3339
|
+
function setOutlet(target) {
|
|
3340
|
+
outlet = typeof target === "string" ? JSswift.dom.q(target) : target;
|
|
3341
|
+
return outlet;
|
|
3342
|
+
}
|
|
3343
|
+
function setBase(b) {
|
|
3344
|
+
base = b ? normalizePath(b) : "";
|
|
3345
|
+
if (base === "/") base = "";
|
|
3346
|
+
}
|
|
3347
|
+
function add(path, view, children = null) {
|
|
3348
|
+
const compiled = compilePattern(path);
|
|
3349
|
+
const record = { path: compiled.pattern, view, _compiled: compiled, children: [] };
|
|
3350
|
+
if (Array.isArray(children)) {
|
|
3351
|
+
for (const c of children) {
|
|
3352
|
+
const [subPath, subView, subChildren] = c;
|
|
3353
|
+
const full = normalizePath(compiled.pattern + "/" + (subPath || "").replace(/^\//, ""));
|
|
3354
|
+
record.children.push({ path: full, view: subView, _compiled: compilePattern(full), children: Array.isArray(subChildren) ? subChildren : [] });
|
|
3355
|
+
}
|
|
3356
|
+
}
|
|
3357
|
+
routes.push(record);
|
|
3358
|
+
}
|
|
3359
|
+
function notFound(view) {
|
|
3360
|
+
routes._notFound = view;
|
|
3361
|
+
}
|
|
3362
|
+
function beforeEach(fn) {
|
|
3363
|
+
beforeHook = fn;
|
|
3364
|
+
}
|
|
3365
|
+
let _routeListeners = /* @__PURE__ */ new Set();
|
|
3366
|
+
function notifyRoute(ctx) {
|
|
3367
|
+
for (const fn of _routeListeners) {
|
|
3368
|
+
try {
|
|
3369
|
+
fn(ctx);
|
|
3370
|
+
} catch (e) {
|
|
3371
|
+
console.error("[router] listener error:", e);
|
|
3372
|
+
}
|
|
3373
|
+
}
|
|
3374
|
+
}
|
|
3375
|
+
function routeStatus() {
|
|
3376
|
+
if (!_currentCtx) return "ROUTER \u23F3 not ready";
|
|
3377
|
+
const q = Object.keys(_currentCtx.query || {}).length ? "?" : "";
|
|
3378
|
+
return `ROUTER ${_currentCtx.path}${q} ${_currentCtx.hash || ""}`;
|
|
3379
|
+
}
|
|
3380
|
+
function routeInspect(label = "router") {
|
|
3381
|
+
if (!_currentCtx) {
|
|
3382
|
+
console.warn("[router.inspect] nessuna route attiva");
|
|
3383
|
+
return null;
|
|
3384
|
+
}
|
|
3385
|
+
const info = { label, mode: typeof mode !== "undefined" ? mode : "unknown", base, path: _currentCtx.path, params: _currentCtx.params, query: _currentCtx.query, hash: _currentCtx.hash, outlet, timestamp: (/* @__PURE__ */ new Date()).toISOString() };
|
|
3386
|
+
console.groupCollapsed(`[JSswift.router.inspect] ${label}`);
|
|
3387
|
+
console.log(info);
|
|
3388
|
+
console.log("history:", _history.slice());
|
|
3389
|
+
console.groupEnd();
|
|
3390
|
+
return info;
|
|
3391
|
+
}
|
|
3392
|
+
function routeTrace(on = true) {
|
|
3393
|
+
_tracing = !!on;
|
|
3394
|
+
console.log("[JSswift.router.trace]", _tracing ? "ON" : "OFF");
|
|
3395
|
+
}
|
|
3396
|
+
function routeHistory() {
|
|
3397
|
+
return _history.slice();
|
|
3398
|
+
}
|
|
3399
|
+
return { meta, setOutlet, setBase, add, setURLOnly, notFound, beforeEach, start, navigate, isActive, subscribe(fn) {
|
|
3400
|
+
_routeListeners.add(fn);
|
|
3401
|
+
return () => _routeListeners.delete(fn);
|
|
3402
|
+
}, current() {
|
|
3403
|
+
return getCurrentURL ? getCurrentURL() : new URL(window.location.href);
|
|
3404
|
+
}, status: routeStatus, inspect: routeInspect, trace: routeTrace, history: routeHistory };
|
|
3405
|
+
})();
|
|
3406
|
+
if ((_i = JSswift.config) == null ? void 0 : _i.debug) {
|
|
3407
|
+
window.$router = JSswift.router;
|
|
3408
|
+
}
|
|
3409
|
+
JSswift.signal = JSswift.reactive.signal;
|
|
3410
|
+
JSswift.effect = JSswift.reactive.effect;
|
|
3411
|
+
JSswift.computed = JSswift.reactive.computed;
|
|
3412
|
+
JSswift.untracked = JSswift.reactive.untracked;
|
|
3413
|
+
JSswift.batch = JSswift.reactive.batch;
|
|
3414
|
+
function normalizeThemeName(theme) {
|
|
3415
|
+
if (theme == null) return null;
|
|
3416
|
+
const value = String(theme).trim();
|
|
3417
|
+
return value || null;
|
|
3418
|
+
}
|
|
3419
|
+
function normalizeThemeList(input) {
|
|
3420
|
+
const values = Array.isArray(input) ? input : typeof input === "string" ? input.split(",") : [];
|
|
3421
|
+
const out = [];
|
|
3422
|
+
const seen = /* @__PURE__ */ new Set();
|
|
3423
|
+
for (const item of values) {
|
|
3424
|
+
const value = normalizeThemeName(item);
|
|
3425
|
+
if (!value || seen.has(value)) continue;
|
|
3426
|
+
seen.add(value);
|
|
3427
|
+
out.push(value);
|
|
3428
|
+
}
|
|
3429
|
+
return out;
|
|
3430
|
+
}
|
|
3431
|
+
function getThemeRoot() {
|
|
3432
|
+
var _a2;
|
|
3433
|
+
if (typeof document !== "undefined" && document.documentElement) {
|
|
3434
|
+
return document.documentElement;
|
|
3435
|
+
}
|
|
3436
|
+
return ((_a2 = JSswift.dom) == null ? void 0 : _a2.q) ? JSswift.dom.q("html") : null;
|
|
3437
|
+
}
|
|
3438
|
+
function getThemeStorage() {
|
|
3439
|
+
try {
|
|
3440
|
+
return typeof globalThis !== "undefined" ? globalThis.localStorage || null : null;
|
|
3441
|
+
} catch (_error) {
|
|
3442
|
+
return null;
|
|
3443
|
+
}
|
|
3444
|
+
}
|
|
3445
|
+
function getThemeStorageKey() {
|
|
3446
|
+
var _a2;
|
|
3447
|
+
return normalizeThemeName((_a2 = JSswift.theme) == null ? void 0 : _a2.storageKey) || "jsswift:theme";
|
|
3448
|
+
}
|
|
3449
|
+
function readSavedTheme() {
|
|
3450
|
+
const storage = getThemeStorage();
|
|
3451
|
+
if (!storage) return null;
|
|
3452
|
+
try {
|
|
3453
|
+
return normalizeThemeName(storage.getItem(getThemeStorageKey()));
|
|
3454
|
+
} catch (_error) {
|
|
3455
|
+
return null;
|
|
3456
|
+
}
|
|
3457
|
+
}
|
|
3458
|
+
function writeSavedTheme(theme) {
|
|
3459
|
+
const storage = getThemeStorage();
|
|
3460
|
+
if (!storage) return;
|
|
3461
|
+
try {
|
|
3462
|
+
if (theme == null) {
|
|
3463
|
+
storage.removeItem(getThemeStorageKey());
|
|
3464
|
+
} else {
|
|
3465
|
+
storage.setItem(getThemeStorageKey(), theme);
|
|
3466
|
+
}
|
|
3467
|
+
} catch (_error) {
|
|
3468
|
+
}
|
|
3469
|
+
}
|
|
3470
|
+
function resolveThemeList(themes) {
|
|
3471
|
+
var _a2, _b2, _c2, _d2, _e2;
|
|
3472
|
+
const html = getThemeRoot();
|
|
3473
|
+
const candidates = [themes, (_a2 = JSswift.theme) == null ? void 0 : _a2.themes, (_b2 = JSswift.config) == null ? void 0 : _b2.themes, typeof globalThis !== "undefined" ? (_c2 = globalThis.JSswift_setting) == null ? void 0 : _c2.themes : null, typeof globalThis !== "undefined" ? (_d2 = globalThis.JSswift_setting) == null ? void 0 : _d2.themeList : null, (_e2 = html == null ? void 0 : html.getAttribute) == null ? void 0 : _e2.call(html, "data-themes")];
|
|
3474
|
+
for (const candidate of candidates) {
|
|
3475
|
+
const list = normalizeThemeList(candidate);
|
|
3476
|
+
if (list.length) return list;
|
|
3477
|
+
}
|
|
3478
|
+
return [];
|
|
3479
|
+
}
|
|
3480
|
+
JSswift.theme = JSswift.theme || {};
|
|
3481
|
+
JSswift.theme.storageKey = getThemeStorageKey();
|
|
3482
|
+
if (!normalizeThemeList(JSswift.theme.themes).length) {
|
|
3483
|
+
JSswift.theme.themes = resolveThemeList();
|
|
3484
|
+
}
|
|
3485
|
+
JSswift.setTheme = function(theme, opts = {}) {
|
|
3486
|
+
const value = normalizeThemeName(theme);
|
|
3487
|
+
const html = getThemeRoot();
|
|
3488
|
+
if (html) {
|
|
3489
|
+
if (value == null) html.removeAttribute("data-theme");
|
|
3490
|
+
else html.setAttribute("data-theme", value);
|
|
3491
|
+
}
|
|
3492
|
+
if (opts.persist !== false) writeSavedTheme(value);
|
|
3493
|
+
return html;
|
|
3494
|
+
};
|
|
3495
|
+
JSswift.getTheme = function(opts = {}) {
|
|
3496
|
+
var _a2;
|
|
3497
|
+
const html = getThemeRoot();
|
|
3498
|
+
const current = normalizeThemeName((_a2 = html == null ? void 0 : html.getAttribute) == null ? void 0 : _a2.call(html, "data-theme"));
|
|
3499
|
+
if (current) return current;
|
|
3500
|
+
if (opts.storage === false) return null;
|
|
3501
|
+
const saved = readSavedTheme();
|
|
3502
|
+
if (saved && html && opts.sync !== false) {
|
|
3503
|
+
html.setAttribute("data-theme", saved);
|
|
3504
|
+
}
|
|
3505
|
+
return saved;
|
|
3506
|
+
};
|
|
3507
|
+
JSswift.toggleTheme = function(themes, opts = {}) {
|
|
3508
|
+
const list = resolveThemeList(themes);
|
|
3509
|
+
const activeList = list.length ? list : normalizeThemeList([JSswift.getTheme({ sync: false }), "light", "dark"]);
|
|
3510
|
+
const current = JSswift.getTheme({ sync: false });
|
|
3511
|
+
const fallback = normalizeThemeName(opts.fallback) || activeList[0] || "light";
|
|
3512
|
+
const currentIndex = current ? activeList.indexOf(current) : -1;
|
|
3513
|
+
const nextTheme = currentIndex >= 0 ? activeList[(currentIndex + 1) % activeList.length] : fallback;
|
|
3514
|
+
JSswift.theme.themes = activeList.slice();
|
|
3515
|
+
JSswift.setTheme(nextTheme, opts);
|
|
3516
|
+
return nextTheme;
|
|
3517
|
+
};
|
|
3518
|
+
const bootTheme = readSavedTheme();
|
|
3519
|
+
if (bootTheme) {
|
|
3520
|
+
JSswift.setTheme(bootTheme, { persist: false });
|
|
3521
|
+
}
|
|
3522
|
+
})();
|