@reticular/speakable 1.6.0 → 1.8.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.
@@ -0,0 +1,87 @@
1
+ // src/browser/upgrade.ts
2
+ var DEFAULT_UPGRADE_TIMEOUT_MS = 2e3;
3
+ function withTimeout(promise, timeoutMs) {
4
+ return new Promise((resolve) => {
5
+ let settled = false;
6
+ const timer = setTimeout(() => {
7
+ if (!settled) {
8
+ settled = true;
9
+ resolve(false);
10
+ }
11
+ }, timeoutMs);
12
+ promise.then(
13
+ () => {
14
+ if (!settled) {
15
+ settled = true;
16
+ clearTimeout(timer);
17
+ resolve(true);
18
+ }
19
+ },
20
+ () => {
21
+ if (!settled) {
22
+ settled = true;
23
+ clearTimeout(timer);
24
+ resolve(true);
25
+ }
26
+ }
27
+ );
28
+ });
29
+ }
30
+ function collectCustomElementTags(root) {
31
+ const tags = /* @__PURE__ */ new Set();
32
+ const rootEl = "documentElement" in root ? root.documentElement : root;
33
+ if (!rootEl) return [];
34
+ const consider = (el) => {
35
+ if (el.localName && el.localName.includes("-")) {
36
+ tags.add(el.localName);
37
+ }
38
+ };
39
+ consider(rootEl);
40
+ const all = rootEl.querySelectorAll("*");
41
+ all.forEach(consider);
42
+ return Array.from(tags);
43
+ }
44
+ function collectUpdateCompletables(root) {
45
+ const rootEl = "documentElement" in root ? root.documentElement : root;
46
+ if (!rootEl) return [];
47
+ const promises = [];
48
+ const consider = (el) => {
49
+ const uc = el.updateComplete;
50
+ if (uc && typeof uc.then === "function") {
51
+ promises.push(uc);
52
+ }
53
+ };
54
+ consider(rootEl);
55
+ rootEl.querySelectorAll("*").forEach(consider);
56
+ return promises;
57
+ }
58
+ async function awaitCustomElementsReady(root, timeoutMs = DEFAULT_UPGRADE_TIMEOUT_MS) {
59
+ const warnings = [];
60
+ const hasCustomElements = typeof customElements !== "undefined" && customElements !== null;
61
+ if (!hasCustomElements) {
62
+ return warnings;
63
+ }
64
+ const tags = collectCustomElementTags(root);
65
+ await Promise.all(
66
+ tags.map(async (tag) => {
67
+ if (customElements.get(tag)) return;
68
+ const defined = await withTimeout(customElements.whenDefined(tag), timeoutMs);
69
+ if (!defined) {
70
+ warnings.push({
71
+ tag,
72
+ message: `Custom element "${tag}" did not upgrade within ${timeoutMs}ms; analysis may reflect a pre-hydration state.`
73
+ });
74
+ }
75
+ })
76
+ );
77
+ const updateCompletables = collectUpdateCompletables(root);
78
+ if (updateCompletables.length > 0) {
79
+ await withTimeout(Promise.all(updateCompletables), timeoutMs);
80
+ }
81
+ return warnings;
82
+ }
83
+
84
+ export {
85
+ awaitCustomElementsReady
86
+ };
87
+ //# sourceMappingURL=chunk-WYFXGXYU.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/browser/upgrade.ts"],"sourcesContent":["/**\n * Custom-element upgrade awaiting.\n *\n * Runs in a real browser (needs `customElements`). Waits for custom elements\n * under a root to be defined and, where supported, to finish rendering\n * (Lit's `updateComplete`) before analysis/capture proceeds. Bounded by a\n * timeout so it never blocks indefinitely; unresolved tags become warnings.\n */\n\nexport interface UpgradeWarning {\n message: string;\n tag: string;\n}\n\nconst DEFAULT_UPGRADE_TIMEOUT_MS = 2000;\n\n/**\n * Resolve a promise, or resolve with a sentinel when the timeout elapses.\n * Returns true if the promise resolved in time, false on timeout.\n */\nfunction withTimeout(promise: Promise<unknown>, timeoutMs: number): Promise<boolean> {\n return new Promise<boolean>(resolve => {\n let settled = false;\n const timer = setTimeout(() => {\n if (!settled) {\n settled = true;\n resolve(false);\n }\n }, timeoutMs);\n\n promise.then(\n () => {\n if (!settled) {\n settled = true;\n clearTimeout(timer);\n resolve(true);\n }\n },\n () => {\n // A rejected upgrade promise still means \"no longer pending\".\n if (!settled) {\n settled = true;\n clearTimeout(timer);\n resolve(true);\n }\n }\n );\n });\n}\n\n/**\n * Collect distinct custom-element tag names present under a root.\n * A custom element tag always contains a hyphen.\n */\nfunction collectCustomElementTags(root: Document | Element): string[] {\n const tags = new Set<string>();\n const rootEl: Element | null =\n 'documentElement' in root ? (root as Document).documentElement : (root as Element);\n if (!rootEl) return [];\n\n const consider = (el: Element) => {\n if (el.localName && el.localName.includes('-')) {\n tags.add(el.localName);\n }\n };\n\n consider(rootEl);\n const all = rootEl.querySelectorAll('*');\n all.forEach(consider);\n\n return Array.from(tags);\n}\n\n/**\n * Collect elements that expose an `updateComplete` thenable (e.g. Lit).\n */\nfunction collectUpdateCompletables(root: Document | Element): Promise<unknown>[] {\n const rootEl: Element | null =\n 'documentElement' in root ? (root as Document).documentElement : (root as Element);\n if (!rootEl) return [];\n\n const promises: Promise<unknown>[] = [];\n const consider = (el: Element) => {\n const uc = (el as unknown as { updateComplete?: unknown }).updateComplete;\n if (uc && typeof (uc as Promise<unknown>).then === 'function') {\n promises.push(uc as Promise<unknown>);\n }\n };\n\n consider(rootEl);\n rootEl.querySelectorAll('*').forEach(consider);\n return promises;\n}\n\n/**\n * Wait for custom elements under `root` to upgrade and render.\n *\n * - Awaits `customElements.whenDefined(tag)` for each custom tag (bounded).\n * - Awaits any `updateComplete` promises found (bounded).\n * - On timeout, resolves anyway and returns a warning per unresolved tag.\n * - Never blocks indefinitely. No-op when `customElements` is unavailable.\n */\nexport async function awaitCustomElementsReady(\n root: Document | Element,\n timeoutMs: number = DEFAULT_UPGRADE_TIMEOUT_MS\n): Promise<UpgradeWarning[]> {\n const warnings: UpgradeWarning[] = [];\n\n const hasCustomElements =\n typeof customElements !== 'undefined' && customElements !== null;\n\n if (!hasCustomElements) {\n return warnings;\n }\n\n const tags = collectCustomElementTags(root);\n\n // Await definitions.\n await Promise.all(\n tags.map(async tag => {\n // Already defined — nothing to wait for.\n if (customElements.get(tag)) return;\n const defined = await withTimeout(customElements.whenDefined(tag), timeoutMs);\n if (!defined) {\n warnings.push({\n tag,\n message: `Custom element \"${tag}\" did not upgrade within ${timeoutMs}ms; analysis may reflect a pre-hydration state.`,\n });\n }\n })\n );\n\n // Await render completion where components expose it (Lit et al.).\n const updateCompletables = collectUpdateCompletables(root);\n if (updateCompletables.length > 0) {\n await withTimeout(Promise.all(updateCompletables), timeoutMs);\n }\n\n return warnings;\n}\n"],"mappings":";AAcA,IAAM,6BAA6B;AAMnC,SAAS,YAAY,SAA2B,WAAqC;AACnF,SAAO,IAAI,QAAiB,aAAW;AACrC,QAAI,UAAU;AACd,UAAM,QAAQ,WAAW,MAAM;AAC7B,UAAI,CAAC,SAAS;AACZ,kBAAU;AACV,gBAAQ,KAAK;AAAA,MACf;AAAA,IACF,GAAG,SAAS;AAEZ,YAAQ;AAAA,MACN,MAAM;AACJ,YAAI,CAAC,SAAS;AACZ,oBAAU;AACV,uBAAa,KAAK;AAClB,kBAAQ,IAAI;AAAA,QACd;AAAA,MACF;AAAA,MACA,MAAM;AAEJ,YAAI,CAAC,SAAS;AACZ,oBAAU;AACV,uBAAa,KAAK;AAClB,kBAAQ,IAAI;AAAA,QACd;AAAA,MACF;AAAA,IACF;AAAA,EACF,CAAC;AACH;AAMA,SAAS,yBAAyB,MAAoC;AACpE,QAAM,OAAO,oBAAI,IAAY;AAC7B,QAAM,SACJ,qBAAqB,OAAQ,KAAkB,kBAAmB;AACpE,MAAI,CAAC,OAAQ,QAAO,CAAC;AAErB,QAAM,WAAW,CAAC,OAAgB;AAChC,QAAI,GAAG,aAAa,GAAG,UAAU,SAAS,GAAG,GAAG;AAC9C,WAAK,IAAI,GAAG,SAAS;AAAA,IACvB;AAAA,EACF;AAEA,WAAS,MAAM;AACf,QAAM,MAAM,OAAO,iBAAiB,GAAG;AACvC,MAAI,QAAQ,QAAQ;AAEpB,SAAO,MAAM,KAAK,IAAI;AACxB;AAKA,SAAS,0BAA0B,MAA8C;AAC/E,QAAM,SACJ,qBAAqB,OAAQ,KAAkB,kBAAmB;AACpE,MAAI,CAAC,OAAQ,QAAO,CAAC;AAErB,QAAM,WAA+B,CAAC;AACtC,QAAM,WAAW,CAAC,OAAgB;AAChC,UAAM,KAAM,GAA+C;AAC3D,QAAI,MAAM,OAAQ,GAAwB,SAAS,YAAY;AAC7D,eAAS,KAAK,EAAsB;AAAA,IACtC;AAAA,EACF;AAEA,WAAS,MAAM;AACf,SAAO,iBAAiB,GAAG,EAAE,QAAQ,QAAQ;AAC7C,SAAO;AACT;AAUA,eAAsB,yBACpB,MACA,YAAoB,4BACO;AAC3B,QAAM,WAA6B,CAAC;AAEpC,QAAM,oBACJ,OAAO,mBAAmB,eAAe,mBAAmB;AAE9D,MAAI,CAAC,mBAAmB;AACtB,WAAO;AAAA,EACT;AAEA,QAAM,OAAO,yBAAyB,IAAI;AAG1C,QAAM,QAAQ;AAAA,IACZ,KAAK,IAAI,OAAM,QAAO;AAEpB,UAAI,eAAe,IAAI,GAAG,EAAG;AAC7B,YAAM,UAAU,MAAM,YAAY,eAAe,YAAY,GAAG,GAAG,SAAS;AAC5E,UAAI,CAAC,SAAS;AACZ,iBAAS,KAAK;AAAA,UACZ;AAAA,UACA,SAAS,mBAAmB,GAAG,4BAA4B,SAAS;AAAA,QACtE,CAAC;AAAA,MACH;AAAA,IACF,CAAC;AAAA,EACH;AAGA,QAAM,qBAAqB,0BAA0B,IAAI;AACzD,MAAI,mBAAmB,SAAS,GAAG;AACjC,UAAM,YAAY,QAAQ,IAAI,kBAAkB,GAAG,SAAS;AAAA,EAC9D;AAEA,SAAO;AACT;","names":[]}
package/dist/cli.js CHANGED
@@ -526,6 +526,12 @@ var SUPPORTED_ROLES = [
526
526
  "meter",
527
527
  "progressbar",
528
528
  "status",
529
+ "switch",
530
+ "slider",
531
+ "spinbutton",
532
+ "tab",
533
+ "tablist",
534
+ "tabpanel",
529
535
  "document",
530
536
  "application",
531
537
  "separator"
@@ -622,7 +628,10 @@ var INPUT_TYPE_ROLE_MAP = {
622
628
  "search": "textbox",
623
629
  "tel": "textbox",
624
630
  "url": "textbox",
625
- "number": "textbox"
631
+ // Per ARIA, <input type="number"> has an implicit role of spinbutton
632
+ // (VoiceOver announces it as "stepper").
633
+ "number": "spinbutton",
634
+ "range": "slider"
626
635
  };
627
636
  function computeRole(element) {
628
637
  const warnings = [];
@@ -655,6 +664,11 @@ function computeImplicitRole(element, _warnings) {
655
664
  if (tagName === "input") {
656
665
  return computeInputRole(element);
657
666
  }
667
+ if (tagName === "select") {
668
+ const multiple = element.hasAttribute("multiple");
669
+ const size = parseInt(element.getAttribute("size") || "0", 10);
670
+ return multiple || size > 1 ? "listbox" : "combobox";
671
+ }
658
672
  if (tagName === "a") {
659
673
  return element.hasAttribute("href") ? "link" : null;
660
674
  }
@@ -1064,6 +1078,7 @@ function processChildNode(node, children, warnings) {
1064
1078
  if (node.nodeType === 1) {
1065
1079
  const element = node;
1066
1080
  if (isHidden(element)) return;
1081
+ if (isConsumedLabel(element)) return;
1067
1082
  if (!isAccessible(element)) {
1068
1083
  const grandchildren = collectChildrenFromRolelessElement(element, warnings);
1069
1084
  children.push(...grandchildren);
@@ -1073,18 +1088,74 @@ function processChildNode(node, children, warnings) {
1073
1088
  if (childNode) children.push(childNode);
1074
1089
  }
1075
1090
  }
1091
+ function isConsumedLabel(element) {
1092
+ if (element.tagName.toLowerCase() !== "label") return false;
1093
+ const forId = element.getAttribute("for");
1094
+ if (forId) {
1095
+ const doc = element.ownerDocument;
1096
+ const target = doc?.getElementById(forId);
1097
+ if (target && isLabelableControl(target)) return true;
1098
+ }
1099
+ const wrapped = element.querySelector("input, select, textarea, button, meter, progress");
1100
+ if (wrapped && isLabelableControl(wrapped)) return true;
1101
+ return false;
1102
+ }
1103
+ function isLabelableControl(element) {
1104
+ const tag = element.tagName.toLowerCase();
1105
+ if (tag === "input") {
1106
+ return (element.getAttribute("type") || "text").toLowerCase() !== "hidden";
1107
+ }
1108
+ return tag === "select" || tag === "textarea" || tag === "button" || tag === "meter" || tag === "progress";
1109
+ }
1076
1110
  function collectChildrenFromRolelessElement(element, warnings) {
1111
+ return collectAccessibleChildren(element, warnings);
1112
+ }
1113
+ function collectAccessibleChildren(element, warnings) {
1077
1114
  const children = [];
1078
1115
  if (element.shadowRoot) {
1079
1116
  for (const child of Array.from(element.shadowRoot.childNodes)) {
1080
- processChildNode(child, children, warnings);
1117
+ processShadowNode(child, children, warnings);
1081
1118
  }
1119
+ return children;
1120
+ }
1121
+ if (element.localName && element.localName.includes("-") && element.childNodes.length === 0) {
1122
+ warnings.push({
1123
+ message: `Custom element <${element.localName}> has no accessible children and may have a closed shadow root; its content could not be analyzed.`,
1124
+ element
1125
+ });
1082
1126
  }
1083
1127
  for (const child of Array.from(element.childNodes)) {
1084
1128
  processChildNode(child, children, warnings);
1085
1129
  }
1086
1130
  return children;
1087
1131
  }
1132
+ function processShadowNode(node, children, warnings) {
1133
+ if (node.nodeType === 1) {
1134
+ const element = node;
1135
+ if (element.localName === "slot") {
1136
+ const slot = element;
1137
+ let assigned = [];
1138
+ if (typeof slot.assignedNodes === "function") {
1139
+ try {
1140
+ assigned = Array.from(slot.assignedNodes({ flatten: true }));
1141
+ } catch {
1142
+ assigned = [];
1143
+ }
1144
+ }
1145
+ if (assigned.length > 0) {
1146
+ for (const assignedNode of assigned) {
1147
+ processChildNode(assignedNode, children, warnings);
1148
+ }
1149
+ } else {
1150
+ for (const child of Array.from(slot.childNodes)) {
1151
+ processShadowNode(child, children, warnings);
1152
+ }
1153
+ }
1154
+ return;
1155
+ }
1156
+ }
1157
+ processChildNode(node, children, warnings);
1158
+ }
1088
1159
  function buildAccessibilityTree(rootElement, sourceHash) {
1089
1160
  const warnings = [];
1090
1161
  if (isHidden(rootElement)) {
@@ -1141,6 +1212,30 @@ function buildAccessibilityTreeWithSelector(rootElement, selector, sourceHash) {
1141
1212
  }
1142
1213
  return results;
1143
1214
  }
1215
+ var SET_MEMBER_ROLES = /* @__PURE__ */ new Set([
1216
+ "radio",
1217
+ "option",
1218
+ "listitem",
1219
+ "tab"
1220
+ ]);
1221
+ function assignImplicitSetPosition(children) {
1222
+ const totals = /* @__PURE__ */ new Map();
1223
+ for (const child of children) {
1224
+ if (SET_MEMBER_ROLES.has(child.role)) {
1225
+ totals.set(child.role, (totals.get(child.role) ?? 0) + 1);
1226
+ }
1227
+ }
1228
+ const seen = /* @__PURE__ */ new Map();
1229
+ for (const child of children) {
1230
+ if (!SET_MEMBER_ROLES.has(child.role)) continue;
1231
+ const total = totals.get(child.role);
1232
+ if (total < 2) continue;
1233
+ const pos = (seen.get(child.role) ?? 0) + 1;
1234
+ seen.set(child.role, pos);
1235
+ if (child.state.posinset === void 0) child.state.posinset = pos;
1236
+ if (child.state.setsize === void 0) child.state.setsize = total;
1237
+ }
1238
+ }
1144
1239
  function buildNodeRecursive(element, warnings) {
1145
1240
  if (isHidden(element)) {
1146
1241
  return null;
@@ -1177,15 +1272,15 @@ function buildNodeRecursive(element, warnings) {
1177
1272
  element: w.element
1178
1273
  })));
1179
1274
  const focusInfo = extractFocusInfo(element);
1180
- const children = [];
1181
- if (element.shadowRoot) {
1182
- for (const child of Array.from(element.shadowRoot.childNodes)) {
1183
- processChildNode(child, children, warnings);
1275
+ let children = collectAccessibleChildren(element, warnings);
1276
+ const isNativeSelect = element.tagName.toLowerCase() === "select";
1277
+ if (isNativeSelect && roleResult.role === "combobox") {
1278
+ children = [];
1279
+ if (stateResult.state.expanded === void 0) {
1280
+ stateResult.state.expanded = false;
1184
1281
  }
1185
1282
  }
1186
- for (const child of Array.from(element.childNodes)) {
1187
- processChildNode(child, children, warnings);
1188
- }
1283
+ assignImplicitSetPosition(children);
1189
1284
  const node = {
1190
1285
  role: roleResult.role,
1191
1286
  name: nameResult.name,
@@ -1198,15 +1293,7 @@ function buildNodeRecursive(element, warnings) {
1198
1293
  return node;
1199
1294
  }
1200
1295
  function createGenericContainer(rootElement, warnings) {
1201
- const children = [];
1202
- if (rootElement.shadowRoot) {
1203
- for (const child of Array.from(rootElement.shadowRoot.childNodes)) {
1204
- processChildNode(child, children, warnings);
1205
- }
1206
- }
1207
- for (const child of Array.from(rootElement.childNodes)) {
1208
- processChildNode(child, children, warnings);
1209
- }
1296
+ const children = collectAccessibleChildren(rootElement, warnings);
1210
1297
  return {
1211
1298
  role: "generic",
1212
1299
  name: "",
@@ -1828,8 +1915,45 @@ function renderNodeVoiceOver(node, announcements, c) {
1828
1915
  renderNodeVoiceOver(child, announcements, c);
1829
1916
  }
1830
1917
  }
1918
+ function isValueFirstWidget(role) {
1919
+ return role === "slider" || role === "spinbutton" || role === "combobox";
1920
+ }
1921
+ function isValueBeforeRoleWidget(role) {
1922
+ return role === "progressbar" || role === "meter";
1923
+ }
1924
+ function isSilentContainerVoiceOver(node) {
1925
+ if (node.role !== "list" && node.role !== "status") return false;
1926
+ if (node.name) return false;
1927
+ return node.children.length > 0;
1928
+ }
1831
1929
  function formatNodeVoiceOver(node, c) {
1930
+ if (isSilentContainerVoiceOver(node)) {
1931
+ return "";
1932
+ }
1832
1933
  const parts = [];
1934
+ const valueFirst = isValueFirstWidget(node.role) && node.value !== void 0;
1935
+ if (valueFirst) {
1936
+ const valueText = node.value.text || String(node.value.current);
1937
+ if (valueText) parts.push(valueText);
1938
+ if (node.name) parts.push(c.elementName(node.name));
1939
+ const roleText = formatRoleVoiceOver(node);
1940
+ if (roleText) parts.push(c.roleName(roleText));
1941
+ const stateText2 = formatStatesVoiceOver(node);
1942
+ if (stateText2) parts.push(c.stateName(stateText2));
1943
+ if (node.description) parts.push(c.description(node.description));
1944
+ return parts.join(", ");
1945
+ }
1946
+ if (isValueBeforeRoleWidget(node.role) && node.value !== void 0) {
1947
+ if (node.name) parts.push(c.elementName(node.name));
1948
+ const valueText = node.value.text || String(node.value.current);
1949
+ if (valueText) parts.push(valueText);
1950
+ const roleText = formatRoleVoiceOver(node);
1951
+ if (roleText) parts.push(c.roleName(roleText));
1952
+ const stateText2 = formatStatesVoiceOver(node);
1953
+ if (stateText2) parts.push(c.stateName(stateText2));
1954
+ if (node.description) parts.push(c.description(node.description));
1955
+ return parts.join(", ");
1956
+ }
1833
1957
  const roleFirst = shouldAnnounceRoleFirst(node.role);
1834
1958
  if (roleFirst) {
1835
1959
  const roleText = formatRoleVoiceOver(node);
@@ -1858,6 +1982,9 @@ function formatNodeVoiceOver(node, c) {
1858
1982
  parts.push(valueText);
1859
1983
  }
1860
1984
  }
1985
+ if (node.state.posinset !== void 0 && node.state.setsize !== void 0) {
1986
+ parts.push(`${node.state.posinset} of ${node.state.setsize}`);
1987
+ }
1861
1988
  if (node.description) {
1862
1989
  parts.push(c.description(node.description));
1863
1990
  }
@@ -1893,7 +2020,7 @@ function formatRoleVoiceOver(node) {
1893
2020
  case "list":
1894
2021
  return "list";
1895
2022
  case "listitem":
1896
- return "item";
2023
+ return "";
1897
2024
  case "navigation":
1898
2025
  return "navigation";
1899
2026
  case "main":
@@ -1949,6 +2076,18 @@ function formatRoleVoiceOver(node) {
1949
2076
  return "progress indicator";
1950
2077
  case "status":
1951
2078
  return "status";
2079
+ case "switch":
2080
+ return "switch";
2081
+ case "slider":
2082
+ return "slider";
2083
+ case "spinbutton":
2084
+ return "stepper";
2085
+ case "tab":
2086
+ return "tab";
2087
+ case "tablist":
2088
+ return node.name ? "tab group" : "tab group";
2089
+ case "tabpanel":
2090
+ return "tab panel";
1952
2091
  case "group":
1953
2092
  return node.name ? "group" : "";
1954
2093
  case "document":
@@ -1969,6 +2108,10 @@ function formatStatesVoiceOver(node) {
1969
2108
  if (node.state.checked !== void 0) {
1970
2109
  if (node.state.checked === "mixed") {
1971
2110
  states.push("mixed");
2111
+ } else if (node.role === "switch") {
2112
+ states.push(node.state.checked ? "on" : "off");
2113
+ } else if (node.role === "radio") {
2114
+ if (node.state.checked) states.push("selected");
1972
2115
  } else {
1973
2116
  states.push(node.state.checked ? "checked" : "unchecked");
1974
2117
  }
@@ -1977,10 +2120,10 @@ function formatStatesVoiceOver(node) {
1977
2120
  if (node.state.pressed === "mixed") {
1978
2121
  states.push("mixed");
1979
2122
  } else {
1980
- states.push(node.state.pressed ? "pressed" : "not pressed");
2123
+ states.push(node.state.pressed ? "selected" : "unselected");
1981
2124
  }
1982
2125
  }
1983
- if (node.state.selected !== void 0) {
2126
+ if (node.state.selected !== void 0 && node.role !== "tab") {
1984
2127
  states.push(node.state.selected ? "selected" : "unselected");
1985
2128
  }
1986
2129
  if (node.state.disabled) {