@jsswift/core 1.0.52 → 1.0.54

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cms.js CHANGED
@@ -661,8 +661,7 @@
661
661
  const schedulePosition = () => {
662
662
  if (!opts.anchorEl) return;
663
663
  const raf = globalThis.requestAnimationFrame || ((fn) => setTimeout(() => fn(Date.now()), 0));
664
- const caf = globalThis.cancelAnimationFrame || clearTimeout;
665
- if (entry._positionFrame != null) caf(entry._positionFrame);
664
+ if (entry._positionFrame != null) return;
666
665
  entry._positionFrame = raf(() => {
667
666
  entry._positionFrame = null;
668
667
  position();
@@ -673,13 +672,13 @@
673
672
  if (opts.anchorEl) {
674
673
  position();
675
674
  schedulePosition();
676
- const onResize = () => position();
675
+ const onResize = schedulePosition;
677
676
  window.addEventListener("resize", onResize);
678
677
  window.addEventListener("scroll", onResize, true);
679
678
  window.visualViewport?.addEventListener?.("resize", onResize);
680
679
  window.visualViewport?.addEventListener?.("scroll", onResize);
681
680
  if (typeof ResizeObserver === "function") {
682
- const panelResizeObserver = new ResizeObserver(() => position());
681
+ const panelResizeObserver = new ResizeObserver(schedulePosition);
683
682
  panelResizeObserver.observe(panel);
684
683
  entry._panelResizeObserver = panelResizeObserver;
685
684
  }
@@ -2340,163 +2339,175 @@ _.dynamic = function (renderFn) {
2340
2339
 
2341
2340
  return r;
2342
2341
  };
2343
- // ===============================
2344
- // Two-way binding: input <-> rod
2345
- // ===============================
2346
- // ===============================
2347
- // Two-way binding avanzato
2348
- // input / checkbox / radio / select
2349
- // ===============================
2350
-
2351
- function getInputKind(el) {
2352
- if (!el || !el.tagName) return null;
2353
-
2354
- const tag = el.tagName.toLowerCase();
2355
-
2356
- if (tag === "input") {
2357
- const type = (el.type || "text").toLowerCase();
2358
- if (type === "checkbox") return "checkbox";
2359
- if (type === "radio") return "radio";
2360
- if (type === "number") return "number";
2361
- return "text";
2362
- }
2363
-
2364
- if (tag === "select") {
2365
- return el.multiple ? "select-multiple" : "select";
2366
- }
2367
-
2368
- return null;
2369
- }
2370
-
2371
- JSswift.rodModel = function (target, rodObj, opts = {}) {
2372
- if (!rodObj || rodObj.type !== "rod") {
2373
- throw new Error("[JSswift.rodModel] rodObj deve essere un rod");
2374
- }
2375
-
2376
- // Supporta singolo elemento o lista (radio)
2377
- const elements = Array.isArray(target) || target instanceof NodeList
2378
- ? Array.from(target)
2379
- : [target];
2380
-
2381
- const eventName = opts.event ?? "change";
2382
- const parse = opts.parse ?? (v => v);
2383
- const format = opts.format ?? (v => v);
2384
-
2385
- let syncing = false;
2386
- const disposers = [];
2387
-
2388
- // === ROD → DOM ===
2389
- const updateDOM = (value) => {
2390
- for (const el of elements) {
2391
- if (!el) continue;
2392
- const kind = getInputKind(el);
2393
-
2394
- switch (kind) {
2395
- case "checkbox":
2396
- el.checked = !!value;
2397
- break;
2398
-
2399
- case "radio":
2400
- el.checked = el.value == value;
2401
- break;
2402
-
2403
- case "select":
2404
- el.value = value ?? "";
2405
- break;
2406
-
2407
- case "select-multiple":
2408
- const arr = Array.isArray(value) ? value.map(String) : [];
2409
- for (const opt of el.options) {
2410
- opt.selected = arr.includes(opt.value);
2411
- }
2412
- break;
2413
-
2414
- case "number":
2415
- case "text":
2416
- default:
2417
- el.value = value ?? "";
2418
- }
2419
- }
2420
- };
2421
-
2422
- // bind rod → DOM (usa rod.action)
2423
- rodObj.action((v) => {
2424
- if (syncing) return;
2425
- syncing = true;
2426
- try {
2427
- updateDOM(format(v));
2428
- } finally {
2429
- syncing = false;
2430
- }
2431
- });
2432
-
2433
- // sync iniziale
2434
- updateDOM(format(rodObj.value));
2435
-
2436
- // === DOM → ROD ===
2437
- for (const el of elements) {
2438
- if (!el) continue;
2439
-
2440
- const kind = getInputKind(el);
2441
-
2442
- const handler = () => {
2443
- if (syncing) return;
2444
-
2445
- let next;
2446
-
2447
- switch (kind) {
2448
- case "checkbox":
2449
- next = el.checked;
2450
- break;
2451
-
2452
- case "radio":
2453
- if (!el.checked) return;
2454
- next = el.value;
2455
- break;
2456
-
2457
- case "select":
2458
- next = el.value;
2459
- break;
2460
-
2461
- case "select-multiple":
2462
- next = Array.from(el.selectedOptions).map(o => o.value);
2463
- break;
2464
-
2465
- case "number":
2466
- next = parse(el.value === "" ? null : Number(el.value));
2467
- break;
2468
-
2469
- case "text":
2470
- default:
2471
- next = parse(el.value);
2472
- }
2473
-
2474
- syncing = true;
2475
- try {
2476
- if (rodObj.value !== next) rodObj.value = next;
2477
- } finally {
2478
- syncing = false;
2479
- }
2480
- };
2481
-
2482
- el.addEventListener(eventName, handler);
2483
- disposers.push(() => el.removeEventListener(eventName, handler));
2484
- }
2485
-
2486
- // cleanup automatico
2487
- if (typeof rodObj.onDispose === "function") {
2488
- rodObj.onDispose(() => disposers.forEach(fn => fn()));
2489
- }
2490
-
2491
- return () => disposers.forEach(fn => fn());
2492
- };
2493
-
2494
-
2495
- // input <-> signal (sugar)
2496
- JSswift.signalModel = function (inputEl, get, set, opts = {}) {
2497
- const r = JSswift.rodFromSignal(get, set);
2498
- return JSswift.rodModel(inputEl, r, opts);
2499
- };
2342
+ // ===============================
2343
+ // Two-way binding: input <-> rod
2344
+ // ===============================
2345
+ // ===============================
2346
+ // Two-way binding avanzato
2347
+ // input / checkbox / radio / select
2348
+ // ===============================
2349
+
2350
+ function getInputKind(el) {
2351
+ if (!el || !el.tagName) return null;
2352
+
2353
+ const tag = el.tagName.toLowerCase();
2354
+
2355
+ if (tag === "input") {
2356
+ const type = (el.type || "text").toLowerCase();
2357
+ if (type === "checkbox") return "checkbox";
2358
+ if (type === "radio") return "radio";
2359
+ if (type === "number") return "number";
2360
+ return "text";
2361
+ }
2362
+
2363
+ if (tag === "select") {
2364
+ return el.multiple ? "select-multiple" : "select";
2365
+ }
2366
+
2367
+ return null;
2368
+ }
2369
+
2370
+ JSswift.rodModel = function (target, rodObj, opts = {}) {
2371
+ if (!rodObj || rodObj.type !== "rod") {
2372
+ throw new Error("[JSswift.rodModel] rodObj deve essere un rod");
2373
+ }
2374
+
2375
+ // Supporta singolo elemento o lista (radio)
2376
+ const elements = Array.isArray(target) || target instanceof NodeList
2377
+ ? Array.from(target)
2378
+ : [target];
2379
+
2380
+ const eventName = opts.event ?? "change";
2381
+ const parse = opts.parse ?? (v => v);
2382
+ const format = opts.format ?? (v => v);
2383
+
2384
+ let syncing = false;
2385
+ const disposers = [];
2386
+
2387
+ // === ROD → DOM ===
2388
+ const updateDOM = (value) => {
2389
+ for (const el of elements) {
2390
+ if (!el) continue;
2391
+ const kind = getInputKind(el);
2392
+
2393
+ switch (kind) {
2394
+ case "checkbox":
2395
+ el.checked = !!value;
2396
+ break;
2397
+
2398
+ case "radio":
2399
+ el.checked = el.value == value;
2400
+ break;
2401
+
2402
+ case "select":
2403
+ el.value = value ?? "";
2404
+ break;
2405
+
2406
+ case "select-multiple":
2407
+ const arr = Array.isArray(value) ? value.map(String) : [];
2408
+ for (const opt of el.options) {
2409
+ opt.selected = arr.includes(opt.value);
2410
+ }
2411
+ break;
2412
+
2413
+ case "number":
2414
+ case "text":
2415
+ default:
2416
+ el.value = value ?? "";
2417
+ }
2418
+ }
2419
+ };
2420
+
2421
+ // bind rod → DOM (usa rod.action)
2422
+ const onValue = (v) => {
2423
+ if (syncing) return;
2424
+ syncing = true;
2425
+ try {
2426
+ updateDOM(format(v));
2427
+ } finally {
2428
+ syncing = false;
2429
+ }
2430
+ };
2431
+ rodObj.action(onValue);
2432
+
2433
+ // sync iniziale
2434
+ updateDOM(format(rodObj.value));
2435
+
2436
+ // === DOM → ROD ===
2437
+ for (const el of elements) {
2438
+ if (!el) continue;
2439
+
2440
+ const kind = getInputKind(el);
2441
+
2442
+ const handler = () => {
2443
+ if (syncing) return;
2444
+
2445
+ let next;
2446
+
2447
+ switch (kind) {
2448
+ case "checkbox":
2449
+ next = el.checked;
2450
+ break;
2451
+
2452
+ case "radio":
2453
+ if (!el.checked) return;
2454
+ next = el.value;
2455
+ break;
2456
+
2457
+ case "select":
2458
+ next = el.value;
2459
+ break;
2460
+
2461
+ case "select-multiple":
2462
+ next = Array.from(el.selectedOptions).map(o => o.value);
2463
+ break;
2464
+
2465
+ case "number":
2466
+ next = parse(el.value === "" ? null : Number(el.value));
2467
+ break;
2468
+
2469
+ case "text":
2470
+ default:
2471
+ next = parse(el.value);
2472
+ }
2473
+
2474
+ syncing = true;
2475
+ try {
2476
+ if (rodObj.value !== next) rodObj.value = next;
2477
+ } finally {
2478
+ syncing = false;
2479
+ }
2480
+ };
2481
+
2482
+ el.addEventListener(eventName, handler);
2483
+ disposers.push(() => el.removeEventListener(eventName, handler));
2484
+ }
2485
+
2486
+ // Release both directions without disposing the caller-owned rod.
2487
+ let disposed = false;
2488
+ const dispose = () => {
2489
+ if (disposed) return;
2490
+ disposed = true;
2491
+ disposers.splice(0).forEach(fn => fn());
2492
+ const actionIndex = rodObj._actions?.indexOf(onValue) ?? -1;
2493
+ if (actionIndex >= 0) rodObj._actions.splice(actionIndex, 1);
2494
+ const hookIndex = rodObj._disposeHooks?.indexOf(dispose) ?? -1;
2495
+ if (hookIndex >= 0 && !rodObj._disposed) rodObj._disposeHooks.splice(hookIndex, 1);
2496
+ };
2497
+ if (typeof rodObj.onDispose === "function") rodObj.onDispose(dispose);
2498
+ return dispose;
2499
+ };
2500
+
2501
+
2502
+ // input <-> signal (sugar)
2503
+ JSswift.signalModel = function (inputEl, get, set, opts = {}) {
2504
+ const r = JSswift.rodFromSignal(get, set);
2505
+ const stopBinding = JSswift.rodModel(inputEl, r, opts);
2506
+ return () => {
2507
+ stopBinding();
2508
+ r.dispose();
2509
+ };
2510
+ };
2500
2511
  // ===============================
2501
2512
  // Lifecycle helpers
2502
2513
  // ===============================