abledom 0.1.1 → 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/esm/index.js CHANGED
@@ -73,6 +73,7 @@ var DOMBuilder = class {
73
73
  const parent = this._stack[0];
74
74
  const element = namespace ? (_a = this._doc) == null ? void 0 : _a.createElementNS(namespace, tagName) : (_b = this._doc) == null ? void 0 : _b.createElement(tagName);
75
75
  if (parent && element) {
76
+ element.__abledomui = true;
76
77
  if (attributes) {
77
78
  for (const [key, value] of Object.entries(attributes)) {
78
79
  if (key === "class") {
@@ -100,8 +101,12 @@ var DOMBuilder = class {
100
101
  return this;
101
102
  }
102
103
  text(text) {
103
- var _a;
104
- (_a = this._stack[0]) == null ? void 0 : _a.appendChild(document.createTextNode(text));
104
+ var _a, _b;
105
+ const textNode = (_a = this._doc) == null ? void 0 : _a.createTextNode(text);
106
+ if (textNode) {
107
+ textNode.__abledomui = true;
108
+ (_b = this._stack[0]) == null ? void 0 : _b.appendChild(textNode);
109
+ }
105
110
  return this;
106
111
  }
107
112
  element(callback) {
@@ -252,25 +257,23 @@ var alignbottomleft_default = function buildSVG11(parent) {
252
257
 
253
258
  // src/ui/ui.ts
254
259
  var pressedClass = "pressed";
255
- var _NotificationUI = class _NotificationUI {
256
- constructor(win, rule) {
260
+ var NotificationUI = class {
261
+ constructor(win, core, rule, notificationsUI) {
257
262
  __publicField(this, "_win");
263
+ __publicField(this, "_core");
264
+ __publicField(this, "_notificationsUI");
258
265
  __publicField(this, "_wrapper");
259
266
  __publicField(this, "_rule");
260
267
  __publicField(this, "_onToggle");
261
268
  __publicField(this, "isHidden", false);
262
269
  this._win = win;
270
+ this._core = core;
263
271
  this._rule = rule;
264
- if (!_NotificationUI._notificationsUI) {
265
- _NotificationUI._notificationsUI = new NotificationsUI(this._win);
266
- }
272
+ this._notificationsUI = notificationsUI;
267
273
  this._wrapper = win.document.createElement(
268
274
  "div"
269
275
  );
270
- if (!_NotificationUI._highlight) {
271
- _NotificationUI._highlight = new ElementHighlighter(win);
272
- }
273
- _NotificationUI._notificationsUI.addNotification(this);
276
+ notificationsUI.addNotification(this);
274
277
  }
275
278
  static setOnToggle(instance, onToggle) {
276
279
  instance._onToggle = onToggle;
@@ -291,11 +294,11 @@ var _NotificationUI = class _NotificationUI {
291
294
  (container) => {
292
295
  container.onmouseover = () => {
293
296
  var _a;
294
- element && ((_a = _NotificationUI._highlight) == null ? void 0 : _a.highlight(element));
297
+ element && ((_a = this._notificationsUI) == null ? void 0 : _a.highlight(element));
295
298
  };
296
299
  container.onmouseout = () => {
297
300
  var _a;
298
- (_a = _NotificationUI._highlight) == null ? void 0 : _a.hide();
301
+ (_a = this._notificationsUI) == null ? void 0 : _a.highlight(null);
299
302
  };
300
303
  }
301
304
  ).openTag("div", {
@@ -308,15 +311,18 @@ var _NotificationUI = class _NotificationUI {
308
311
  },
309
312
  (logButton) => {
310
313
  logButton.onclick = () => {
311
- console.error(
314
+ const { id, message, element: element2, rel, help, ...extra } = notification;
315
+ this._core.log(
312
316
  "AbleDOM: ",
317
+ "\nid:",
318
+ id,
313
319
  "\nmessage:",
314
- notification.message,
320
+ message,
315
321
  "\nelement:",
316
- element,
317
- ...notification.rel ? ["\nrelative:", notification.rel] : [],
318
- "\nnotification:",
319
- notification
322
+ element2,
323
+ ...rel ? ["\nrelative:", rel] : [],
324
+ ...help ? ["\nhelp:", help] : [],
325
+ ...Object.keys(extra).length > 0 ? ["\nextra:", extra] : []
320
326
  );
321
327
  };
322
328
  }
@@ -328,12 +334,13 @@ var _NotificationUI = class _NotificationUI {
328
334
  },
329
335
  (revealButton) => {
330
336
  var _a;
337
+ const body = win.document.body;
331
338
  const hasDevTools = !!((_a = win.__ableDOMDevtools) == null ? void 0 : _a.revealElement) && false;
332
- if (hasDevTools && element && win.document.body.contains(element)) {
339
+ if (hasDevTools && element && body.contains(element)) {
333
340
  revealButton.onclick = () => {
334
341
  var _a2;
335
342
  const revealElement = (_a2 = win.__ableDOMDevtools) == null ? void 0 : _a2.revealElement;
336
- if (revealElement && win.document.body.contains(element)) {
343
+ if (revealElement && body.contains(element)) {
337
344
  revealElement(element).then((revealed) => {
338
345
  if (!revealed) {
339
346
  }
@@ -367,7 +374,7 @@ var _NotificationUI = class _NotificationUI {
367
374
  closeButton.onclick = () => {
368
375
  var _a;
369
376
  this.toggle(false);
370
- (_a = _NotificationUI._highlight) == null ? void 0 : _a.hide();
377
+ (_a = this._notificationsUI) == null ? void 0 : _a.highlight(null);
371
378
  };
372
379
  }
373
380
  ).element(close_default).closeTag().closeTag().closeTag();
@@ -386,12 +393,10 @@ var _NotificationUI = class _NotificationUI {
386
393
  dispose() {
387
394
  var _a;
388
395
  this._wrapper.remove();
389
- (_a = _NotificationUI._notificationsUI) == null ? void 0 : _a.removeNotification(this);
396
+ (_a = this._notificationsUI) == null ? void 0 : _a.removeNotification(this);
397
+ delete this._notificationsUI;
390
398
  }
391
399
  };
392
- __publicField(_NotificationUI, "_notificationsUI");
393
- __publicField(_NotificationUI, "_highlight");
394
- var NotificationUI = _NotificationUI;
395
400
  var NotificationsUI = class {
396
401
  constructor(win) {
397
402
  __publicField(this, "_container");
@@ -406,17 +411,21 @@ var NotificationsUI = class {
406
411
  __publicField(this, "_alignBottomRightButton");
407
412
  __publicField(this, "_isMuted", false);
408
413
  __publicField(this, "_notifications", /* @__PURE__ */ new Set());
409
- const container = this._container = win.document.createElement("div");
414
+ __publicField(this, "_highlighter");
415
+ const doc = win.document;
416
+ const container = this._container = doc.createElement("div");
410
417
  container.__abledomui = true;
411
418
  container.id = "abledom-report";
412
- const style = document.createElement("style");
419
+ const style = doc.createElement("style");
413
420
  style.type = "text/css";
414
- style.appendChild(document.createTextNode(ui_default));
421
+ style.appendChild(doc.createTextNode(ui_default));
415
422
  container.appendChild(style);
416
- const notificationsContainer = this._notificationsContainer = win.document.createElement("div");
423
+ const notificationsContainer = this._notificationsContainer = doc.createElement("div");
424
+ notificationsContainer.__abledomui = true;
417
425
  notificationsContainer.className = "abledom-notifications-container";
418
426
  container.appendChild(notificationsContainer);
419
- const menuElement = this._menuElement = win.document.createElement("div");
427
+ const menuElement = this._menuElement = doc.createElement("div");
428
+ menuElement.__abledomui = true;
420
429
  menuElement.className = "abledom-menu-container";
421
430
  container.appendChild(menuElement);
422
431
  new DOMBuilder(menuElement).openTag("div", { class: "abledom-menu" }).openTag(
@@ -523,10 +532,14 @@ var NotificationsUI = class {
523
532
  };
524
533
  }
525
534
  ).element(alignbottomright_default).closeTag().closeTag();
526
- win.document.body.appendChild(container);
535
+ doc.body.appendChild(container);
536
+ this._highlighter = new ElementHighlighter(win);
527
537
  }
528
538
  setUIAlignment(alignment) {
529
539
  var _a, _b, _c, _d, _e, _f, _g, _h;
540
+ if (!this._container || !this._notificationsContainer || !this._menuElement) {
541
+ return;
542
+ }
530
543
  (_a = this._alignBottomLeftButton) == null ? void 0 : _a.classList.remove(pressedClass);
531
544
  (_b = this._alignBottomRightButton) == null ? void 0 : _b.classList.remove(pressedClass);
532
545
  (_c = this._alignTopLeftButton) == null ? void 0 : _c.classList.remove(pressedClass);
@@ -566,6 +579,9 @@ var NotificationsUI = class {
566
579
  );
567
580
  }
568
581
  _setNotificationsCount(count) {
582
+ if (!this._menuElement) {
583
+ return;
584
+ }
569
585
  const countElement = this._notificationCountElement;
570
586
  if (countElement && count > 0) {
571
587
  countElement.textContent = "";
@@ -597,6 +613,9 @@ var NotificationsUI = class {
597
613
  showAllButton.style.display = allVisible ? "none" : "block";
598
614
  }
599
615
  addNotification(notification) {
616
+ if (!this._notificationsContainer) {
617
+ throw new Error("NotificationsUI is not initialized");
618
+ }
600
619
  if (this._notifications.has(notification)) {
601
620
  return;
602
621
  }
@@ -620,6 +639,7 @@ var NotificationsUI = class {
620
639
  this._notifications.delete(notification);
621
640
  this._setNotificationsCount(this._notifications.size);
622
641
  this._setShowHideButtonsVisibility();
642
+ this.highlight(null);
623
643
  }
624
644
  hideAll() {
625
645
  this._notifications.forEach((notification) => {
@@ -633,6 +653,26 @@ var NotificationsUI = class {
633
653
  });
634
654
  this._setShowHideButtonsVisibility();
635
655
  }
656
+ highlight(element) {
657
+ var _a;
658
+ (_a = this._highlighter) == null ? void 0 : _a.highlight(element);
659
+ }
660
+ dispose() {
661
+ var _a, _b;
662
+ (_a = this._highlighter) == null ? void 0 : _a.dispose();
663
+ (_b = this._container) == null ? void 0 : _b.remove();
664
+ delete this._highlighter;
665
+ delete this._container;
666
+ delete this._notificationsContainer;
667
+ delete this._menuElement;
668
+ delete this._notificationCountElement;
669
+ delete this._showAllButton;
670
+ delete this._hideAllButton;
671
+ delete this._alignBottomLeftButton;
672
+ delete this._alignTopLeftButton;
673
+ delete this._alignTopRightButton;
674
+ delete this._alignBottomRightButton;
675
+ }
636
676
  };
637
677
  var ElementHighlighter = class {
638
678
  constructor(win) {
@@ -644,15 +684,20 @@ var ElementHighlighter = class {
644
684
  container.className = "abledom-highlight";
645
685
  }
646
686
  highlight(element) {
647
- const rect = element.getBoundingClientRect();
648
- if (rect.width === 0 || rect.height === 0) {
687
+ if (!element) {
688
+ this._container && (this._container.style.display = "none");
649
689
  return;
650
690
  }
651
691
  const win = this._window;
652
692
  const container = this._container;
693
+ const rect = element.getBoundingClientRect();
694
+ if (!win || !container || rect.width === 0 || rect.height === 0) {
695
+ return;
696
+ }
697
+ const body = win.document.body;
653
698
  const style = container.style;
654
- if (container.parentElement !== win.document.body) {
655
- win.document.body.appendChild(container);
699
+ if (container.parentElement !== body) {
700
+ body.appendChild(container);
656
701
  }
657
702
  style.width = `${rect.width}px`;
658
703
  style.height = `${rect.height}px`;
@@ -660,8 +705,11 @@ var ElementHighlighter = class {
660
705
  style.left = `${rect.left}px`;
661
706
  container.style.display = "block";
662
707
  }
663
- hide() {
664
- this._container.style.display = "none";
708
+ dispose() {
709
+ var _a;
710
+ (_a = this._container) == null ? void 0 : _a.remove();
711
+ delete this._container;
712
+ delete this._window;
665
713
  }
666
714
  };
667
715
  function isAbleDOMUIElement(element) {
@@ -856,8 +904,9 @@ function getStackTrace() {
856
904
 
857
905
  // src/core.ts
858
906
  var AbleDOM = class {
859
- constructor(win) {
907
+ constructor(win, props = {}) {
860
908
  __publicField(this, "_win");
909
+ __publicField(this, "_props");
861
910
  __publicField(this, "_observer");
862
911
  __publicField(this, "_clearValidationTimeout");
863
912
  __publicField(this, "_elementsWithNotifications", /* @__PURE__ */ new Set());
@@ -868,6 +917,7 @@ var AbleDOM = class {
868
917
  __publicField(this, "_rules", []);
869
918
  __publicField(this, "_startFunc");
870
919
  __publicField(this, "_isStarted", false);
920
+ __publicField(this, "_notificationsUI");
871
921
  __publicField(this, "_onFocusIn", (event) => {
872
922
  var _a;
873
923
  const target = event.target;
@@ -897,11 +947,18 @@ var AbleDOM = class {
897
947
  __publicField(this, "_notifyAsync", (rule, notification) => {
898
948
  this._addNotification(rule, notification);
899
949
  });
950
+ __publicField(this, "log", (...args) => {
951
+ var _a, _b, _c, _d;
952
+ return (_d = ((_a = this._props) == null ? void 0 : _a.log) || // In a multi-window application, just `console.error` could belong to a different window.
953
+ ((_c = (_b = this._win) == null ? void 0 : _b.console) == null ? void 0 : _c.error)) == null ? void 0 : _d.apply(null, args);
954
+ });
900
955
  this._win = win;
956
+ this._props = props;
901
957
  const _elementsToValidate = /* @__PURE__ */ new Set();
902
958
  const _elementsToRemove = /* @__PURE__ */ new Set();
903
- win.document.addEventListener("focusin", this._onFocusIn, true);
904
- win.document.addEventListener("focusout", this._onFocusOut, true);
959
+ const doc = win.document;
960
+ doc.addEventListener("focusin", this._onFocusIn, true);
961
+ doc.addEventListener("focusout", this._onFocusOut, true);
905
962
  this._observer = new MutationObserver((mutations) => {
906
963
  var _a;
907
964
  for (let mutation of mutations) {
@@ -939,12 +996,13 @@ var AbleDOM = class {
939
996
  });
940
997
  this._startFunc = () => {
941
998
  delete this._startFunc;
942
- this._observer.observe(win.document, {
999
+ this._observer.observe(doc, {
943
1000
  childList: true,
944
1001
  subtree: true,
945
- attributes: true
1002
+ attributes: true,
1003
+ characterData: true
946
1004
  });
947
- findTargets(win.document.body, false);
1005
+ findTargets(doc.body, false);
948
1006
  this._validate(_elementsToValidate);
949
1007
  _elementsToValidate.clear();
950
1008
  };
@@ -974,7 +1032,7 @@ var AbleDOM = class {
974
1032
  return;
975
1033
  }
976
1034
  addTarget(node, removed);
977
- const walker = win.document.createTreeWalker(
1035
+ const walker = doc.createTreeWalker(
978
1036
  node,
979
1037
  NodeFilter.SHOW_ELEMENT,
980
1038
  (node2) => {
@@ -1025,6 +1083,9 @@ var AbleDOM = class {
1025
1083
  }
1026
1084
  }
1027
1085
  _addNotification(rule, notification) {
1086
+ if (!this._notificationsUI) {
1087
+ this._notificationsUI = new NotificationsUI(this._win);
1088
+ }
1028
1089
  const element = notification == null ? void 0 : notification.element;
1029
1090
  if (!notification) {
1030
1091
  this._removeNotification(element || this._win.document.body, rule);
@@ -1042,12 +1103,22 @@ var AbleDOM = class {
1042
1103
  }
1043
1104
  notificationUI = notifications.get(rule);
1044
1105
  if (!notificationUI) {
1045
- notificationUI = new NotificationUI(this._win, rule);
1106
+ notificationUI = new NotificationUI(
1107
+ this._win,
1108
+ this,
1109
+ rule,
1110
+ this._notificationsUI
1111
+ );
1046
1112
  notifications.set(rule, notificationUI);
1047
1113
  }
1048
1114
  this._elementsWithNotifications.add(element);
1049
1115
  } else {
1050
- notificationUI = new NotificationUI(this._win, rule);
1116
+ notificationUI = new NotificationUI(
1117
+ this._win,
1118
+ this,
1119
+ rule,
1120
+ this._notificationsUI
1121
+ );
1051
1122
  }
1052
1123
  notificationUI.update(notification);
1053
1124
  }
@@ -1181,17 +1252,20 @@ var AbleDOM = class {
1181
1252
  (_b = this._startFunc) == null ? void 0 : _b.call(this);
1182
1253
  }
1183
1254
  dispose() {
1184
- var _a, _b;
1185
- this._win.document.addEventListener("focusin", this._onFocusIn, true);
1186
- this._win.document.addEventListener("focusout", this._onFocusOut, true);
1255
+ var _a, _b, _c;
1256
+ const doc = this._win.document;
1257
+ doc.addEventListener("focusin", this._onFocusIn, true);
1258
+ doc.addEventListener("focusout", this._onFocusOut, true);
1187
1259
  this._remove(this._elementsWithNotifications);
1188
1260
  this._elementsWithNotifications.clear();
1189
1261
  this._dependantIdsByElement.clear();
1190
1262
  this._elementsDependingOnId.clear();
1191
1263
  this._idByElement.clear();
1192
- (_a = this._clearValidationTimeout) == null ? void 0 : _a.call(this);
1264
+ (_a = this._notificationsUI) == null ? void 0 : _a.dispose();
1265
+ delete this._notificationsUI;
1266
+ (_b = this._clearValidationTimeout) == null ? void 0 : _b.call(this);
1193
1267
  for (const rule of this._rules) {
1194
- (_b = rule.stop) == null ? void 0 : _b.call(rule);
1268
+ (_c = rule.stop) == null ? void 0 : _c.call(rule);
1195
1269
  ValidationRule.dispose(rule);
1196
1270
  }
1197
1271
  this._rules = [];
@@ -1215,7 +1289,7 @@ var AtomicRule = class extends ValidationRule {
1215
1289
  return matchesSelector(element, focusableElementSelector);
1216
1290
  }
1217
1291
  validate(element) {
1218
- const parentAtomic = document.evaluate(
1292
+ const parentAtomic = element.ownerDocument.evaluate(
1219
1293
  `ancestor::*[
1220
1294
  @role = 'button' or
1221
1295
  @role = 'checkbox' or
@@ -1276,7 +1350,7 @@ var FocusableElementLabelRule = class extends ValidationRule {
1276
1350
  __publicField(this, "anchored", true);
1277
1351
  }
1278
1352
  _isAriaHidden(element) {
1279
- return document.evaluate(
1353
+ return element.ownerDocument.evaluate(
1280
1354
  `ancestor-or-self::*[@aria-hidden = 'true' or @hidden]`,
1281
1355
  element,
1282
1356
  null,
@@ -1303,7 +1377,7 @@ var FocusableElementLabelRule = class extends ValidationRule {
1303
1377
  if ((_a = element.textContent) == null ? void 0 : _a.trim()) {
1304
1378
  return true;
1305
1379
  }
1306
- const labelNodes = document.evaluate(
1380
+ const labelNodes = element.ownerDocument.evaluate(
1307
1381
  `(
1308
1382
  .//@aria-label |
1309
1383
  .//text() |
@@ -1330,6 +1404,7 @@ var FocusableElementLabelRule = class extends ValidationRule {
1330
1404
  }
1331
1405
  validate(element) {
1332
1406
  var _a, _b;
1407
+ const doc = element.ownerDocument;
1333
1408
  if (element.tagName === "INPUT") {
1334
1409
  const type = element.type;
1335
1410
  if (type === "hidden") {
@@ -1355,7 +1430,7 @@ var FocusableElementLabelRule = class extends ValidationRule {
1355
1430
  if (this._hasLabel(element)) {
1356
1431
  return null;
1357
1432
  }
1358
- const labelledByNodes = document.evaluate(
1433
+ const labelledByNodes = doc.evaluate(
1359
1434
  `.//@aria-labelledby[not(ancestor-or-self::*[@aria-hidden = 'true' or @hidden])]`,
1360
1435
  element,
1361
1436
  null,
@@ -1370,7 +1445,7 @@ var FocusableElementLabelRule = class extends ValidationRule {
1370
1445
  }
1371
1446
  }
1372
1447
  for (const id of labelledByValues) {
1373
- const labelElement = document.getElementById(id);
1448
+ const labelElement = doc.getElementById(id);
1374
1449
  if (labelElement && this._hasLabel(labelElement)) {
1375
1450
  return {
1376
1451
  dependsOnIds: new Set(labelledByValues)
@@ -1411,7 +1486,7 @@ var ExistingIdRule = class extends ValidationRule {
1411
1486
  return null;
1412
1487
  }
1413
1488
  for (const id of ids) {
1414
- if (document.getElementById(id)) {
1489
+ if (element.ownerDocument.getElementById(id)) {
1415
1490
  return {
1416
1491
  dependsOnIds: new Set(ids)
1417
1492
  };
@@ -1476,13 +1551,14 @@ var FocusLostRule = class extends ValidationRule {
1476
1551
  if (!target || !win || event.relatedTarget || this._mouseEventTimer) {
1477
1552
  return null;
1478
1553
  }
1554
+ const doc = win.document;
1479
1555
  const targetPosition = this._focusedElement === target ? this._focusedElementPosition : void 0;
1480
1556
  this._lastBlurStack = getStackTrace();
1481
1557
  this._focusedElement = void 0;
1482
1558
  this._focusedElementPosition = void 0;
1483
1559
  const focusLostTimer = win.setTimeout(() => {
1484
1560
  delete this._clearScheduledFocusLost;
1485
- if (win.document.body && (!win.document.activeElement || win.document.activeElement === win.document.body) && (!win.document.body.contains(target) || !isElementVisible(target))) {
1561
+ if (doc.body && (!doc.activeElement || doc.activeElement === doc.body) && (!doc.body.contains(target) || !isElementVisible(target))) {
1486
1562
  this.notify({
1487
1563
  element: target,
1488
1564
  id: "focus-lost",
@@ -1557,15 +1633,16 @@ var BadFocusRule = class extends ValidationRule {
1557
1633
  if (!win) {
1558
1634
  return null;
1559
1635
  }
1636
+ const doc = win.document;
1560
1637
  this._lastBlurStack = getStackTrace();
1561
1638
  (_a = this._clearCheckTimer) == null ? void 0 : _a.call(this);
1562
1639
  const checkTimer = win.setTimeout(() => {
1563
1640
  delete this._clearCheckTimer;
1564
- if (document.activeElement && !isElementVisible(document.activeElement)) {
1641
+ if (doc.activeElement && !isElementVisible(doc.activeElement)) {
1565
1642
  this.notify({
1566
1643
  id: "bad-focus",
1567
1644
  message: "Focused stolen by invisible element.",
1568
- element: document.activeElement,
1645
+ element: doc.activeElement,
1569
1646
  stack: this._lastBlurStack,
1570
1647
  relStack: this._lastFocusStack
1571
1648
  });