@hyperfixi/behaviors 2.2.1 → 2.3.1

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.
Files changed (131) hide show
  1. package/dist/all.browser.global.js +316 -54
  2. package/dist/all.browser.global.js.map +1 -1
  3. package/dist/autodismiss.browser.global.js +54 -2
  4. package/dist/autodismiss.browser.global.js.map +1 -1
  5. package/dist/behaviors/autodismiss.cjs +54 -18
  6. package/dist/behaviors/autodismiss.cjs.map +1 -1
  7. package/dist/behaviors/autodismiss.js +2 -2
  8. package/dist/behaviors/clickoutside.cjs +23 -4
  9. package/dist/behaviors/clickoutside.cjs.map +1 -1
  10. package/dist/behaviors/clickoutside.js +2 -2
  11. package/dist/behaviors/clipboard.cjs +43 -21
  12. package/dist/behaviors/clipboard.cjs.map +1 -1
  13. package/dist/behaviors/clipboard.js +2 -2
  14. package/dist/behaviors/focustrap.cjs +44 -3
  15. package/dist/behaviors/focustrap.cjs.map +1 -1
  16. package/dist/behaviors/focustrap.js +2 -2
  17. package/dist/behaviors/removable.cjs +7 -4
  18. package/dist/behaviors/removable.cjs.map +1 -1
  19. package/dist/behaviors/removable.js +2 -2
  20. package/dist/behaviors/resizable.cjs +22 -14
  21. package/dist/behaviors/resizable.cjs.map +1 -1
  22. package/dist/behaviors/resizable.js +2 -2
  23. package/dist/behaviors/scrollreveal.cjs +25 -13
  24. package/dist/behaviors/scrollreveal.cjs.map +1 -1
  25. package/dist/behaviors/scrollreveal.js +2 -2
  26. package/dist/behaviors/sortable.cjs +18 -12
  27. package/dist/behaviors/sortable.cjs.map +1 -1
  28. package/dist/behaviors/sortable.js +2 -2
  29. package/dist/behaviors/tabs.cjs +80 -11
  30. package/dist/behaviors/tabs.cjs.map +1 -1
  31. package/dist/behaviors/tabs.js +2 -2
  32. package/dist/behaviors/toggleable.cjs +5 -2
  33. package/dist/behaviors/toggleable.cjs.map +1 -1
  34. package/dist/behaviors/toggleable.js +2 -2
  35. package/dist/chunk-66U2RSYJ.js +100 -0
  36. package/dist/chunk-66U2RSYJ.js.map +1 -0
  37. package/dist/{chunk-2NSKQMNM.js → chunk-7O7UOSBH.js} +2 -2
  38. package/dist/{chunk-244WSZOL.js → chunk-EKKJVXDZ.js} +2 -2
  39. package/dist/{chunk-AFGA54SA.js → chunk-F3CASJ7Q.js} +2 -2
  40. package/dist/{chunk-YHQRFUWV.js → chunk-GALKOMJ5.js} +2 -2
  41. package/dist/{chunk-272NKUJ3.js → chunk-HMU342XE.js} +8 -5
  42. package/dist/chunk-HMU342XE.js.map +1 -0
  43. package/dist/chunk-HQDYH45I.js +137 -0
  44. package/dist/chunk-HQDYH45I.js.map +1 -0
  45. package/dist/{chunk-WTUVPVSD.js → chunk-IQM32TEY.js} +2 -2
  46. package/dist/{chunk-H5Y5ARYG.js → chunk-L52AGCMP.js} +2 -2
  47. package/dist/{chunk-H2MCHR5A.js → chunk-LQJOQTBZ.js} +2 -2
  48. package/dist/{chunk-7JDV2THA.js → chunk-MVNHMYIK.js} +11 -11
  49. package/dist/{chunk-BWZ6EGII.js → chunk-NC6I6T7T.js} +19 -13
  50. package/dist/{chunk-BWZ6EGII.js.map → chunk-NC6I6T7T.js.map} +1 -1
  51. package/dist/chunk-OJ3YWO3W.js +92 -0
  52. package/dist/chunk-OJ3YWO3W.js.map +1 -0
  53. package/dist/{chunk-64DIQGQI.js → chunk-SLOXO6IY.js} +24 -5
  54. package/dist/chunk-SLOXO6IY.js.map +1 -0
  55. package/dist/{chunk-RPERQTFU.js → chunk-TLLGH5KX.js} +2 -2
  56. package/dist/{chunk-V5IWDPDP.js → chunk-U5GNXZUF.js} +23 -15
  57. package/dist/{chunk-V5IWDPDP.js.map → chunk-U5GNXZUF.js.map} +1 -1
  58. package/dist/{chunk-GPTHAUWN.js → chunk-URZGNPTQ.js} +26 -14
  59. package/dist/chunk-URZGNPTQ.js.map +1 -0
  60. package/dist/chunk-WOTZO7K4.js +92 -0
  61. package/dist/chunk-WOTZO7K4.js.map +1 -0
  62. package/dist/{chunk-JKBWIJFI.js → chunk-WPOQIMCC.js} +2 -2
  63. package/dist/{chunk-WUHHMDAA.js → chunk-XYZ7FOXJ.js} +6 -3
  64. package/dist/{chunk-WUHHMDAA.js.map → chunk-XYZ7FOXJ.js.map} +1 -1
  65. package/dist/{chunk-DYHBKDKB.js → chunk-ZZ2WSAX3.js} +2 -2
  66. package/dist/clickoutside.browser.global.js +16 -5
  67. package/dist/clickoutside.browser.global.js.map +1 -1
  68. package/dist/clipboard.browser.global.js +43 -2
  69. package/dist/clipboard.browser.global.js.map +1 -1
  70. package/dist/core.browser.global.js +5 -2
  71. package/dist/core.browser.global.js.map +1 -1
  72. package/dist/focustrap.browser.global.js +46 -5
  73. package/dist/focustrap.browser.global.js.map +1 -1
  74. package/dist/index.cjs +321 -102
  75. package/dist/index.cjs.map +1 -1
  76. package/dist/index.js +21 -21
  77. package/dist/loaders.cjs +321 -102
  78. package/dist/loaders.cjs.map +1 -1
  79. package/dist/loaders.js +11 -11
  80. package/dist/removable.browser.global.js +8 -5
  81. package/dist/removable.browser.global.js.map +1 -1
  82. package/dist/resizable.browser.global.js +24 -16
  83. package/dist/resizable.browser.global.js.map +1 -1
  84. package/dist/resolver.browser.global.js +407 -0
  85. package/dist/resolver.browser.global.js.map +1 -0
  86. package/dist/schemas/index.cjs +321 -102
  87. package/dist/schemas/index.cjs.map +1 -1
  88. package/dist/schemas/index.js +10 -10
  89. package/dist/scrollreveal.browser.global.js +27 -8
  90. package/dist/scrollreveal.browser.global.js.map +1 -1
  91. package/dist/sortable.browser.global.js +19 -13
  92. package/dist/sortable.browser.global.js.map +1 -1
  93. package/dist/tabs.browser.global.js +80 -2
  94. package/dist/tabs.browser.global.js.map +1 -1
  95. package/dist/toggleable.browser.global.js +6 -3
  96. package/dist/toggleable.browser.global.js.map +1 -1
  97. package/package.json +1 -1
  98. package/src/behavior-resolver.ts +91 -0
  99. package/src/browser-resolver-bundle.ts +42 -0
  100. package/src/schemas/autodismiss.schema.ts +54 -18
  101. package/src/schemas/clickoutside.schema.ts +22 -3
  102. package/src/schemas/clipboard.schema.ts +43 -21
  103. package/src/schemas/focustrap.schema.ts +44 -3
  104. package/src/schemas/removable.schema.ts +7 -4
  105. package/src/schemas/resizable.schema.ts +22 -14
  106. package/src/schemas/scrollreveal.schema.ts +25 -13
  107. package/src/schemas/sortable.schema.ts +18 -12
  108. package/src/schemas/tabs.schema.ts +80 -11
  109. package/src/schemas/toggleable.schema.ts +5 -2
  110. package/dist/chunk-272NKUJ3.js.map +0 -1
  111. package/dist/chunk-64DIQGQI.js.map +0 -1
  112. package/dist/chunk-GPTHAUWN.js.map +0 -1
  113. package/dist/chunk-GVJPA2EK.js +0 -68
  114. package/dist/chunk-GVJPA2EK.js.map +0 -1
  115. package/dist/chunk-QKDYW4VM.js +0 -70
  116. package/dist/chunk-QKDYW4VM.js.map +0 -1
  117. package/dist/chunk-RB7OB6OD.js +0 -51
  118. package/dist/chunk-RB7OB6OD.js.map +0 -1
  119. package/dist/chunk-ZQQZTMCJ.js +0 -64
  120. package/dist/chunk-ZQQZTMCJ.js.map +0 -1
  121. /package/dist/{chunk-2NSKQMNM.js.map → chunk-7O7UOSBH.js.map} +0 -0
  122. /package/dist/{chunk-244WSZOL.js.map → chunk-EKKJVXDZ.js.map} +0 -0
  123. /package/dist/{chunk-AFGA54SA.js.map → chunk-F3CASJ7Q.js.map} +0 -0
  124. /package/dist/{chunk-YHQRFUWV.js.map → chunk-GALKOMJ5.js.map} +0 -0
  125. /package/dist/{chunk-WTUVPVSD.js.map → chunk-IQM32TEY.js.map} +0 -0
  126. /package/dist/{chunk-H5Y5ARYG.js.map → chunk-L52AGCMP.js.map} +0 -0
  127. /package/dist/{chunk-H2MCHR5A.js.map → chunk-LQJOQTBZ.js.map} +0 -0
  128. /package/dist/{chunk-7JDV2THA.js.map → chunk-MVNHMYIK.js.map} +0 -0
  129. /package/dist/{chunk-RPERQTFU.js.map → chunk-TLLGH5KX.js.map} +0 -0
  130. /package/dist/{chunk-JKBWIJFI.js.map → chunk-WPOQIMCC.js.map} +0 -0
  131. /package/dist/{chunk-DYHBKDKB.js.map → chunk-ZZ2WSAX3.js.map} +0 -0
