-
Notifications
You must be signed in to change notification settings - Fork 278
Expand file tree
/
Copy pathCustomAnnouncement.ts
More file actions
124 lines (104 loc) · 3.94 KB
/
CustomAnnouncement.ts
File metadata and controls
124 lines (104 loc) · 3.94 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
import I18nBundle from "@ui5/webcomponents-base/dist/i18nBundle.js";
import { getTabbableElements } from "@ui5/webcomponents-base/dist/util/TabbableElements.js";
import type { AccessibilityInfo } from "@ui5/webcomponents-base";
import {
ACC_STATE_EMPTY,
ACC_STATE_REQUIRED,
ACC_STATE_DISABLED,
ACC_STATE_READONLY,
ACC_STATE_SINGLE_CONTROL,
ACC_STATE_MULTIPLE_CONTROLS,
} from "./generated/i18n/i18n-defaults.js";
let i18nBundle: I18nBundle;
let invisibleText: HTMLElement;
const getBundle = (): I18nBundle => {
i18nBundle ??= new I18nBundle("@ui5/webcomponents-base");
return i18nBundle;
};
const checkVisibility = (element: HTMLElement): boolean => {
return element.checkVisibility() || getComputedStyle(element).display === "contents";
};
const applyCustomAnnouncement = (element: HTMLElement, text: string | string[] = []) => {
if (!invisibleText || !invisibleText.isConnected) {
invisibleText = document.createElement("span");
invisibleText.id = "ui5-invisible-text";
invisibleText.hidden = true;
document.body.appendChild(invisibleText);
}
const ariaLabelledByElements = [...((element as any).ariaLabelledByElements || [])];
const invisibleTextIndex = ariaLabelledByElements.indexOf(invisibleText);
text = Array.isArray(text) ? text.filter(Boolean).join(" . ").trim() : text.trim();
invisibleText.textContent = text;
if (text && invisibleTextIndex === -1) {
ariaLabelledByElements.unshift(invisibleText);
(element as any).ariaLabelledByElements = ariaLabelledByElements;
} else if (!text && invisibleTextIndex > -1) {
ariaLabelledByElements.splice(invisibleTextIndex, 1);
(element as any).ariaLabelledByElements = ariaLabelledByElements.length ? ariaLabelledByElements : null;
}
};
type CustomAnnouncementOptions = {
lessDetails?: boolean;
};
const getCustomAnnouncement = (element: Node, options: CustomAnnouncementOptions = {}, _isRootElement: boolean = true): string => {
if (!element) {
return "";
}
if (element.nodeType === Node.TEXT_NODE) {
return (element as Text).data.trim();
}
if (!(element instanceof HTMLElement)) {
return "";
}
if (element.hasAttribute("data-ui5-acc-text")) {
return element.getAttribute("data-ui5-acc-text") || "";
}
if (element.ariaHidden === "true" || !checkVisibility(element)) {
return _isRootElement ? getBundle().getText(ACC_STATE_EMPTY) : "";
}
let childNodes = [] as Array<Node>;
const descriptions = [] as Array<string>;
const accessibilityInfo = (element as any).accessibilityInfo as AccessibilityInfo | undefined;
const { lessDetails } = options;
if (accessibilityInfo) {
const {
type, description, required, disabled, readonly, children,
} = accessibilityInfo;
childNodes = children || [];
type && descriptions.push(type);
description && descriptions.push(description);
if (!lessDetails) {
required && descriptions.push(getBundle().getText(ACC_STATE_REQUIRED));
disabled && descriptions.push(getBundle().getText(ACC_STATE_DISABLED));
readonly && descriptions.push(getBundle().getText(ACC_STATE_READONLY));
}
} else if (element.localName === "slot") {
childNodes = (element as HTMLSlotElement).assignedNodes({ flatten: true });
} else {
childNodes = element.shadowRoot ? [...element.shadowRoot.childNodes] : [...element.childNodes];
}
childNodes.forEach(child => {
const childDescription = getCustomAnnouncement(child, options, false);
childDescription && descriptions.push(childDescription);
});
if (_isRootElement) {
const hasDescription = descriptions.length > 0;
if (!hasDescription || !lessDetails) {
const tabbables = getTabbableElements(element);
const bundleKey = [
hasDescription ? "" : ACC_STATE_EMPTY,
ACC_STATE_SINGLE_CONTROL,
ACC_STATE_MULTIPLE_CONTROLS,
][Math.min(tabbables.length, 2)];
if (bundleKey) {
hasDescription && descriptions.push(".");
descriptions.push(getBundle().getText(bundleKey));
}
}
}
return descriptions.join(" ").trim();
};
export {
getCustomAnnouncement,
applyCustomAnnouncement,
};