abledom 0.1.0 → 0.2.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/dist/index.js CHANGED
@@ -111,6 +111,7 @@ var DOMBuilder = class {
111
111
  const parent = this._stack[0];
112
112
  const element = namespace ? (_a = this._doc) == null ? void 0 : _a.createElementNS(namespace, tagName) : (_b = this._doc) == null ? void 0 : _b.createElement(tagName);
113
113
  if (parent && element) {
114
+ element.__abledomui = true;
114
115
  if (attributes) {
115
116
  for (const [key, value] of Object.entries(attributes)) {
116
117
  if (key === "class") {
@@ -138,8 +139,12 @@ var DOMBuilder = class {
138
139
  return this;
139
140
  }
140
141
  text(text) {
141
- var _a;
142
- (_a = this._stack[0]) == null ? void 0 : _a.appendChild(document.createTextNode(text));
142
+ var _a, _b;
143
+ const textNode = (_a = this._doc) == null ? void 0 : _a.createTextNode(text);
144
+ if (textNode) {
145
+ textNode.__abledomui = true;
146
+ (_b = this._stack[0]) == null ? void 0 : _b.appendChild(textNode);
147
+ }
143
148
  return this;
144
149
  }
145
150
  element(callback) {
@@ -290,25 +295,23 @@ var alignbottomleft_default = function buildSVG11(parent) {
290
295
 
291
296
  // src/ui/ui.ts
292
297
  var pressedClass = "pressed";
293
- var _NotificationUI = class _NotificationUI {
294
- constructor(win, rule) {
298
+ var NotificationUI = class {
299
+ constructor(win, core, rule, notificationsUI) {
295
300
  __publicField(this, "_win");
301
+ __publicField(this, "_core");
302
+ __publicField(this, "_notificationsUI");
296
303
  __publicField(this, "_wrapper");
297
304
  __publicField(this, "_rule");
298
305
  __publicField(this, "_onToggle");
299
306
  __publicField(this, "isHidden", false);
300
307
  this._win = win;
308
+ this._core = core;
301
309
  this._rule = rule;
302
- if (!_NotificationUI._notificationsUI) {
303
- _NotificationUI._notificationsUI = new NotificationsUI(this._win);
304
- }
310
+ this._notificationsUI = notificationsUI;
305
311
  this._wrapper = win.document.createElement(
306
312
  "div"
307
313
  );
308
- if (!_NotificationUI._highlight) {
309
- _NotificationUI._highlight = new ElementHighlighter(win);
310
- }
311
- _NotificationUI._notificationsUI.addNotification(this);
314
+ notificationsUI.addNotification(this);
312
315
  }
313
316
  static setOnToggle(instance, onToggle) {
314
317
  instance._onToggle = onToggle;
@@ -329,11 +332,11 @@ var _NotificationUI = class _NotificationUI {
329
332
  (container) => {
330
333
  container.onmouseover = () => {
331
334
  var _a;
332
- element && ((_a = _NotificationUI._highlight) == null ? void 0 : _a.highlight(element));
335
+ element && ((_a = this._notificationsUI) == null ? void 0 : _a.highlight(element));
333
336
  };
334
337
  container.onmouseout = () => {
335
338
  var _a;
336
- (_a = _NotificationUI._highlight) == null ? void 0 : _a.hide();
339
+ (_a = this._notificationsUI) == null ? void 0 : _a.highlight(null);
337
340
  };
338
341
  }
339
342
  ).openTag("div", {
@@ -346,15 +349,18 @@ var _NotificationUI = class _NotificationUI {
346
349
  },
347
350
  (logButton) => {
348
351
  logButton.onclick = () => {
349
- console.error(
352
+ const { id, message, element: element2, rel, help, ...extra } = notification;
353
+ this._core.log(
350
354
  "AbleDOM: ",
355
+ "\nid:",
356
+ id,
351
357
  "\nmessage:",
352
- notification.message,
358
+ message,
353
359
  "\nelement:",
354
- element,
355
- ...notification.rel ? ["\nrelative:", notification.rel] : [],
356
- "\nnotification:",
357
- notification
360
+ element2,
361
+ ...rel ? ["\nrelative:", rel] : [],
362
+ ...help ? ["\nhelp:", help] : [],
363
+ ...Object.keys(extra).length > 0 ? ["\nextra:", extra] : []
358
364
  );
359
365
  };
360
366
  }
@@ -366,12 +372,13 @@ var _NotificationUI = class _NotificationUI {
366
372
  },
367
373
  (revealButton) => {
368
374
  var _a;
375
+ const body = win.document.body;
369
376
  const hasDevTools = !!((_a = win.__ableDOMDevtools) == null ? void 0 : _a.revealElement) && false;
370
- if (hasDevTools && element && win.document.body.contains(element)) {
377
+ if (hasDevTools && element && body.contains(element)) {
371
378
  revealButton.onclick = () => {
372
379
  var _a2;
373
380
  const revealElement = (_a2 = win.__ableDOMDevtools) == null ? void 0 : _a2.revealElement;
374
- if (revealElement && win.document.body.contains(element)) {
381
+ if (revealElement && body.contains(element)) {
375
382
  revealElement(element).then((revealed) => {
376
383
  if (!revealed) {
377
384
  }
@@ -405,7 +412,7 @@ var _NotificationUI = class _NotificationUI {
405
412
  closeButton.onclick = () => {
406
413
  var _a;
407
414
  this.toggle(false);
408
- (_a = _NotificationUI._highlight) == null ? void 0 : _a.hide();
415
+ (_a = this._notificationsUI) == null ? void 0 : _a.highlight(null);
409
416
  };
410
417
  }
411
418
  ).element(close_default).closeTag().closeTag().closeTag();
@@ -424,12 +431,10 @@ var _NotificationUI = class _NotificationUI {
424
431
  dispose() {
425
432
  var _a;
426
433
  this._wrapper.remove();
427
- (_a = _NotificationUI._notificationsUI) == null ? void 0 : _a.removeNotification(this);
434
+ (_a = this._notificationsUI) == null ? void 0 : _a.removeNotification(this);
435
+ delete this._notificationsUI;
428
436
  }
429
437
  };
430
- __publicField(_NotificationUI, "_notificationsUI");
431
- __publicField(_NotificationUI, "_highlight");
432
- var NotificationUI = _NotificationUI;
433
438
  var NotificationsUI = class {
434
439
  constructor(win) {
435
440
  __publicField(this, "_container");
@@ -444,17 +449,21 @@ var NotificationsUI = class {
444
449
  __publicField(this, "_alignBottomRightButton");
445
450
  __publicField(this, "_isMuted", false);
446
451
  __publicField(this, "_notifications", /* @__PURE__ */ new Set());
447
- const container = this._container = win.document.createElement("div");
452
+ __publicField(this, "_highlighter");
453
+ const doc = win.document;
454
+ const container = this._container = doc.createElement("div");
448
455
  container.__abledomui = true;
449
456
  container.id = "abledom-report";
450
- const style = document.createElement("style");
457
+ const style = doc.createElement("style");
451
458
  style.type = "text/css";
452
- style.appendChild(document.createTextNode(ui_default));
459
+ style.appendChild(doc.createTextNode(ui_default));
453
460
  container.appendChild(style);
454
- const notificationsContainer = this._notificationsContainer = win.document.createElement("div");
461
+ const notificationsContainer = this._notificationsContainer = doc.createElement("div");
462
+ notificationsContainer.__abledomui = true;
455
463
  notificationsContainer.className = "abledom-notifications-container";
456
464
  container.appendChild(notificationsContainer);
457
- const menuElement = this._menuElement = win.document.createElement("div");
465
+ const menuElement = this._menuElement = doc.createElement("div");
466
+ menuElement.__abledomui = true;
458
467
  menuElement.className = "abledom-menu-container";
459
468
  container.appendChild(menuElement);
460
469
  new DOMBuilder(menuElement).openTag("div", { class: "abledom-menu" }).openTag(
@@ -561,10 +570,14 @@ var NotificationsUI = class {
561
570
  };
562
571
  }
563
572
  ).element(alignbottomright_default).closeTag().closeTag();
564
- win.document.body.appendChild(container);
573
+ doc.body.appendChild(container);
574
+ this._highlighter = new ElementHighlighter(win);
565
575
  }
566
576
  setUIAlignment(alignment) {
567
577
  var _a, _b, _c, _d, _e, _f, _g, _h;
578
+ if (!this._container || !this._notificationsContainer || !this._menuElement) {
579
+ return;
580
+ }
568
581
  (_a = this._alignBottomLeftButton) == null ? void 0 : _a.classList.remove(pressedClass);
569
582
  (_b = this._alignBottomRightButton) == null ? void 0 : _b.classList.remove(pressedClass);
570
583
  (_c = this._alignTopLeftButton) == null ? void 0 : _c.classList.remove(pressedClass);
@@ -604,6 +617,9 @@ var NotificationsUI = class {
604
617
  );
605
618
  }
606
619
  _setNotificationsCount(count) {
620
+ if (!this._menuElement) {
621
+ return;
622
+ }
607
623
  const countElement = this._notificationCountElement;
608
624
  if (countElement && count > 0) {
609
625
  countElement.textContent = "";
@@ -635,6 +651,9 @@ var NotificationsUI = class {
635
651
  showAllButton.style.display = allVisible ? "none" : "block";
636
652
  }
637
653
  addNotification(notification) {
654
+ if (!this._notificationsContainer) {
655
+ throw new Error("NotificationsUI is not initialized");
656
+ }
638
657
  if (this._notifications.has(notification)) {
639
658
  return;
640
659
  }
@@ -658,6 +677,7 @@ var NotificationsUI = class {
658
677
  this._notifications.delete(notification);
659
678
  this._setNotificationsCount(this._notifications.size);
660
679
  this._setShowHideButtonsVisibility();
680
+ this.highlight(null);
661
681
  }
662
682
  hideAll() {
663
683
  this._notifications.forEach((notification) => {
@@ -671,6 +691,26 @@ var NotificationsUI = class {
671
691
  });
672
692
  this._setShowHideButtonsVisibility();
673
693
  }
694
+ highlight(element) {
695
+ var _a;
696
+ (_a = this._highlighter) == null ? void 0 : _a.highlight(element);
697
+ }
698
+ dispose() {
699
+ var _a, _b;
700
+ (_a = this._highlighter) == null ? void 0 : _a.dispose();
701
+ (_b = this._container) == null ? void 0 : _b.remove();
702
+ delete this._highlighter;
703
+ delete this._container;
704
+ delete this._notificationsContainer;
705
+ delete this._menuElement;
706
+ delete this._notificationCountElement;
707
+ delete this._showAllButton;
708
+ delete this._hideAllButton;
709
+ delete this._alignBottomLeftButton;
710
+ delete this._alignTopLeftButton;
711
+ delete this._alignTopRightButton;
712
+ delete this._alignBottomRightButton;
713
+ }
674
714
  };
675
715
  var ElementHighlighter = class {
676
716
  constructor(win) {
@@ -682,15 +722,20 @@ var ElementHighlighter = class {
682
722
  container.className = "abledom-highlight";
683
723
  }
684
724
  highlight(element) {
685
- const rect = element.getBoundingClientRect();
686
- if (rect.width === 0 || rect.height === 0) {
725
+ if (!element) {
726
+ this._container && (this._container.style.display = "none");
687
727
  return;
688
728
  }
689
729
  const win = this._window;
690
730
  const container = this._container;
731
+ const rect = element.getBoundingClientRect();
732
+ if (!win || !container || rect.width === 0 || rect.height === 0) {
733
+ return;
734
+ }
735
+ const body = win.document.body;
691
736
  const style = container.style;
692
- if (container.parentElement !== win.document.body) {
693
- win.document.body.appendChild(container);
737
+ if (container.parentElement !== body) {
738
+ body.appendChild(container);
694
739
  }
695
740
  style.width = `${rect.width}px`;
696
741
  style.height = `${rect.height}px`;
@@ -698,8 +743,11 @@ var ElementHighlighter = class {
698
743
  style.left = `${rect.left}px`;
699
744
  container.style.display = "block";
700
745
  }
701
- hide() {
702
- this._container.style.display = "none";
746
+ dispose() {
747
+ var _a;
748
+ (_a = this._container) == null ? void 0 : _a.remove();
749
+ delete this._container;
750
+ delete this._window;
703
751
  }
704
752
  };
705
753
  function isAbleDOMUIElement(element) {
@@ -894,8 +942,9 @@ function getStackTrace() {
894
942
 
895
943
  // src/core.ts
896
944
  var AbleDOM = class {
897
- constructor(win) {
945
+ constructor(win, props = {}) {
898
946
  __publicField(this, "_win");
947
+ __publicField(this, "_props");
899
948
  __publicField(this, "_observer");
900
949
  __publicField(this, "_clearValidationTimeout");
901
950
  __publicField(this, "_elementsWithNotifications", /* @__PURE__ */ new Set());
@@ -906,6 +955,7 @@ var AbleDOM = class {
906
955
  __publicField(this, "_rules", []);
907
956
  __publicField(this, "_startFunc");
908
957
  __publicField(this, "_isStarted", false);
958
+ __publicField(this, "_notificationsUI");
909
959
  __publicField(this, "_onFocusIn", (event) => {
910
960
  var _a;
911
961
  const target = event.target;
@@ -935,11 +985,18 @@ var AbleDOM = class {
935
985
  __publicField(this, "_notifyAsync", (rule, notification) => {
936
986
  this._addNotification(rule, notification);
937
987
  });
988
+ __publicField(this, "log", (...args) => {
989
+ var _a, _b, _c, _d;
990
+ return (_d = ((_a = this._props) == null ? void 0 : _a.log) || // In a multi-window application, just `console.error` could belong to a different window.
991
+ ((_c = (_b = this._win) == null ? void 0 : _b.console) == null ? void 0 : _c.error)) == null ? void 0 : _d.apply(null, args);
992
+ });
938
993
  this._win = win;
994
+ this._props = props;
939
995
  const _elementsToValidate = /* @__PURE__ */ new Set();
940
996
  const _elementsToRemove = /* @__PURE__ */ new Set();
941
- win.document.addEventListener("focusin", this._onFocusIn, true);
942
- win.document.addEventListener("focusout", this._onFocusOut, true);
997
+ const doc = win.document;
998
+ doc.addEventListener("focusin", this._onFocusIn, true);
999
+ doc.addEventListener("focusout", this._onFocusOut, true);
943
1000
  this._observer = new MutationObserver((mutations) => {
944
1001
  var _a;
945
1002
  for (let mutation of mutations) {
@@ -977,12 +1034,13 @@ var AbleDOM = class {
977
1034
  });
978
1035
  this._startFunc = () => {
979
1036
  delete this._startFunc;
980
- this._observer.observe(win.document, {
1037
+ this._observer.observe(doc, {
981
1038
  childList: true,
982
1039
  subtree: true,
983
- attributes: true
1040
+ attributes: true,
1041
+ characterData: true
984
1042
  });
985
- findTargets(win.document.body, false);
1043
+ findTargets(doc.body, false);
986
1044
  this._validate(_elementsToValidate);
987
1045
  _elementsToValidate.clear();
988
1046
  };
@@ -1012,7 +1070,7 @@ var AbleDOM = class {
1012
1070
  return;
1013
1071
  }
1014
1072
  addTarget(node, removed);
1015
- const walker = win.document.createTreeWalker(
1073
+ const walker = doc.createTreeWalker(
1016
1074
  node,
1017
1075
  NodeFilter.SHOW_ELEMENT,
1018
1076
  (node2) => {
@@ -1063,6 +1121,9 @@ var AbleDOM = class {
1063
1121
  }
1064
1122
  }
1065
1123
  _addNotification(rule, notification) {
1124
+ if (!this._notificationsUI) {
1125
+ this._notificationsUI = new NotificationsUI(this._win);
1126
+ }
1066
1127
  const element = notification == null ? void 0 : notification.element;
1067
1128
  if (!notification) {
1068
1129
  this._removeNotification(element || this._win.document.body, rule);
@@ -1080,12 +1141,22 @@ var AbleDOM = class {
1080
1141
  }
1081
1142
  notificationUI = notifications.get(rule);
1082
1143
  if (!notificationUI) {
1083
- notificationUI = new NotificationUI(this._win, rule);
1144
+ notificationUI = new NotificationUI(
1145
+ this._win,
1146
+ this,
1147
+ rule,
1148
+ this._notificationsUI
1149
+ );
1084
1150
  notifications.set(rule, notificationUI);
1085
1151
  }
1086
1152
  this._elementsWithNotifications.add(element);
1087
1153
  } else {
1088
- notificationUI = new NotificationUI(this._win, rule);
1154
+ notificationUI = new NotificationUI(
1155
+ this._win,
1156
+ this,
1157
+ rule,
1158
+ this._notificationsUI
1159
+ );
1089
1160
  }
1090
1161
  notificationUI.update(notification);
1091
1162
  }
@@ -1219,17 +1290,20 @@ var AbleDOM = class {
1219
1290
  (_b = this._startFunc) == null ? void 0 : _b.call(this);
1220
1291
  }
1221
1292
  dispose() {
1222
- var _a, _b;
1223
- this._win.document.addEventListener("focusin", this._onFocusIn, true);
1224
- this._win.document.addEventListener("focusout", this._onFocusOut, true);
1293
+ var _a, _b, _c;
1294
+ const doc = this._win.document;
1295
+ doc.addEventListener("focusin", this._onFocusIn, true);
1296
+ doc.addEventListener("focusout", this._onFocusOut, true);
1225
1297
  this._remove(this._elementsWithNotifications);
1226
1298
  this._elementsWithNotifications.clear();
1227
1299
  this._dependantIdsByElement.clear();
1228
1300
  this._elementsDependingOnId.clear();
1229
1301
  this._idByElement.clear();
1230
- (_a = this._clearValidationTimeout) == null ? void 0 : _a.call(this);
1302
+ (_a = this._notificationsUI) == null ? void 0 : _a.dispose();
1303
+ delete this._notificationsUI;
1304
+ (_b = this._clearValidationTimeout) == null ? void 0 : _b.call(this);
1231
1305
  for (const rule of this._rules) {
1232
- (_b = rule.stop) == null ? void 0 : _b.call(rule);
1306
+ (_c = rule.stop) == null ? void 0 : _c.call(rule);
1233
1307
  ValidationRule.dispose(rule);
1234
1308
  }
1235
1309
  this._rules = [];
@@ -1253,7 +1327,7 @@ var AtomicRule = class extends ValidationRule {
1253
1327
  return matchesSelector(element, focusableElementSelector);
1254
1328
  }
1255
1329
  validate(element) {
1256
- const parentAtomic = document.evaluate(
1330
+ const parentAtomic = element.ownerDocument.evaluate(
1257
1331
  `ancestor::*[
1258
1332
  @role = 'button' or
1259
1333
  @role = 'checkbox' or
@@ -1314,7 +1388,7 @@ var FocusableElementLabelRule = class extends ValidationRule {
1314
1388
  __publicField(this, "anchored", true);
1315
1389
  }
1316
1390
  _isAriaHidden(element) {
1317
- return document.evaluate(
1391
+ return element.ownerDocument.evaluate(
1318
1392
  `ancestor-or-self::*[@aria-hidden = 'true' or @hidden]`,
1319
1393
  element,
1320
1394
  null,
@@ -1323,7 +1397,7 @@ var FocusableElementLabelRule = class extends ValidationRule {
1323
1397
  ).booleanValue;
1324
1398
  }
1325
1399
  _hasLabel(element) {
1326
- var _a, _b;
1400
+ var _a, _b, _c;
1327
1401
  const labels = element.labels;
1328
1402
  if (labels && labels.length > 0) {
1329
1403
  for (let i = 0; i < labels.length; i++) {
@@ -1338,7 +1412,10 @@ var FocusableElementLabelRule = class extends ValidationRule {
1338
1412
  return true;
1339
1413
  }
1340
1414
  }
1341
- const labelNodes = document.evaluate(
1415
+ if ((_a = element.textContent) == null ? void 0 : _a.trim()) {
1416
+ return true;
1417
+ }
1418
+ const labelNodes = element.ownerDocument.evaluate(
1342
1419
  `(
1343
1420
  .//@aria-label |
1344
1421
  .//text() |
@@ -1353,7 +1430,7 @@ var FocusableElementLabelRule = class extends ValidationRule {
1353
1430
  null
1354
1431
  );
1355
1432
  for (let i = 0; i < labelNodes.snapshotLength; i++) {
1356
- const val = (_b = (_a = labelNodes.snapshotItem(i)) == null ? void 0 : _a.nodeValue) == null ? void 0 : _b.trim();
1433
+ const val = (_c = (_b = labelNodes.snapshotItem(i)) == null ? void 0 : _b.nodeValue) == null ? void 0 : _c.trim();
1357
1434
  if (val) {
1358
1435
  return true;
1359
1436
  }
@@ -1365,6 +1442,7 @@ var FocusableElementLabelRule = class extends ValidationRule {
1365
1442
  }
1366
1443
  validate(element) {
1367
1444
  var _a, _b;
1445
+ const doc = element.ownerDocument;
1368
1446
  if (element.tagName === "INPUT") {
1369
1447
  const type = element.type;
1370
1448
  if (type === "hidden") {
@@ -1390,7 +1468,7 @@ var FocusableElementLabelRule = class extends ValidationRule {
1390
1468
  if (this._hasLabel(element)) {
1391
1469
  return null;
1392
1470
  }
1393
- const labelledByNodes = document.evaluate(
1471
+ const labelledByNodes = doc.evaluate(
1394
1472
  `.//@aria-labelledby[not(ancestor-or-self::*[@aria-hidden = 'true' or @hidden])]`,
1395
1473
  element,
1396
1474
  null,
@@ -1405,7 +1483,7 @@ var FocusableElementLabelRule = class extends ValidationRule {
1405
1483
  }
1406
1484
  }
1407
1485
  for (const id of labelledByValues) {
1408
- const labelElement = document.getElementById(id);
1486
+ const labelElement = doc.getElementById(id);
1409
1487
  if (labelElement && this._hasLabel(labelElement)) {
1410
1488
  return {
1411
1489
  dependsOnIds: new Set(labelledByValues)
@@ -1446,7 +1524,7 @@ var ExistingIdRule = class extends ValidationRule {
1446
1524
  return null;
1447
1525
  }
1448
1526
  for (const id of ids) {
1449
- if (document.getElementById(id)) {
1527
+ if (element.ownerDocument.getElementById(id)) {
1450
1528
  return {
1451
1529
  dependsOnIds: new Set(ids)
1452
1530
  };
@@ -1511,13 +1589,14 @@ var FocusLostRule = class extends ValidationRule {
1511
1589
  if (!target || !win || event.relatedTarget || this._mouseEventTimer) {
1512
1590
  return null;
1513
1591
  }
1592
+ const doc = win.document;
1514
1593
  const targetPosition = this._focusedElement === target ? this._focusedElementPosition : void 0;
1515
1594
  this._lastBlurStack = getStackTrace();
1516
1595
  this._focusedElement = void 0;
1517
1596
  this._focusedElementPosition = void 0;
1518
1597
  const focusLostTimer = win.setTimeout(() => {
1519
1598
  delete this._clearScheduledFocusLost;
1520
- if (win.document.body && (!win.document.activeElement || win.document.activeElement === win.document.body) && (!win.document.body.contains(target) || !isElementVisible(target))) {
1599
+ if (doc.body && (!doc.activeElement || doc.activeElement === doc.body) && (!doc.body.contains(target) || !isElementVisible(target))) {
1521
1600
  this.notify({
1522
1601
  element: target,
1523
1602
  id: "focus-lost",
@@ -1592,15 +1671,16 @@ var BadFocusRule = class extends ValidationRule {
1592
1671
  if (!win) {
1593
1672
  return null;
1594
1673
  }
1674
+ const doc = win.document;
1595
1675
  this._lastBlurStack = getStackTrace();
1596
1676
  (_a = this._clearCheckTimer) == null ? void 0 : _a.call(this);
1597
1677
  const checkTimer = win.setTimeout(() => {
1598
1678
  delete this._clearCheckTimer;
1599
- if (document.activeElement && !isElementVisible(document.activeElement)) {
1679
+ if (doc.activeElement && !isElementVisible(doc.activeElement)) {
1600
1680
  this.notify({
1601
1681
  id: "bad-focus",
1602
1682
  message: "Focused stolen by invisible element.",
1603
- element: document.activeElement,
1683
+ element: doc.activeElement,
1604
1684
  stack: this._lastBlurStack,
1605
1685
  relStack: this._lastFocusStack
1606
1686
  });