package/dist/index.cjs CHANGED
@@ -182,7 +182,7 @@ var init_removable_schema = __esm({
182
182
  description: "Element that triggers removal (e.g., a close button)"
183
183
  },
184
184
  {
185
- name: "confirm",
185
+ name: "confirmRemoval",
186
186
  type: "boolean",
187
187
  optional: true,
188
188
  default: false,
@@ -202,15 +202,18 @@ var init_removable_schema = __esm({
202
202
  { name: "removable:removed", description: "Fired after removal" }
203
203
  ],
204
204
  source: `
205
- behavior Removable(triggerEl, confirm, effect)
205
+ behavior Removable(triggerEl, confirmRemoval, effect)
206
206
  init
207
207
  if triggerEl is undefined
208
208
  set triggerEl to me
209
209
  end
210
210
  end
211
211
  on click from triggerEl
212
- if confirm
213
- if not window.confirm("Are you sure?")
212
+ if confirmRemoval
213
+ js(me)
214
+ if (!window.confirm("Are you sure?")) return "cancel";
215
+ end
216
+ if it is "cancel"
214
217
  halt
215
218
  end
216
219
  end
@@ -311,9 +314,12 @@ behavior Toggleable(cls, target)
311
314
  end
312
315
  on click
313
316
  toggle .{cls} on target
317
+ js(target, cls)
318
+ var eventName = target.classList.contains(cls) ? 'toggleable:on' : 'toggleable:off';
319
+ target.dispatchEvent(new CustomEvent(eventName, { bubbles: true }));
320
+ end
314
321
  end
315
- end
316
- `.trim()
322
+ end`.trim()
317
323
  };
318
324
  }
319
325
  });
@@ -420,27 +426,33 @@ var init_sortable_schema = __esm({
420
426
  source: `
421
427
  behavior Sortable(handle, dragClass)
422
428
  init
423
- if no dragClass set dragClass to "sorting"
429
+ if dragClass is undefined
430
+ set dragClass to "sorting"
431
+ end
424
432
  end
425
- on pointerdown(target, clientY) from me
426
- set item to closest <li/> in target
427
- if no item exit
428
- if handle
429
- set handleEl to target.closest(handle)
430
- if no handleEl exit
433
+ on pointerdown(clientY) from me
434
+ js(me, handle, event)
435
+ var target = event.target;
436
+ var item = target.closest("li");
437
+ if (!item || !me.contains(item)) return false;
438
+ if (handle && !target.closest(handle)) return false;
439
+ event.preventDefault();
440
+ event.stopPropagation();
441
+ return true;
431
442
  end
432
- halt the event
433
- add dragClass to item
443
+ if it is false
444
+ exit
445
+ end
446
+ add .{dragClass} to me
434
447
  trigger sortable:start on me
435
448
  repeat until event pointerup from document
436
449
  wait for pointermove(clientY) from document
437
450
  trigger sortable:move on me
438
451
  end
439
- remove dragClass from item
452
+ remove .{dragClass} from me
440
453
  trigger sortable:end on me
441
454
  end
442
- end
443
- `.trim()
455
+ end`.trim()
444
456
  };
445
457
  }
446
458
  });
