@incursa/ui-kit 0.3.7 → 0.4.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.
@@ -6,6 +6,7 @@
6
6
  menu: ".inc-dropdown__menu",
7
7
  collapseToggle: '[data-inc-toggle="collapse"]',
8
8
  tabToggle: '[data-inc-toggle="tab"]',
9
+ autoRefresh: "[data-inc-auto-refresh]",
9
10
  modalToggle: '[data-inc-toggle="modal"]',
10
11
  modalDismiss: '[data-inc-dismiss="modal"]',
11
12
  offcanvasToggle: '[data-inc-toggle="offcanvas"]',
@@ -25,6 +26,9 @@
25
26
  '[tabindex]:not([tabindex="-1"])',
26
27
  ].join(", ");
27
28
 
29
+ const autoRefreshControllers = [];
30
+ let autoRefreshReloadScheduled = false;
31
+
28
32
  function getTarget(trigger) {
29
33
  const rawTarget = trigger.getAttribute("data-inc-target")
30
34
  || trigger.getAttribute("href")
@@ -380,6 +384,190 @@
380
384
  return overlays[overlays.length - 1] || null;
381
385
  }
382
386
 
387
+ function parsePositiveInteger(value) {
388
+ const parsed = Number.parseInt(value || "", 10);
389
+
390
+ if (!Number.isFinite(parsed) || parsed < 1) {
391
+ return null;
392
+ }
393
+
394
+ return parsed;
395
+ }
396
+
397
+ function formatAutoRefreshRemaining(totalSeconds) {
398
+ if (totalSeconds < 60) {
399
+ return `${totalSeconds}s`;
400
+ }
401
+
402
+ const minutes = Math.floor(totalSeconds / 60);
403
+ const seconds = totalSeconds % 60;
404
+ return `${minutes}m ${seconds}s`;
405
+ }
406
+
407
+ function getAutoRefreshParts(root) {
408
+ return {
409
+ countdown: root.querySelector(".inc-auto-refresh__countdown"),
410
+ label: root.querySelector(".inc-auto-refresh__label"),
411
+ value: root.querySelector(".inc-auto-refresh__value"),
412
+ status: root.querySelector(".inc-auto-refresh__status"),
413
+ statusText: root.querySelector(".inc-auto-refresh__status-text"),
414
+ };
415
+ }
416
+
417
+ function renderAutoRefreshCountdown(controller, remainingSeconds) {
418
+ const { root, parts, refreshLabel } = controller;
419
+
420
+ if (parts.label) {
421
+ parts.label.textContent = refreshLabel;
422
+ }
423
+
424
+ if (parts.value) {
425
+ parts.value.textContent = formatAutoRefreshRemaining(remainingSeconds);
426
+ }
427
+
428
+ root.classList.remove("is-loading");
429
+ root.setAttribute("aria-busy", "false");
430
+
431
+ if (parts.countdown) {
432
+ parts.countdown.hidden = false;
433
+ }
434
+
435
+ if (parts.status) {
436
+ parts.status.hidden = true;
437
+ }
438
+ }
439
+
440
+ function setAutoRefreshLoadingState(controller) {
441
+ const { root, parts, loadingLabel } = controller;
442
+
443
+ root.classList.add("is-loading");
444
+ root.setAttribute("aria-busy", "true");
445
+
446
+ if (parts.countdown) {
447
+ parts.countdown.hidden = true;
448
+ }
449
+
450
+ if (parts.statusText) {
451
+ parts.statusText.textContent = loadingLabel;
452
+ }
453
+
454
+ if (parts.status) {
455
+ parts.status.hidden = false;
456
+ }
457
+ }
458
+
459
+ function stopAutoRefreshController(controller) {
460
+ if (controller.timeoutId) {
461
+ window.clearTimeout(controller.timeoutId);
462
+ controller.timeoutId = 0;
463
+ }
464
+ }
465
+
466
+ function scheduleWindowReload() {
467
+ if (autoRefreshReloadScheduled) {
468
+ return;
469
+ }
470
+
471
+ autoRefreshReloadScheduled = true;
472
+ autoRefreshControllers.forEach((controller) => stopAutoRefreshController(controller));
473
+
474
+ const deferToPaint = window.requestAnimationFrame
475
+ ? window.requestAnimationFrame.bind(window)
476
+ : (callback) => window.setTimeout(callback, 16);
477
+
478
+ deferToPaint(() => {
479
+ window.setTimeout(() => {
480
+ window.location.reload();
481
+ }, 120);
482
+ });
483
+ }
484
+
485
+ function startAutoRefreshReload(controller) {
486
+ if (autoRefreshReloadScheduled || controller.isLoading) {
487
+ return;
488
+ }
489
+
490
+ controller.isLoading = true;
491
+ stopAutoRefreshController(controller);
492
+ setAutoRefreshLoadingState(controller);
493
+ scheduleWindowReload();
494
+ }
495
+
496
+ function scheduleAutoRefreshTick(controller) {
497
+ if (autoRefreshReloadScheduled || controller.isLoading) {
498
+ return;
499
+ }
500
+
501
+ stopAutoRefreshController(controller);
502
+
503
+ const remainingMs = controller.deadline - Date.now();
504
+
505
+ if (remainingMs <= 0) {
506
+ startAutoRefreshReload(controller);
507
+ return;
508
+ }
509
+
510
+ const remainingSeconds = Math.ceil(remainingMs / 1000);
511
+ renderAutoRefreshCountdown(controller, remainingSeconds);
512
+
513
+ const nextDelay = remainingMs % 1000 || 1000;
514
+ controller.timeoutId = window.setTimeout(() => {
515
+ scheduleAutoRefreshTick(controller);
516
+ }, nextDelay);
517
+ }
518
+
519
+ function initializeAutoRefresh() {
520
+ document.querySelectorAll(selectors.autoRefresh).forEach((root) => {
521
+ if (!(root instanceof HTMLElement) || root._incAutoRefreshInitialized) {
522
+ return;
523
+ }
524
+
525
+ root._incAutoRefreshInitialized = true;
526
+
527
+ const refreshSeconds = parsePositiveInteger(root.getAttribute("data-inc-refresh-seconds"));
528
+
529
+ if (!refreshSeconds) {
530
+ return;
531
+ }
532
+
533
+ const controller = {
534
+ root,
535
+ parts: getAutoRefreshParts(root),
536
+ refreshLabel: root.getAttribute("data-inc-refresh-label") || "Refresh in",
537
+ loadingLabel: root.getAttribute("data-inc-refresh-loading-label") || "Refreshing",
538
+ deadline: Date.now() + (refreshSeconds * 1000),
539
+ timeoutId: 0,
540
+ isLoading: false,
541
+ };
542
+
543
+ autoRefreshControllers.push(controller);
544
+ scheduleAutoRefreshTick(controller);
545
+ });
546
+
547
+ if (!document._incAutoRefreshVisibilityBound && autoRefreshControllers.length) {
548
+ document._incAutoRefreshVisibilityBound = true;
549
+
550
+ document.addEventListener("visibilitychange", () => {
551
+ if (document.hidden || autoRefreshReloadScheduled) {
552
+ return;
553
+ }
554
+
555
+ autoRefreshControllers.forEach((controller) => {
556
+ if (controller.isLoading) {
557
+ return;
558
+ }
559
+
560
+ if ((controller.deadline - Date.now()) <= 0) {
561
+ startAutoRefreshReload(controller);
562
+ return;
563
+ }
564
+
565
+ scheduleAutoRefreshTick(controller);
566
+ });
567
+ });
568
+ }
569
+ }
570
+
383
571
  function trapFocus(event, container) {
384
572
  if (event.key !== "Tab") {
385
573
  return false;
@@ -686,6 +874,7 @@
686
874
  initializeMenus();
687
875
  initializeCollapses();
688
876
  initializeTabs();
877
+ initializeAutoRefresh();
689
878
  attachEventHandlers();
690
879
  }
691
880