abledom 0.5.0 → 0.6.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.
package/dist/index.js CHANGED
@@ -31,6 +31,9 @@ __export(index_exports, {
31
31
  FindElementRule: () => FindElementRule,
32
32
  FocusLostRule: () => FocusLostRule,
33
33
  FocusableElementLabelRule: () => FocusableElementLabelRule,
34
+ NestedInteractiveElementRule: () => NestedInteractiveElementRule,
35
+ RequiredParentRule: () => RequiredParentRule,
36
+ TabIndexRule: () => TabIndexRule,
34
37
  ValidationRule: () => ValidationRule,
35
38
  ValidationRuleType: () => ValidationRuleType,
36
39
  hasAccessibilityAttribute: () => hasAccessibilityAttribute,
@@ -97,7 +100,10 @@ var ValidationRule = class {
97
100
  };
98
101
 
99
102
  // inline-file:/Users/marata/Documents/Work/abledom/src/ui/ui.css
100
- var ui_default = "#abledom-report {\n bottom: 20px;\n display: flex;\n flex-direction: column;\n left: 10px;\n max-height: 80%;\n max-width: 60%;\n padding: 4px 8px;\n position: fixed;\n z-index: 100500;\n}\n\n#abledom-report :focus-visible {\n outline: 3px solid red;\n mix-blend-mode: difference;\n}\n\n#abledom-report.abledom-align-left {\n left: 10px;\n right: auto;\n}\n\n#abledom-report.abledom-align-right {\n left: auto;\n right: 10px;\n}\n\n#abledom-report.abledom-align-bottom {\n bottom: 20px;\n top: auto;\n}\n\n#abledom-report.abledom-align-top {\n /* flex-direction: column-reverse; */\n bottom: auto;\n top: 10px;\n}\n\n.abledom-menu-container {\n backdrop-filter: blur(3px);\n border-radius: 8px;\n box-shadow: 0px 0px 4px rgba(127, 127, 127, 0.5);\n display: inline-block;\n margin: 2px auto 2px 0;\n}\n\n#abledom-report.abledom-align-right .abledom-menu-container {\n margin: 2px 0 2px auto;\n}\n\n.abledom-menu {\n background-color: rgba(140, 10, 121, 0.7);\n border-radius: 8px;\n color: white;\n display: inline flex;\n font-family: Arial, Helvetica, sans-serif;\n font-size: 16px;\n line-height: 26px;\n padding: 4px;\n}\n\n.abledom-menu .issues-count {\n margin: 0 8px;\n display: inline-block;\n}\n\n.abledom-menu .button {\n all: unset;\n color: #000;\n cursor: pointer;\n background: linear-gradient(\n 180deg,\n rgba(255, 255, 255, 1) 0%,\n rgba(200, 200, 200, 1) 100%\n );\n border-radius: 6px;\n border: 1px solid rgba(255, 255, 255, 0.4);\n box-sizing: border-box;\n line-height: 0px;\n margin-right: 4px;\n max-height: 26px;\n padding: 2px;\n text-decoration: none;\n}\n\n.abledom-menu .align-button {\n border-right-color: rgba(0, 0, 0, 0.4);\n border-radius: 0;\n margin: 0;\n}\n\n.abledom-menu .align-button:active,\n#abledom-report .pressed {\n background: linear-gradient(\n 180deg,\n rgba(130, 130, 130, 1) 0%,\n rgba(180, 180, 180, 1) 100%\n );\n}\n\n.abledom-menu .align-button-first {\n border-top-left-radius: 6px;\n border-bottom-left-radius: 6px;\n margin-left: 8px;\n}\n.abledom-menu .align-button-last {\n border-top-right-radius: 6px;\n border-bottom-right-radius: 6px;\n border-right-color: rgba(255, 255, 255, 0.4);\n}\n\n.abledom-issues-container {\n overflow: auto;\n max-height: calc(100vh - 100px);\n}\n\n#abledom-report.abledom-align-right .abledom-issues-container {\n text-align: right;\n}\n\n.abledom-issue-container {\n backdrop-filter: blur(3px);\n border-radius: 8px;\n box-shadow: 0px 0px 4px rgba(127, 127, 127, 0.5);\n display: inline-flex;\n margin: 2px 0;\n}\n\n.abledom-issue {\n background-color: rgba(164, 2, 2, 0.7);\n border-radius: 8px;\n color: white;\n display: inline flex;\n font-family: Arial, Helvetica, sans-serif;\n font-size: 16px;\n line-height: 26px;\n padding: 4px;\n}\n.abledom-issue_warning {\n background-color: rgba(163, 82, 1, 0.7);\n}\n.abledom-issue_info {\n background-color: rgba(0, 0, 255, 0.7);\n}\n\n.abledom-issue .button {\n all: unset;\n color: #000;\n cursor: pointer;\n background: linear-gradient(\n 180deg,\n rgba(255, 255, 255, 1) 0%,\n rgba(200, 200, 200, 1) 100%\n );\n border-radius: 6px;\n border: 1px solid rgba(255, 255, 255, 0.4);\n box-sizing: border-box;\n line-height: 0px;\n margin-right: 4px;\n max-height: 26px;\n padding: 2px;\n text-decoration: none;\n}\n\n.abledom-issue .button:hover {\n opacity: 0.7;\n}\n\n.abledom-issue .button.close {\n background: none;\n border-color: transparent;\n color: #fff;\n margin: 0;\n}\n\n.abledom-highlight {\n background-color: yellow;\n box-sizing: border-box;\n display: none;\n opacity: 0.6;\n position: fixed;\n z-index: 100499;\n}\n\n.abledom-highlight-border1 {\n border-top: 2px solid red;\n border-bottom: 2px solid red;\n box-sizing: border-box;\n position: absolute;\n top: -2px;\n width: calc(100% + 20px);\n height: calc(100% + 4px);\n margin: 0 -10px;\n}\n\n.abledom-highlight-border2 {\n border-left: 2px solid red;\n border-right: 2px solid red;\n box-sizing: border-box;\n position: absolute;\n width: calc(100% + 4px);\n left: -2px;\n height: calc(100% + 20px);\n margin: -10px 0;\n}\n";
103
+ var ui_default = "#abledom-report {\n bottom: 20px;\n display: flex;\n flex-direction: column;\n left: 10px;\n max-height: 80%;\n max-width: 60%;\n padding: 4px 8px;\n position: fixed;\n z-index: 100500;\n}\n\n#abledom-report :focus-visible {\n outline: 3px solid red;\n mix-blend-mode: difference;\n}\n\n#abledom-report.abledom-align-left {\n left: 10px;\n right: auto;\n}\n\n#abledom-report.abledom-align-right {\n left: auto;\n right: 10px;\n}\n\n#abledom-report.abledom-align-bottom {\n bottom: 20px;\n top: auto;\n}\n\n#abledom-report.abledom-align-top {\n /* flex-direction: column-reverse; */\n bottom: auto;\n top: 10px;\n}\n\n.abledom-menu-container {\n backdrop-filter: blur(3px);\n border-radius: 8px;\n box-shadow: 0px 0px 4px rgba(127, 127, 127, 0.5);\n display: inline-block;\n margin: 2px auto 2px 0;\n}\n\n#abledom-report.abledom-align-right .abledom-menu-container {\n margin: 2px 0 2px auto;\n}\n\n.abledom-menu {\n background-color: rgba(140, 10, 121, 0.7);\n border-radius: 8px;\n color: white;\n display: inline flex;\n font-family: Arial, Helvetica, sans-serif;\n font-size: 16px;\n line-height: 26px;\n padding: 4px;\n}\n\n.abledom-menu .issues-count {\n margin: 0 8px;\n display: inline-block;\n}\n\n.abledom-menu .button {\n all: unset;\n color: #000;\n cursor: pointer;\n background: linear-gradient(\n 180deg,\n rgba(255, 255, 255, 1) 0%,\n rgba(200, 200, 200, 1) 100%\n );\n border-radius: 6px;\n border: 1px solid rgba(255, 255, 255, 0.4);\n box-sizing: border-box;\n line-height: 0px;\n margin-right: 4px;\n max-height: 26px;\n padding: 2px;\n text-decoration: none;\n}\n\n.abledom-menu .align-button {\n border-right-color: rgba(0, 0, 0, 0.4);\n border-radius: 0;\n margin: 0;\n}\n\n.abledom-menu .align-button:active,\n#abledom-report .pressed {\n background: linear-gradient(\n 180deg,\n rgba(130, 130, 130, 1) 0%,\n rgba(180, 180, 180, 1) 100%\n );\n}\n\n.abledom-menu .align-button-first {\n border-top-left-radius: 6px;\n border-bottom-left-radius: 6px;\n margin-left: 8px;\n}\n.abledom-menu .align-button-last {\n border-top-right-radius: 6px;\n border-bottom-right-radius: 6px;\n border-right-color: rgba(255, 255, 255, 0.4);\n}\n\n.abledom-issues-container {\n overflow: auto;\n max-height: calc(100vh - 100px);\n}\n\n#abledom-report.abledom-align-right .abledom-issues-container {\n text-align: right;\n}\n\n.abledom-issue-container {\n backdrop-filter: blur(3px);\n border-radius: 8px;\n box-shadow: 0px 0px 4px rgba(127, 127, 127, 0.5);\n display: inline-flex;\n margin: 2px 0;\n}\n\n.abledom-issue {\n background-color: rgba(164, 2, 2, 0.7);\n border-radius: 8px;\n color: white;\n display: inline flex;\n font-family: Arial, Helvetica, sans-serif;\n font-size: 16px;\n line-height: 26px;\n padding: 4px;\n}\n.abledom-issue_warning {\n background-color: rgba(163, 82, 1, 0.7);\n}\n.abledom-issue_info {\n background-color: rgba(0, 0, 255, 0.7);\n}\n\n.abledom-issue .button {\n all: unset;\n color: #000;\n cursor: pointer;\n background: linear-gradient(\n 180deg,\n rgba(255, 255, 255, 1) 0%,\n rgba(200, 200, 200, 1) 100%\n );\n border-radius: 6px;\n border: 1px solid rgba(255, 255, 255, 0.4);\n box-sizing: border-box;\n line-height: 0px;\n margin-right: 4px;\n max-height: 26px;\n padding: 2px;\n text-decoration: none;\n}\n\n.abledom-issue .button:hover {\n opacity: 0.7;\n}\n\n.abledom-issue .button.close {\n background: none;\n border-color: transparent;\n color: #fff;\n margin: 0;\n}\n";
104
+
105
+ // inline-file:/Users/marata/Documents/Work/abledom/src/ui/highlighter.css
106
+ var highlighter_default = ".abledom-highlight {\n background-color: yellow;\n box-sizing: border-box;\n display: none;\n opacity: 0.6;\n position: fixed;\n z-index: 100499;\n}\n\n.abledom-highlight-border1 {\n border-top: 2px solid red;\n border-bottom: 2px solid red;\n box-sizing: border-box;\n position: absolute;\n top: -2px;\n width: calc(100% + 20px);\n height: calc(100% + 4px);\n margin: 0 -10px;\n}\n\n.abledom-highlight-border2 {\n border-left: 2px solid red;\n border-right: 2px solid red;\n box-sizing: border-box;\n position: absolute;\n width: calc(100% + 4px);\n left: -2px;\n height: calc(100% + 20px);\n margin: -10px 0;\n}\n";
101
107
 
102
108
  // src/ui/domBuilder.ts
103
109
  var DOMBuilder = class {
@@ -403,8 +409,7 @@ var IssueUI = class {
403
409
  if (element2) {
404
410
  revealButton.onclick = () => {
405
411
  var _a;
406
- element2.scrollIntoView({ block: "center" });
407
- (_a = this._issuesUI) == null ? void 0 : _a.highlight(element2);
412
+ (_a = this._issuesUI) == null ? void 0 : _a.highlight(element2, true);
408
413
  };
409
414
  } else {
410
415
  revealButton.style.display = "none";
@@ -481,7 +486,8 @@ var IssueUI = class {
481
486
  }
482
487
  };
483
488
  var IssuesUI = class {
484
- constructor(win, props) {
489
+ constructor(win, getHighlighter, props) {
490
+ __publicField(this, "_win");
485
491
  __publicField(this, "_container");
486
492
  __publicField(this, "_issuesContainer");
487
493
  __publicField(this, "_menuElement");
@@ -494,9 +500,11 @@ var IssuesUI = class {
494
500
  __publicField(this, "_alignBottomRightButton");
495
501
  __publicField(this, "_isMuted", false);
496
502
  __publicField(this, "_issues", /* @__PURE__ */ new Set());
497
- __publicField(this, "_highlighter");
503
+ __publicField(this, "_getHighlighter");
498
504
  __publicField(this, "bugReport");
499
505
  __publicField(this, "headless");
506
+ this._win = win;
507
+ this._getHighlighter = getHighlighter;
500
508
  this.bugReport = props.bugReport;
501
509
  this.headless = !!props.headless;
502
510
  if (this.headless) {
@@ -617,7 +625,6 @@ var IssuesUI = class {
617
625
  }
618
626
  ).element(alignbottomright_default).closeTag().closeTag();
619
627
  doc.body.appendChild(container);
620
- this._highlighter = new ElementHighlighter(win);
621
628
  }
622
629
  setUIAlignment(alignment) {
623
630
  var _a, _b, _c, _d, _e, _f, _g, _h;
@@ -739,15 +746,15 @@ var IssuesUI = class {
739
746
  });
740
747
  this._setShowHideButtonsVisibility();
741
748
  }
742
- highlight(element) {
743
- var _a;
744
- (_a = this._highlighter) == null ? void 0 : _a.highlight(element);
749
+ highlight(element, scrollIntoView, autoHideTime) {
750
+ var _a, _b;
751
+ (_b = (_a = this._getHighlighter) == null ? void 0 : _a.call(this)) == null ? void 0 : _b.highlight(element, scrollIntoView, autoHideTime);
745
752
  }
746
753
  dispose() {
747
- var _a, _b;
748
- (_a = this._highlighter) == null ? void 0 : _a.dispose();
749
- (_b = this._container) == null ? void 0 : _b.remove();
750
- delete this._highlighter;
754
+ var _a;
755
+ (_a = this._container) == null ? void 0 : _a.remove();
756
+ delete this._win;
757
+ delete this._getHighlighter;
751
758
  delete this._container;
752
759
  delete this._issuesContainer;
753
760
  delete this._menuElement;
@@ -767,6 +774,7 @@ var ElementHighlighter = class {
767
774
  __publicField(this, "_element");
768
775
  __publicField(this, "_cancelScrollTimer");
769
776
  __publicField(this, "_intersectionObserver");
777
+ __publicField(this, "_cancelAutoHideTimer");
770
778
  __publicField(this, "_onScroll", () => {
771
779
  var _a;
772
780
  (_a = this._cancelScrollTimer) == null ? void 0 : _a.call(this);
@@ -787,10 +795,19 @@ var ElementHighlighter = class {
787
795
  const container = this._container = win.document.createElement("div");
788
796
  container.__abledomui = true;
789
797
  container.className = "abledom-highlight";
798
+ const doc = win.document;
799
+ const style = doc.createElement("style");
800
+ style.type = "text/css";
801
+ style.appendChild(doc.createTextNode(highlighter_default));
802
+ container.appendChild(style);
790
803
  new DOMBuilder(container).openTag("div", { class: "abledom-highlight-border1" }).closeTag().openTag("div", { class: "abledom-highlight-border2" }).closeTag();
791
804
  win.addEventListener("scroll", this._onScroll, true);
792
805
  }
793
- highlight(element) {
806
+ highlight(element, scrollIntoView, autoHideTime) {
807
+ var _a;
808
+ if (this._cancelAutoHideTimer && element !== this._element) {
809
+ this._cancelAutoHideTimer();
810
+ }
794
811
  if (!element) {
795
812
  delete this._element;
796
813
  this._unobserve();
@@ -803,6 +820,22 @@ var ElementHighlighter = class {
803
820
  return;
804
821
  }
805
822
  this._element = element;
823
+ if (scrollIntoView) {
824
+ element.scrollIntoView({ block: "center" });
825
+ }
826
+ if (autoHideTime) {
827
+ (_a = this._cancelAutoHideTimer) == null ? void 0 : _a.call(this);
828
+ const autoHideTimeout = win.setTimeout(() => {
829
+ if (this._cancelAutoHideTimer) {
830
+ this.highlight(null);
831
+ delete this._cancelAutoHideTimer;
832
+ }
833
+ }, autoHideTime);
834
+ this._cancelAutoHideTimer = () => {
835
+ win.clearTimeout(autoHideTimeout);
836
+ delete this._cancelAutoHideTimer;
837
+ };
838
+ }
806
839
  this._intersectionObserver = new IntersectionObserver(([entry]) => {
807
840
  if (entry) {
808
841
  const rect = entry.boundingClientRect;
@@ -821,11 +854,12 @@ var ElementHighlighter = class {
821
854
  this._intersectionObserver.observe(element);
822
855
  }
823
856
  dispose() {
824
- var _a, _b, _c;
857
+ var _a, _b, _c, _d;
825
858
  this._unobserve();
826
859
  (_a = this._cancelScrollTimer) == null ? void 0 : _a.call(this);
827
- (_b = this._window) == null ? void 0 : _b.removeEventListener("scroll", this._onScroll, true);
828
- (_c = this._container) == null ? void 0 : _c.remove();
860
+ (_b = this._cancelAutoHideTimer) == null ? void 0 : _b.call(this);
861
+ (_c = this._window) == null ? void 0 : _c.removeEventListener("scroll", this._onScroll, true);
862
+ (_d = this._container) == null ? void 0 : _d.remove();
829
863
  delete this._element;
830
864
  delete this._container;
831
865
  delete this._window;
@@ -868,10 +902,17 @@ var AccessibilityAffectingElements = {
868
902
  aside: true,
869
903
  body: true,
870
904
  button: true,
905
+ caption: true,
906
+ col: true,
907
+ colgroup: true,
871
908
  datalist: true,
909
+ dd: true,
872
910
  details: true,
873
911
  dialog: true,
874
912
  dl: true,
913
+ dt: true,
914
+ figcaption: true,
915
+ figure: true,
875
916
  form: true,
876
917
  h1: true,
877
918
  h2: true,
@@ -883,6 +924,7 @@ var AccessibilityAffectingElements = {
883
924
  iframe: true,
884
925
  img: true,
885
926
  input: true,
927
+ legend: true,
886
928
  li: true,
887
929
  link: true,
888
930
  main: true,
@@ -892,15 +934,22 @@ var AccessibilityAffectingElements = {
892
934
  nav: true,
893
935
  object: true,
894
936
  ol: true,
937
+ optgroup: true,
895
938
  option: true,
896
939
  progress: true,
897
940
  section: true,
898
941
  select: true,
942
+ source: true,
943
+ summary: true,
944
+ table: true,
899
945
  tbody: true,
946
+ td: true,
900
947
  textarea: true,
901
948
  tfoot: true,
902
949
  th: true,
903
950
  thead: true,
951
+ tr: true,
952
+ track: true,
904
953
  ul: true
905
954
  };
906
955
  var AccessibilityAttributes = {
@@ -1033,6 +1082,7 @@ function getStackTrace() {
1033
1082
  var AbleDOM = class {
1034
1083
  constructor(win, props = {}) {
1035
1084
  __publicField(this, "_win");
1085
+ __publicField(this, "_isDisposed", false);
1036
1086
  __publicField(this, "_props");
1037
1087
  __publicField(this, "_observer");
1038
1088
  __publicField(this, "_clearValidationTimeout");
@@ -1045,8 +1095,17 @@ var AbleDOM = class {
1045
1095
  __publicField(this, "_startFunc");
1046
1096
  __publicField(this, "_isStarted", false);
1047
1097
  __publicField(this, "_issuesUI");
1098
+ __publicField(this, "_elementHighlighter");
1048
1099
  __publicField(this, "_idlePromise");
1049
1100
  __publicField(this, "_idleResolve");
1101
+ __publicField(this, "_currentAnchoredIssues", /* @__PURE__ */ new Map());
1102
+ __publicField(this, "_currentNotAnchoredIssues", []);
1103
+ __publicField(this, "_getHighlighter", () => {
1104
+ if (!this._elementHighlighter && !this._isDisposed) {
1105
+ this._elementHighlighter = new ElementHighlighter(this._win);
1106
+ }
1107
+ return this._elementHighlighter;
1108
+ });
1050
1109
  __publicField(this, "_onFocusIn", (event) => {
1051
1110
  var _a;
1052
1111
  const target = event.target;
@@ -1214,9 +1273,9 @@ var AbleDOM = class {
1214
1273
  }
1215
1274
  }
1216
1275
  _addIssue(rule, issue) {
1217
- var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k;
1276
+ var _a, _b;
1218
1277
  if (!this._issuesUI) {
1219
- this._issuesUI = new IssuesUI(this._win, {
1278
+ this._issuesUI = new IssuesUI(this._win, this._getHighlighter, {
1220
1279
  bugReport: (_a = this._props) == null ? void 0 : _a.bugReport,
1221
1280
  headless: (_b = this._props) == null ? void 0 : _b.headless
1222
1281
  });
@@ -1242,26 +1301,21 @@ var AbleDOM = class {
1242
1301
  issueUI = new IssueUI(this._win, this, rule, this._issuesUI);
1243
1302
  issues.set(rule, issueUI);
1244
1303
  justUpdate = false;
1245
- (_e = (_d = (_c = this._props) == null ? void 0 : _c.callbacks) == null ? void 0 : _d.onIssueAdded) == null ? void 0 : _e.call(_d, element, rule, issue);
1304
+ this._onIssueAdded(element, rule, issue);
1246
1305
  }
1247
1306
  this._elementsWithIssues.add(element);
1248
1307
  } else {
1249
1308
  issueUI = new IssueUI(this._win, this, rule, this._issuesUI);
1250
1309
  justUpdate = false;
1251
- (_h = (_g = (_f = this._props) == null ? void 0 : _f.callbacks) == null ? void 0 : _g.onIssueAdded) == null ? void 0 : _h.call(_g, null, rule, issue);
1310
+ this._onIssueAdded(null, rule, issue);
1252
1311
  }
1253
1312
  issueUI.update(issue);
1254
- if (justUpdate) {
1255
- (_k = (_j = (_i = this._props) == null ? void 0 : _i.callbacks) == null ? void 0 : _j.onIssueUpdated) == null ? void 0 : _k.call(
1256
- _j,
1257
- rule.anchored && element ? element : null,
1258
- rule,
1259
- issue
1260
- );
1313
+ if (justUpdate && rule.anchored && element) {
1314
+ this._onIssueUpdated(element, rule, issue);
1261
1315
  }
1262
1316
  }
1263
1317
  _removeIssue(element, rule) {
1264
- var _a, _b, _c, _d;
1318
+ var _a;
1265
1319
  if (!rule.anchored) {
1266
1320
  return;
1267
1321
  }
@@ -1273,7 +1327,7 @@ var AbleDOM = class {
1273
1327
  if (issue) {
1274
1328
  issue.dispose();
1275
1329
  issues.delete(rule);
1276
- (_d = (_c = (_b = this._props) == null ? void 0 : _b.callbacks) == null ? void 0 : _c.onIssueRemoved) == null ? void 0 : _d.call(_c, element, rule);
1330
+ this._onIssueRemoved(element, rule);
1277
1331
  }
1278
1332
  if (issues.size === 0) {
1279
1333
  this._elementsWithIssues.delete(element);
@@ -1359,6 +1413,42 @@ var AbleDOM = class {
1359
1413
  this._dependantIdsByElement.set(element, dependsOnIds);
1360
1414
  }
1361
1415
  }
1416
+ _updateCurrentAnchoredIssues(element, rule, issue) {
1417
+ let issuesByElement = this._currentAnchoredIssues.get(element);
1418
+ if (!issuesByElement && issue) {
1419
+ issuesByElement = /* @__PURE__ */ new Map();
1420
+ this._currentAnchoredIssues.set(element, issuesByElement);
1421
+ }
1422
+ if (issuesByElement) {
1423
+ if (issue) {
1424
+ issuesByElement.set(rule, issue);
1425
+ } else {
1426
+ issuesByElement.delete(rule);
1427
+ if (issuesByElement.size === 0) {
1428
+ this._currentAnchoredIssues.delete(element);
1429
+ }
1430
+ }
1431
+ }
1432
+ }
1433
+ _onIssueAdded(element, rule, issue) {
1434
+ var _a, _b, _c;
1435
+ if (element) {
1436
+ this._updateCurrentAnchoredIssues(element, rule, issue);
1437
+ } else {
1438
+ this._currentNotAnchoredIssues.push(issue);
1439
+ }
1440
+ (_c = (_b = (_a = this._props) == null ? void 0 : _a.callbacks) == null ? void 0 : _b.onIssueAdded) == null ? void 0 : _c.call(_b, element, rule, issue);
1441
+ }
1442
+ _onIssueUpdated(element, rule, issue) {
1443
+ var _a, _b, _c;
1444
+ this._updateCurrentAnchoredIssues(element, rule, issue);
1445
+ (_c = (_b = (_a = this._props) == null ? void 0 : _a.callbacks) == null ? void 0 : _b.onIssueUpdated) == null ? void 0 : _c.call(_b, element, rule, issue);
1446
+ }
1447
+ _onIssueRemoved(element, rule) {
1448
+ var _a, _b, _c;
1449
+ this._updateCurrentAnchoredIssues(element, rule, null);
1450
+ (_c = (_b = (_a = this._props) == null ? void 0 : _a.callbacks) == null ? void 0 : _b.onIssueRemoved) == null ? void 0 : _c.call(_b, element, rule);
1451
+ }
1362
1452
  _remove(elements) {
1363
1453
  elements.forEach((element) => {
1364
1454
  var _a, _b;
@@ -1366,21 +1456,42 @@ var AbleDOM = class {
1366
1456
  rules.forEach((rule) => this._removeIssue(element, rule));
1367
1457
  });
1368
1458
  }
1459
+ _getCurrentIssues() {
1460
+ const issues = this._currentNotAnchoredIssues.slice(0);
1461
+ this._currentAnchoredIssues.forEach((issueByRule) => {
1462
+ issueByRule.forEach((issue) => {
1463
+ issues.push(issue);
1464
+ });
1465
+ });
1466
+ return issues;
1467
+ }
1369
1468
  idle() {
1370
1469
  if (!this._clearValidationTimeout) {
1371
- return Promise.resolve();
1470
+ return Promise.resolve(this._getCurrentIssues());
1372
1471
  }
1373
1472
  if (!this._idlePromise) {
1374
1473
  this._idlePromise = new Promise((resolve) => {
1375
1474
  this._idleResolve = () => {
1376
1475
  delete this._idlePromise;
1377
1476
  delete this._idleResolve;
1378
- resolve();
1477
+ resolve(this._getCurrentIssues());
1379
1478
  };
1380
1479
  });
1381
1480
  }
1382
1481
  return this._idlePromise;
1383
1482
  }
1483
+ clearCurrentIssues(anchored = true, notAnchored = true) {
1484
+ if (anchored) {
1485
+ this._currentAnchoredIssues.clear();
1486
+ }
1487
+ if (notAnchored) {
1488
+ this._currentNotAnchoredIssues = [];
1489
+ }
1490
+ }
1491
+ highlightElement(element, scrollIntoView, autoHideTime) {
1492
+ var _a;
1493
+ (_a = this._getHighlighter()) == null ? void 0 : _a.highlight(element, scrollIntoView, autoHideTime);
1494
+ }
1384
1495
  addRule(rule) {
1385
1496
  this._rules.push(rule);
1386
1497
  }
@@ -1407,6 +1518,7 @@ var AbleDOM = class {
1407
1518
  }
1408
1519
  dispose() {
1409
1520
  var _a, _b, _c, _d;
1521
+ this._isDisposed = true;
1410
1522
  const doc = this._win.document;
1411
1523
  doc.addEventListener("focusin", this._onFocusIn, true);
1412
1524
  doc.addEventListener("focusout", this._onFocusOut, true);
@@ -1415,6 +1527,7 @@ var AbleDOM = class {
1415
1527
  this._dependantIdsByElement.clear();
1416
1528
  this._elementsDependingOnId.clear();
1417
1529
  this._idByElement.clear();
1530
+ this.clearCurrentIssues();
1418
1531
  (_a = this._issuesUI) == null ? void 0 : _a.dispose();
1419
1532
  delete this._issuesUI;
1420
1533
  (_b = this._clearValidationTimeout) == null ? void 0 : _b.call(this);
@@ -2006,6 +2119,475 @@ var CustomNotifyRule = class extends ValidationRule {
2006
2119
  });
2007
2120
  }
2008
2121
  };
2122
+
2123
+ // src/rules/requiredparent.ts
2124
+ var RequiredParentRule = class extends ValidationRule {
2125
+ constructor() {
2126
+ super(...arguments);
2127
+ __publicField(this, "type", 1 /* Error */);
2128
+ __publicField(this, "name", "aria-required-parent");
2129
+ __publicField(this, "anchored", true);
2130
+ __publicField(this, "parentRequirements", /* @__PURE__ */ new Map([
2131
+ [
2132
+ "LI",
2133
+ {
2134
+ allowedParents: ["UL", "OL"],
2135
+ allowedParentRoles: ["list"]
2136
+ }
2137
+ ],
2138
+ [
2139
+ "DT",
2140
+ {
2141
+ allowedParents: ["DL"],
2142
+ allowIntermediateWrappers: true,
2143
+ allowedWrappers: ["DIV"]
2144
+ }
2145
+ ],
2146
+ [
2147
+ "DD",
2148
+ {
2149
+ allowedParents: ["DL"],
2150
+ allowIntermediateWrappers: true,
2151
+ allowedWrappers: ["DIV"]
2152
+ }
2153
+ ],
2154
+ [
2155
+ "TR",
2156
+ {
2157
+ allowedParents: ["TABLE", "THEAD", "TBODY", "TFOOT"],
2158
+ allowedParentRoles: ["table", "grid", "treegrid"]
2159
+ }
2160
+ ],
2161
+ [
2162
+ "TH",
2163
+ {
2164
+ allowedParents: ["TR"],
2165
+ allowedParentRoles: ["row"]
2166
+ }
2167
+ ],
2168
+ [
2169
+ "TD",
2170
+ {
2171
+ allowedParents: ["TR"],
2172
+ allowedParentRoles: ["row"]
2173
+ }
2174
+ ],
2175
+ [
2176
+ "THEAD",
2177
+ {
2178
+ allowedParents: ["TABLE"],
2179
+ allowedParentRoles: ["table", "grid", "treegrid"]
2180
+ }
2181
+ ],
2182
+ [
2183
+ "TBODY",
2184
+ {
2185
+ allowedParents: ["TABLE"],
2186
+ allowedParentRoles: ["table", "grid", "treegrid"]
2187
+ }
2188
+ ],
2189
+ [
2190
+ "TFOOT",
2191
+ {
2192
+ allowedParents: ["TABLE"],
2193
+ allowedParentRoles: ["table", "grid", "treegrid"]
2194
+ }
2195
+ ],
2196
+ [
2197
+ "CAPTION",
2198
+ {
2199
+ allowedParents: ["TABLE"]
2200
+ }
2201
+ ],
2202
+ [
2203
+ "COLGROUP",
2204
+ {
2205
+ allowedParents: ["TABLE"]
2206
+ }
2207
+ ],
2208
+ [
2209
+ "COL",
2210
+ {
2211
+ allowedParents: ["COLGROUP"]
2212
+ }
2213
+ ],
2214
+ [
2215
+ "FIGCAPTION",
2216
+ {
2217
+ allowedParents: ["FIGURE"]
2218
+ }
2219
+ ],
2220
+ [
2221
+ "OPTION",
2222
+ {
2223
+ allowedParents: ["SELECT", "OPTGROUP", "DATALIST"]
2224
+ }
2225
+ ],
2226
+ [
2227
+ "OPTGROUP",
2228
+ {
2229
+ allowedParents: ["SELECT"]
2230
+ }
2231
+ ],
2232
+ [
2233
+ "LEGEND",
2234
+ {
2235
+ allowedParents: ["FIELDSET"]
2236
+ }
2237
+ ],
2238
+ [
2239
+ "SUMMARY",
2240
+ {
2241
+ allowedParents: ["DETAILS"]
2242
+ }
2243
+ ],
2244
+ [
2245
+ "SOURCE",
2246
+ {
2247
+ allowedParents: ["AUDIO", "VIDEO", "PICTURE"]
2248
+ }
2249
+ ],
2250
+ [
2251
+ "TRACK",
2252
+ {
2253
+ allowedParents: ["AUDIO", "VIDEO"]
2254
+ }
2255
+ ],
2256
+ [
2257
+ "role=menuitem",
2258
+ {
2259
+ allowedParentRoles: ["menu", "menubar", "group"]
2260
+ }
2261
+ ],
2262
+ [
2263
+ "role=menuitemcheckbox",
2264
+ {
2265
+ allowedParentRoles: ["menu", "menubar", "group"]
2266
+ }
2267
+ ],
2268
+ [
2269
+ "role=menuitemradio",
2270
+ {
2271
+ allowedParentRoles: ["menu", "menubar", "group"]
2272
+ }
2273
+ ],
2274
+ [
2275
+ "role=listitem",
2276
+ {
2277
+ allowedParentRoles: ["list", "group"]
2278
+ }
2279
+ ],
2280
+ [
2281
+ "role=treeitem",
2282
+ {
2283
+ allowedParentRoles: ["tree", "group"]
2284
+ }
2285
+ ],
2286
+ [
2287
+ "role=tab",
2288
+ {
2289
+ allowedParentRoles: ["tablist"]
2290
+ }
2291
+ ],
2292
+ [
2293
+ "role=row",
2294
+ {
2295
+ allowedParentRoles: ["table", "grid", "treegrid", "rowgroup"]
2296
+ }
2297
+ ],
2298
+ [
2299
+ "role=cell",
2300
+ {
2301
+ allowedParentRoles: ["row"]
2302
+ }
2303
+ ],
2304
+ [
2305
+ "role=gridcell",
2306
+ {
2307
+ allowedParentRoles: ["row"]
2308
+ }
2309
+ ],
2310
+ [
2311
+ "role=columnheader",
2312
+ {
2313
+ allowedParentRoles: ["row"]
2314
+ }
2315
+ ],
2316
+ [
2317
+ "role=rowheader",
2318
+ {
2319
+ allowedParentRoles: ["row"]
2320
+ }
2321
+ ],
2322
+ [
2323
+ "role=rowgroup",
2324
+ {
2325
+ allowedParentRoles: ["table", "grid", "treegrid"]
2326
+ }
2327
+ ],
2328
+ [
2329
+ "role=option",
2330
+ {
2331
+ allowedParentRoles: ["listbox", "group"]
2332
+ }
2333
+ ]
2334
+ ]));
2335
+ }
2336
+ accept(element) {
2337
+ const tagName = element.tagName;
2338
+ const role = element.getAttribute("role");
2339
+ if (this.parentRequirements.has(tagName)) {
2340
+ return true;
2341
+ }
2342
+ if (role && this.parentRequirements.has(`role=${role}`)) {
2343
+ return true;
2344
+ }
2345
+ return false;
2346
+ }
2347
+ validate(element) {
2348
+ const tagName = element.tagName;
2349
+ const role = element.getAttribute("role");
2350
+ let requirement;
2351
+ let identifier = "";
2352
+ if (role && this.parentRequirements.has(`role=${role}`)) {
2353
+ requirement = this.parentRequirements.get(`role=${role}`);
2354
+ identifier = `role="${role}"`;
2355
+ } else if (this.parentRequirements.has(tagName)) {
2356
+ requirement = this.parentRequirements.get(tagName);
2357
+ identifier = `<${tagName.toLowerCase()}>`;
2358
+ }
2359
+ if (!requirement) {
2360
+ return null;
2361
+ }
2362
+ if (requirement.customValidator) {
2363
+ if (requirement.customValidator(element)) {
2364
+ return null;
2365
+ } else {
2366
+ return this.createIssue(element, identifier, requirement);
2367
+ }
2368
+ }
2369
+ if (this.hasValidParent(element, requirement)) {
2370
+ return null;
2371
+ }
2372
+ return this.createIssue(element, identifier, requirement);
2373
+ }
2374
+ hasValidParent(element, requirement) {
2375
+ var _a, _b, _c;
2376
+ let parent = element.parentElement;
2377
+ let depth = 0;
2378
+ const maxDepth = requirement.allowIntermediateWrappers ? 2 : 1;
2379
+ while (parent && depth < maxDepth) {
2380
+ if ((_a = requirement.allowedParents) == null ? void 0 : _a.includes(parent.tagName)) {
2381
+ return true;
2382
+ }
2383
+ const parentRole = parent.getAttribute("role");
2384
+ if (parentRole && ((_b = requirement.allowedParentRoles) == null ? void 0 : _b.includes(parentRole))) {
2385
+ return true;
2386
+ }
2387
+ if (depth === 0 && !requirement.allowIntermediateWrappers) {
2388
+ break;
2389
+ }
2390
+ if (depth === 0 && requirement.allowIntermediateWrappers) {
2391
+ if (!((_c = requirement.allowedWrappers) == null ? void 0 : _c.includes(parent.tagName))) {
2392
+ break;
2393
+ }
2394
+ }
2395
+ parent = parent.parentElement;
2396
+ depth++;
2397
+ }
2398
+ return false;
2399
+ }
2400
+ createIssue(element, identifier, requirement) {
2401
+ var _a, _b;
2402
+ const allowedParentsText = [
2403
+ ...((_a = requirement.allowedParents) == null ? void 0 : _a.map((p) => `<${p.toLowerCase()}>`)) || [],
2404
+ ...((_b = requirement.allowedParentRoles) == null ? void 0 : _b.map((r) => `role="${r}"`)) || []
2405
+ ].join(", ");
2406
+ const message = `${identifier} must be contained by ${allowedParentsText}`;
2407
+ return {
2408
+ issue: {
2409
+ id: "aria-required-parent",
2410
+ message,
2411
+ element,
2412
+ help: "https://dequeuniversity.com/rules/axe/4.2/aria-required-parent"
2413
+ }
2414
+ };
2415
+ }
2416
+ };
2417
+
2418
+ // src/rules/nestedInteractive.ts
2419
+ var interactiveElementSelector = [
2420
+ "a[href]",
2421
+ "button",
2422
+ "input:not([type='hidden'])",
2423
+ "select",
2424
+ "textarea",
2425
+ "details",
2426
+ "audio[controls]",
2427
+ "video[controls]",
2428
+ "*[role='button']",
2429
+ "*[role='link']",
2430
+ "*[role='checkbox']",
2431
+ "*[role='radio']",
2432
+ "*[role='switch']",
2433
+ "*[role='tab']",
2434
+ "*[role='menuitem']",
2435
+ "*[role='menuitemcheckbox']",
2436
+ "*[role='menuitemradio']",
2437
+ "*[role='option']",
2438
+ "*[role='treeitem']"
2439
+ ].join(", ");
2440
+ var NestedInteractiveElementRule = class extends ValidationRule {
2441
+ constructor() {
2442
+ super(...arguments);
2443
+ __publicField(this, "type", 1 /* Error */);
2444
+ __publicField(this, "name", "NestedInteractiveElementRule");
2445
+ __publicField(this, "anchored", true);
2446
+ }
2447
+ _isAriaHidden(element) {
2448
+ return element.ownerDocument.evaluate(
2449
+ `ancestor-or-self::*[@aria-hidden = 'true' or @hidden]`,
2450
+ element,
2451
+ null,
2452
+ XPathResult.BOOLEAN_TYPE,
2453
+ null
2454
+ ).booleanValue;
2455
+ }
2456
+ _isInteractive(element) {
2457
+ return matchesSelector(element, interactiveElementSelector);
2458
+ }
2459
+ _findNestedInteractive(element) {
2460
+ const descendants = element.querySelectorAll(interactiveElementSelector);
2461
+ for (let i = 0; i < descendants.length; i++) {
2462
+ const descendant = descendants[i];
2463
+ if (this._isAriaHidden(descendant)) {
2464
+ continue;
2465
+ }
2466
+ return descendant;
2467
+ }
2468
+ return null;
2469
+ }
2470
+ accept(element) {
2471
+ return this._isInteractive(element);
2472
+ }
2473
+ validate(element) {
2474
+ if (this._isAriaHidden(element)) {
2475
+ return null;
2476
+ }
2477
+ const nestedElement = this._findNestedInteractive(element);
2478
+ if (nestedElement) {
2479
+ const elementTag = element.tagName.toLowerCase();
2480
+ const elementRole = element.getAttribute("role");
2481
+ const nestedTag = nestedElement.tagName.toLowerCase();
2482
+ const nestedRole = nestedElement.getAttribute("role");
2483
+ const elementDesc = elementRole ? `${elementTag}[role="${elementRole}"]` : elementTag;
2484
+ const nestedDesc = nestedRole ? `${nestedTag}[role="${nestedRole}"]` : nestedTag;
2485
+ return {
2486
+ issue: isElementVisible(element) ? {
2487
+ id: "nested-interactive",
2488
+ message: `Interactive element <${elementDesc}> contains a nested interactive element <${nestedDesc}>. This can confuse users and assistive technologies.`,
2489
+ element,
2490
+ rel: nestedElement,
2491
+ help: "https://dequeuniversity.com/rules/axe/4.4/nested-interactive"
2492
+ } : void 0
2493
+ };
2494
+ }
2495
+ return null;
2496
+ }
2497
+ };
2498
+
2499
+ // src/rules/tabindex.ts
2500
+ var INTERACTIVE_ELEMENTS = /* @__PURE__ */ new Set([
2501
+ "A",
2502
+ "BUTTON",
2503
+ "INPUT",
2504
+ "SELECT",
2505
+ "TEXTAREA",
2506
+ "DETAILS",
2507
+ "SUMMARY",
2508
+ "AUDIO",
2509
+ "VIDEO"
2510
+ ]);
2511
+ var INTERACTIVE_ROLES = /* @__PURE__ */ new Set([
2512
+ "button",
2513
+ "link",
2514
+ "checkbox",
2515
+ "radio",
2516
+ "textbox",
2517
+ "combobox",
2518
+ "listbox",
2519
+ "menu",
2520
+ "menubar",
2521
+ "menuitem",
2522
+ "menuitemcheckbox",
2523
+ "menuitemradio",
2524
+ "option",
2525
+ "searchbox",
2526
+ "slider",
2527
+ "spinbutton",
2528
+ "switch",
2529
+ "tab",
2530
+ "tablist",
2531
+ "tree",
2532
+ "treegrid",
2533
+ "treeitem",
2534
+ "grid",
2535
+ "gridcell"
2536
+ ]);
2537
+ var TabIndexRule = class extends ValidationRule {
2538
+ constructor() {
2539
+ super(...arguments);
2540
+ __publicField(this, "type", 2 /* Warning */);
2541
+ __publicField(this, "name", "tabindex");
2542
+ __publicField(this, "anchored", true);
2543
+ }
2544
+ accept(element) {
2545
+ return element.hasAttribute("tabindex");
2546
+ }
2547
+ isInteractiveElement(element) {
2548
+ if (INTERACTIVE_ELEMENTS.has(element.tagName)) {
2549
+ if (element.hasAttribute("disabled")) {
2550
+ return false;
2551
+ }
2552
+ if (element.tagName === "A" && !element.hasAttribute("href")) {
2553
+ return false;
2554
+ }
2555
+ return true;
2556
+ }
2557
+ const role = element.getAttribute("role");
2558
+ if (role && INTERACTIVE_ROLES.has(role)) {
2559
+ return true;
2560
+ }
2561
+ if (element.isContentEditable) {
2562
+ return true;
2563
+ }
2564
+ return false;
2565
+ }
2566
+ validate(element) {
2567
+ const tabindex = parseInt(element.getAttribute("tabindex") || "0", 10);
2568
+ if (tabindex > 0 && this.isInteractiveElement(element)) {
2569
+ return {
2570
+ issue: {
2571
+ id: "tabindex",
2572
+ message: `Avoid positive tabindex values (found: ${tabindex})`,
2573
+ element,
2574
+ help: "https://dequeuniversity.com/rules/axe/4.2/tabindex"
2575
+ }
2576
+ };
2577
+ }
2578
+ if (!this.isInteractiveElement(element)) {
2579
+ return {
2580
+ issue: {
2581
+ id: "tabindex-non-interactive",
2582
+ message: `Avoid using tabindex on non-interactive elements (<${element.tagName.toLowerCase()}>). Consider adding an interactive role or making the element naturally interactive.`,
2583
+ element,
2584
+ help: "https://dequeuniversity.com/rules/axe/4.2/tabindex"
2585
+ }
2586
+ };
2587
+ }
2588
+ return null;
2589
+ }
2590
+ };
2009
2591
  // Annotate the CommonJS export names for ESM import in node:
2010
2592
  0 && (module.exports = {
2011
2593
  AbleDOM,
@@ -2017,6 +2599,9 @@ var CustomNotifyRule = class extends ValidationRule {
2017
2599
  FindElementRule,
2018
2600
  FocusLostRule,
2019
2601
  FocusableElementLabelRule,
2602
+ NestedInteractiveElementRule,
2603
+ RequiredParentRule,
2604
+ TabIndexRule,
2020
2605
  ValidationRule,
2021
2606
  ValidationRuleType,
2022
2607
  hasAccessibilityAttribute,