@@ -577,11 +589,21 @@ var init_resizable_schema = __esm({
577
589
  source: `
578
590
  behavior Resizable(handle, minWidth, minHeight, maxWidth, maxHeight)
579
591
  init
580
- if no handle set handle to me
581
- if no minWidth set minWidth to 50
582
- if no minHeight set minHeight to 50
583
- if no maxWidth set maxWidth to 9999
584
- if no maxHeight set maxHeight to 9999
592
+ if handle is undefined
593
+ set handle to me
594
+ end
595
+ if minWidth is undefined
596
+ set minWidth to 50
597
+ end
598
+ if minHeight is undefined
599
+ set minHeight to 50
600
+ end
601
+ if maxWidth is undefined
602
+ set maxWidth to 9999
603
+ end
604
+ if maxHeight is undefined
605
+ set maxHeight to 9999
606
+ end
585
607
  end
586
608
  on pointerdown(clientX, clientY) from handle
587
609
  halt the event
@@ -593,15 +615,13 @@ behavior Resizable(handle, minWidth, minHeight, maxWidth, maxHeight)
593
615
  set startX to clientX
594
616
  set startY to clientY
595
617
  repeat until event pointerup from document
596
- wait for pointermove(clientX, clientY) or
597
- pointerup from document
598
- set newWidth to startWidth + (clientX - startX)
599
- set newHeight to startHeight + (clientY - startY)
600
- if newWidth < minWidth set newWidth to minWidth
601
- if newWidth > maxWidth set newWidth to maxWidth
602
- if newHeight < minHeight set newHeight to minHeight
603
- if newHeight > maxHeight set newHeight to maxHeight
604
- add { width: \${newWidth}px; height: \${newHeight}px; }
618
+ wait for pointermove(clientX, clientY) or pointerup from document
619
+ js(startWidth, startHeight, clientX, clientY, startX, startY, minWidth, maxWidth, minHeight, maxHeight, me)
620
+ var nw = Math.max(minWidth, Math.min(maxWidth, startWidth + (clientX - startX)));
621
+ var nh = Math.max(minHeight, Math.min(maxHeight, startHeight + (clientY - startY)));
622
+ me.style.width = nw + 'px';
623
+ me.style.height = nh + 'px';
624
+ end
605
625
  trigger resizable:resize
606
626
  end
607
627
  trigger resizable:end
@@ -740,27 +760,49 @@ var init_clipboard_schema = __esm({
740
760
  "navigator.clipboard.writeText() requires HTTPS or localhost",
741
761
  'Falls back to document.execCommand("copy") for older browsers'
742
762
  ],
743
- source: [
744
- "behavior Clipboard(text, source, feedback, feedbackDuration)",
745
- " init",
746
- " if feedbackDuration is undefined set feedbackDuration to 2000",
747
- " if source is undefined set source to me",
748
- " end",
749
- " on click",
750
- " if text is not undefined",
751
- " set copyText to text",
752
- " else",
753
- " set copyText to source's value or source's textContent",
754
- " end",
755
- " js(copyText) navigator.clipboard.writeText(copyText) end",
756
- " set feedbackEl to feedback or me",
757
- " add .copied to feedbackEl",
758
- " wait feedbackDuration ms",
759
- " remove .copied from feedbackEl",
760
- " trigger clipboard:copied",
761
- " end",
762
- "end"
763
- ].join("\n")
763
+ source: `
764
+ behavior Clipboard(text, source, feedback, feedbackDuration)
765
+ init
766
+ if feedbackDuration is undefined
767
+ set feedbackDuration to 2000
768
+ end
769
+ if source is undefined
770
+ set source to me
771
+ end
772
+ end
773
+ on click
774
+ if text is not undefined
775
+ set copyText to text
776
+ else
777
+ set copyText to source's textContent
778
+ end
779
+ js(me, copyText, feedback, feedbackDuration)
780
+ async function copyToClipboard(t) {
781
+ if (navigator.clipboard && navigator.clipboard.writeText) {
782
+ await navigator.clipboard.writeText(t);
783
+ return;
784
+ }
785
+ var ta = document.createElement('textarea');
786
+ ta.value = t;
787
+ ta.style.position = 'fixed';
788
+ ta.style.opacity = '0';
789
+ document.body.appendChild(ta);
790
+ ta.select();
791
+ try { document.execCommand('copy'); } finally { document.body.removeChild(ta); }
792
+ }
793
+ try {
794
+ await copyToClipboard(copyText);
795
+ var feedbackEl = feedback ? (typeof feedback === 'string' ? document.querySelector(feedback) : feedback) : me;
796
+ if (!feedbackEl) feedbackEl = me;
797
+ feedbackEl.classList.add('copied');
798
+ me.dispatchEvent(new CustomEvent('clipboard:copied', { bubbles: true, detail: { text: copyText } }));
799
+ setTimeout(function() { feedbackEl.classList.remove('copied'); }, feedbackDuration);
800
+ } catch(err) {
801
+ me.dispatchEvent(new CustomEvent('clipboard:error', { bubbles: true, detail: { error: err } }));
802
+ }
803
+ end
804
+ end
805
+ end`.trim()
764
806
  };
765
807
  }
766
808
  });
@@ -909,24 +951,60 @@ var init_autodismiss_schema = __esm({
909
951
  { name: "autodismiss:paused", description: "Fired when the timer is paused on hover" },
910
952
  { name: "autodismiss:resumed", description: "Fired when the timer resumes on mouseleave" }
911
953
  ],
912
- source: [
913
- "behavior AutoDismiss(delay, pauseOnHover, effect)",
914
- " init",
915
- " if delay is undefined set delay to 5000",
916
- " if pauseOnHover is undefined set pauseOnHover to true",
917
- ' if effect is undefined set effect to "none"',
918
- " end",
919
- " on load",
920
- " trigger autodismiss:start",
921
- " wait delay ms",
922
- " trigger autodismiss:dismissed",
923
- ' if effect is "fade"',
924
- " transition opacity to 0 over 300ms",
925
- " end",
926
- " remove me",
927
- " end",
928
- "end"
929
- ].join("\n")
954
+ source: `
955
+ behavior AutoDismiss(delay, pauseOnHover, effect)
956
+ init
957
+ if delay is undefined
958
+ set delay to 5000
959
+ end
960
+ if pauseOnHover is undefined
961
+ set pauseOnHover to true
962
+ end
963
+ if effect is undefined
964
+ set effect to "none"
965
+ end
966
+ js(me, delay, effect, pauseOnHover)
967
+ me.dispatchEvent(new CustomEvent('autodismiss:start', { bubbles: true }));
968
+ var remaining = delay;
969
+ var startedAt = Date.now();
970
+ var timerId = null;
971
+ function dismiss() {
972
+ var ev = new CustomEvent('autodismiss:dismissed', { bubbles: true, cancelable: true });
973
+ me.dispatchEvent(ev);
974
+ if (ev.defaultPrevented) return;
975
+ if (effect === 'fade') {
976
+ me.style.transition = 'opacity 300ms';
977
+ me.style.opacity = '0';
978
+ setTimeout(function() { me.remove(); }, 300);
979
+ } else {
980
+ me.remove();
981
+ }
982
+ }
983
+ function start() {
984
+ startedAt = Date.now();
985
+ timerId = setTimeout(dismiss, remaining);
986
+ }
987
+ start();
988
+ if (pauseOnHover) {
989
+ me.addEventListener('mouseenter', function() {
990
+ if (timerId != null) {
991
+ clearTimeout(timerId);
992
+ timerId = null;
993
+ remaining -= Date.now() - startedAt;
994
+ if (remaining < 0) remaining = 0;
995
+ me.dispatchEvent(new CustomEvent('autodismiss:paused', { bubbles: true }));
996
+ }
997
+ });
998
+ me.addEventListener('mouseleave', function() {
999
+ if (timerId == null && me.isConnected) {
1000
+ me.dispatchEvent(new CustomEvent('autodismiss:resumed', { bubbles: true }));
1001
+ start();
1002
+ }
1003
+ });
1004
+ }
1005
+ end
1006
+ end
1007
+ end`.trim()
930
1008
  };
931
1009
  }
932
1010
  });
@@ -1042,19 +1120,38 @@ var init_clickoutside_schema = __esm({
1042
1120
  }
1043
1121
  ],
1044
1122
  events: [
1045
- { name: "clickoutside", description: "Fired when a pointer press occurs outside the element" }
1123
+ { name: "clickoutside", description: "Fired when a pointer press occurs outside the element" },
1124
+ {
1125
+ name: "clickoutside:activate",
1126
+ description: "Listen for this event to programmatically activate detection"
1127
+ },
1128
+ {
1129
+ name: "clickoutside:deactivate",
1130
+ description: "Listen for this event to programmatically deactivate detection"
1131
+ }
1046
1132
  ],
1047
1133
  requirements: ["Uses pointerdown instead of click to avoid timing issues with removed elements"],
1048
1134
  source: `
1049
1135
  behavior ClickOutside(active)
1050
1136
  init
1051
- if active is undefined set active to true
1137
+ if active is undefined
1138
+ set active to true
1139
+ end
1052
1140
  end
1053
1141
  on pointerdown from document
1054
- if active and not me.contains(event.target)
1055
- trigger clickoutside on me
1142
+ js(me, active)
1143
+ if (!me.isConnected) return;
1144
+ if (active && !me.contains(event.target)) {
1145
+ me.dispatchEvent(new CustomEvent('clickoutside', { bubbles: true }));
1146
+ }
1056
1147
  end
1057
1148
  end
1149
+ on clickoutside:activate
1150
+ set active to true
1151
+ end
1152
+ on clickoutside:deactivate
1153
+ set active to false
1154
+ end
1058
1155
  end`.trim()
1059
1156
  };
1060
1157
  }
@@ -1167,10 +1264,51 @@ var init_focustrap_schema = __esm({
1167
1264
  source: `
1168
1265
  behavior FocusTrap(active, initialFocus, returnFocus)
1169
1266
  init
1170
- if active is undefined set active to true
1171
- if returnFocus is undefined set returnFocus to true
1267
+ if active is undefined
1268
+ set active to true
1269
+ end
1270
+ if returnFocus is undefined
1271
+ set returnFocus to true
1272
+ end
1273
+ js(me, active, initialFocus, returnFocus)
1274
+ var FOCUSABLE = 'a[href],button:not(:disabled),input:not(:disabled),select:not(:disabled),textarea:not(:disabled),[tabindex]:not([tabindex="-1"])';
1275
+ var isActive = false;
1276
+ var previouslyFocused = null;
1277
+ function getFocusable() { return Array.from(me.querySelectorAll(FOCUSABLE)); }
1278
+ function activate() {
1279
+ if (isActive) return;
1280
+ isActive = true;
1281
+ previouslyFocused = document.activeElement;
1282
+ me.setAttribute('aria-modal', 'true');
1283
+ var initEl = initialFocus ? (typeof initialFocus === 'string' ? me.querySelector(initialFocus) : initialFocus) : null;
1284
+ if (initEl) { initEl.focus(); } else { var f = getFocusable(); if (f.length) f[0].focus(); }
1285
+ me.dispatchEvent(new CustomEvent('focustrap:activated', { bubbles: true }));
1286
+ }
1287
+ function deactivate() {
1288
+ if (!isActive) return;
1289
+ isActive = false;
1290
+ me.removeAttribute('aria-modal');
1291
+ if (returnFocus && previouslyFocused instanceof HTMLElement) previouslyFocused.focus();
1292
+ previouslyFocused = null;
1293
+ me.dispatchEvent(new CustomEvent('focustrap:deactivated', { bubbles: true }));
1294
+ }
1295
+ me.addEventListener('keydown', function(e) {
1296
+ if (!isActive || e.key !== 'Tab') return;
1297
+ var focusable = getFocusable();
1298
+ if (focusable.length === 0) { e.preventDefault(); return; }
1299
+ e.preventDefault();
1300
+ var idx = focusable.indexOf(document.activeElement);
1301
+ if (e.shiftKey) {
1302
+ focusable[idx <= 0 ? focusable.length - 1 : idx - 1].focus();
1303
+ } else {
1304
+ focusable[idx >= focusable.length - 1 ? 0 : idx + 1].focus();
1305
+ }
1306
+ });
1307
+ me.addEventListener('focustrap:activate', function() { activate(); });
1308
+ me.addEventListener('focustrap:deactivate', function() { deactivate(); });
1309
+ if (active) activate();
1310
+ end
1172
1311
  end
1173
- -- Imperative installer handles Tab key trapping and focus management
1174
1312
  end`.trim()
1175
1313
  };
1176
1314
  }
@@ -1325,13 +1463,6 @@ var init_scrollreveal_schema = __esm({
1325
1463
  optional: true,
1326
1464
  default: true,
1327
1465
  description: "If true, disconnect observer after first intersection"
1328
- },
1329
- {
1330
- name: "rootMargin",
1331
- type: "string",
1332
- optional: true,
1333
- default: "0px",
1334
- description: "CSS margin around the viewport for triggering earlier or later"
1335
1466
  }
1336
1467
  ],
1337
1468
  events: [
@@ -1340,14 +1471,33 @@ var init_scrollreveal_schema = __esm({
1340
1471
  ],
1341
1472
  requirements: ["Uses IntersectionObserver API (supported in all modern browsers)"],
1342
1473
  source: `
1343
- behavior ScrollReveal(cls, threshold, once, rootMargin)
1474
+ behavior ScrollReveal(cls, threshold, once)
1344
1475
  init
1345
- if cls is undefined set cls to "revealed"
1346
- if threshold is undefined set threshold to 0.1
1347
- if once is undefined set once to true
1348
- if rootMargin is undefined set rootMargin to "0px"
1476
+ if cls is undefined
1477
+ set cls to "revealed"
1478
+ end
1479
+ if threshold is undefined
1480
+ set threshold to 0.1
1481
+ end
1482
+ if once is undefined
1483
+ set once to true
1484
+ end
1485
+ js(me, cls, threshold, once)
1486
+ var obs = new IntersectionObserver(function(entries) {
1487
+ for (var i = 0; i < entries.length; i++) {
1488
+ if (entries[i].isIntersecting) {
1489
+ me.classList.add(cls);
1490
+ me.dispatchEvent(new CustomEvent('scrollreveal:enter', { bubbles: true }));
1491
+ if (once) obs.disconnect();
1492
+ } else if (!once) {
1493
+ me.classList.remove(cls);
1494
+ me.dispatchEvent(new CustomEvent('scrollreveal:exit', { bubbles: true }));
1495
+ }
1496
+ }
1497
+ }, { threshold: threshold });
1498
+ obs.observe(me);
1499
+ end
1349
1500
  end
1350
- -- Imperative installer handles IntersectionObserver setup
1351
1501
  end`.trim()
1352
1502
  };
1353
1503
  }
@@ -1476,17 +1626,86 @@ var init_tabs_schema = __esm({
1476
1626
  "Manages aria-selected, aria-controls, aria-labelledby, and tabindex",
1477
1627
  "Keyboard: Arrow keys navigate, Home/End jump to first/last, Tab exits tablist"
1478
1628
  ],
1479
- source: [
1480
- "behavior Tabs(orientation, activeTab, wrap, activeClass)",
1481
- " init",
1482
- ' if orientation is undefined set orientation to "horizontal"',
1483
- " if activeTab is undefined set activeTab to 0",
1484
- " if wrap is undefined set wrap to true",
1485
- ' if activeClass is undefined set activeClass to "active"',
1486
- " end",
1487
- " -- Imperative installer handles ARIA wiring and keyboard navigation",
1488
- "end"
1489
- ].join("\n")
1629
+ source: `
1630
+ behavior Tabs(orientation, activeTab, wrap, activeClass)
1631
+ init
1632
+ if orientation is undefined
1633
+ set orientation to "horizontal"
1634
+ end
1635
+ if activeTab is undefined
1636
+ set activeTab to 0
1637
+ end
1638
+ if wrap is undefined
1639
+ set wrap to true
1640
+ end
1641
+ if activeClass is undefined
1642
+ set activeClass to "active"
1643
+ end
1644
+ js(me, orientation, activeTab, wrap, activeClass)
1645
+ var tabs = Array.from(me.querySelectorAll('[role="tab"]'));
1646
+ var panels = Array.from(me.querySelectorAll('[role="tabpanel"]'));
1647
+ if (tabs.length === 0 || panels.length === 0) return;
1648
+ var tablist = me.querySelector('[role="tablist"]') || tabs[0].parentElement;
1649
+ if (tablist) tablist.setAttribute('aria-orientation', orientation);
1650
+ var count = Math.min(tabs.length, panels.length);
1651
+ var idN = 0;
1652
+ for (var i = 0; i < count; i++) {
1653
+ if (!tabs[i].id) tabs[i].id = 'tab-' + (++idN);
1654
+ if (!panels[i].id) panels[i].id = 'tabpanel-' + idN;
1655
+ tabs[i].setAttribute('aria-controls', panels[i].id);
1656
+ panels[i].setAttribute('aria-labelledby', tabs[i].id);
1657
+ }
1658
+ var cur = Math.max(0, Math.min(activeTab, count - 1));
1659
+ function activate(idx, prev) {
1660
+ if (idx === prev || idx < 0 || idx >= count) return false;
1661
+ var ev = new CustomEvent('tabs:change', { bubbles: true, cancelable: true, detail: { index: idx, previousIndex: prev } });
1662
+ me.dispatchEvent(ev);
1663
+ if (ev.defaultPrevented) return false;
1664
+ if (prev >= 0 && prev < count) {
1665
+ tabs[prev].setAttribute('aria-selected', 'false');
1666
+ tabs[prev].setAttribute('tabindex', '-1');
1667
+ tabs[prev].classList.remove(activeClass);
1668
+ panels[prev].setAttribute('aria-hidden', 'true');
1669
+ panels[prev].classList.remove(activeClass);
1670
+ }
1671
+ tabs[idx].setAttribute('aria-selected', 'true');
1672
+ tabs[idx].setAttribute('tabindex', '0');
1673
+ tabs[idx].classList.add(activeClass);
1674
+ panels[idx].setAttribute('aria-hidden', 'false');
1675
+ panels[idx].classList.add(activeClass);
1676
+ cur = idx;
1677
+ me.dispatchEvent(new CustomEvent('tabs:changed', { bubbles: true, detail: { index: idx } }));
1678
+ return true;
1679
+ }
1680
+ for (var j = 0; j < count; j++) {
1681
+ tabs[j].setAttribute('aria-selected', 'false');
1682
+ tabs[j].setAttribute('tabindex', '-1');
1683
+ panels[j].setAttribute('aria-hidden', 'true');
1684
+ panels[j].classList.remove(activeClass);
1685
+ }
1686
+ tabs[cur].setAttribute('aria-selected', 'true');
1687
+ tabs[cur].setAttribute('tabindex', '0');
1688
+ tabs[cur].classList.add(activeClass);
1689
+ panels[cur].setAttribute('aria-hidden', 'false');
1690
+ panels[cur].classList.add(activeClass);
1691
+ for (var k = 0; k < count; k++) {
1692
+ (function(idx) {
1693
+ tabs[idx].addEventListener('click', function() { if (activate(idx, cur)) tabs[idx].focus(); });
1694
+ })(k);
1695
+ }
1696
+ (tablist || me).addEventListener('keydown', function(e) {
1697
+ var nk = orientation === 'horizontal' ? 'ArrowRight' : 'ArrowDown';
1698
+ var pk = orientation === 'horizontal' ? 'ArrowLeft' : 'ArrowUp';
1699
+ var next = null;
1700
+ if (e.key === nk) { next = cur + 1; if (next >= count) next = wrap ? 0 : null; }
1701
+ else if (e.key === pk) { next = cur - 1; if (next < 0) next = wrap ? count - 1 : null; }
1702
+ else if (e.key === 'Home') next = 0;
1703
+ else if (e.key === 'End') next = count - 1;
1704
+ if (next !== null) { e.preventDefault(); if (activate(next, cur)) tabs[next].focus(); }
1705
+ });
1706
+ end
1707
+ end
1708
+ end`.trim()
1490
1709
  };
1491
1710
  }
1492
1711
  });