@creative-web-solution/front-library 7.1.52 → 7.1.54
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/CHANGELOG.md
CHANGED
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
type AriaAnnouncementHelperBaseOptions = {
|
|
2
|
+
/** In words/minute. Average is between 150 and 200
|
|
3
|
+
* @default 175
|
|
4
|
+
*/
|
|
5
|
+
estimatedSpeechSpeed?: number;
|
|
6
|
+
};
|
|
7
|
+
type AriaAnnouncementHelperSelectorsOptions =
|
|
8
|
+
AriaAnnouncementHelperBaseOptions & {
|
|
9
|
+
assertiveAreaSelector: string;
|
|
10
|
+
politeAreaSelector: string;
|
|
11
|
+
};
|
|
12
|
+
type AriaAnnouncementHelperCssClassOptions =
|
|
13
|
+
AriaAnnouncementHelperBaseOptions & {
|
|
14
|
+
areaCssClass?: string;
|
|
15
|
+
};
|
|
16
|
+
type AriaAnnouncementOptions = OneOf<
|
|
17
|
+
[
|
|
18
|
+
AriaAnnouncementHelperSelectorsOptions,
|
|
19
|
+
AriaAnnouncementHelperCssClassOptions,
|
|
20
|
+
]
|
|
21
|
+
>;
|
|
22
|
+
|
|
23
|
+
type MessagePriority = "polite" | "assertive";
|
|
24
|
+
|
|
25
|
+
const PRIORITIES: Record<MessagePriority, MessagePriority> = {
|
|
26
|
+
assertive: "assertive",
|
|
27
|
+
polite: "polite",
|
|
28
|
+
} as const;
|
|
29
|
+
|
|
30
|
+
const ESTIMATED_WORDS_BY_MINUTES_SPEED = 175;
|
|
31
|
+
|
|
32
|
+
export default class AriaAnnouncementHelper {
|
|
33
|
+
#$assertiveArea!: HTMLElement;
|
|
34
|
+
#estimatedSpeechSpeed: number;
|
|
35
|
+
#isSpeeching: boolean = false;
|
|
36
|
+
#messages: {
|
|
37
|
+
message: string;
|
|
38
|
+
priority: MessagePriority;
|
|
39
|
+
}[] = [];
|
|
40
|
+
#options?: AriaAnnouncementOptions;
|
|
41
|
+
#$politeArea!: HTMLElement;
|
|
42
|
+
#rafId: number = -1;
|
|
43
|
+
#timeoutId: number = -1;
|
|
44
|
+
|
|
45
|
+
constructor(options?: AriaAnnouncementOptions) {
|
|
46
|
+
this.#options = options;
|
|
47
|
+
this.#estimatedSpeechSpeed =
|
|
48
|
+
this.#options?.estimatedSpeechSpeed ??
|
|
49
|
+
ESTIMATED_WORDS_BY_MINUTES_SPEED;
|
|
50
|
+
this.#createElements();
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
#createElements(): void {
|
|
54
|
+
this.#$politeArea = this.#createArea(
|
|
55
|
+
PRIORITIES.polite,
|
|
56
|
+
(this.#options as AriaAnnouncementHelperSelectorsOptions)
|
|
57
|
+
?.politeAreaSelector,
|
|
58
|
+
);
|
|
59
|
+
this.#$assertiveArea = this.#createArea(
|
|
60
|
+
PRIORITIES.assertive,
|
|
61
|
+
(this.#options as AriaAnnouncementHelperSelectorsOptions)
|
|
62
|
+
?.assertiveAreaSelector,
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
#createArea(priority: MessagePriority, selector?: string): HTMLElement {
|
|
67
|
+
let $element: HTMLElement | null = null;
|
|
68
|
+
if (selector) {
|
|
69
|
+
$element = document.querySelector<HTMLElement>(selector);
|
|
70
|
+
}
|
|
71
|
+
if (!$element) {
|
|
72
|
+
$element = document.createElement("div");
|
|
73
|
+
$element.id = `announcement-${priority}`;
|
|
74
|
+
$element.setAttribute("aria-live", priority);
|
|
75
|
+
$element.setAttribute("aria-atomic", "true");
|
|
76
|
+
const areaCssClass = (
|
|
77
|
+
this.#options as AriaAnnouncementHelperCssClassOptions
|
|
78
|
+
)?.areaCssClass;
|
|
79
|
+
if (areaCssClass) {
|
|
80
|
+
$element.classList.add(areaCssClass);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
document.body.appendChild($element);
|
|
84
|
+
|
|
85
|
+
return $element;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
assertiveMessage(message: string): this {
|
|
89
|
+
this.#addMessage(message, PRIORITIES.assertive);
|
|
90
|
+
return this;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
politeMessage(message: string): this {
|
|
94
|
+
this.#addMessage(message, PRIORITIES.polite);
|
|
95
|
+
return this;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
#addMessage(message: string, priority: MessagePriority): void {
|
|
99
|
+
this.#messages.push({ message, priority });
|
|
100
|
+
this.#processMessages();
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
#processMessages(): void {
|
|
104
|
+
if (this.#isSpeeching || this.#messages.length === 0) {
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
this.#isSpeeching = true;
|
|
108
|
+
this.#clearZones();
|
|
109
|
+
|
|
110
|
+
const { message, priority } = this.#messages.shift()!;
|
|
111
|
+
|
|
112
|
+
this.#updateZone(
|
|
113
|
+
message,
|
|
114
|
+
priority === PRIORITIES.assertive
|
|
115
|
+
? this.#$assertiveArea
|
|
116
|
+
: this.#$politeArea,
|
|
117
|
+
);
|
|
118
|
+
|
|
119
|
+
this.#timeoutId = setTimeout(() => {
|
|
120
|
+
this.#isSpeeching = false;
|
|
121
|
+
this.#processMessages();
|
|
122
|
+
}, this.#timeEstimationInMs(message));
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
#updateZone(message: string, $element: HTMLElement): void {
|
|
126
|
+
this.#rafId = requestAnimationFrame(() => {
|
|
127
|
+
$element.textContent = message;
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
#timeEstimationInMs(texte: string): number {
|
|
132
|
+
const wordsCount = texte.split(" ").length;
|
|
133
|
+
return (wordsCount / this.#estimatedSpeechSpeed) * 60 * 1000;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
clearAll(): this {
|
|
137
|
+
this.#messages.length = 0;
|
|
138
|
+
clearTimeout(this.#timeoutId);
|
|
139
|
+
cancelAnimationFrame(this.#rafId);
|
|
140
|
+
this.#clearZones();
|
|
141
|
+
return this;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
#clearZones(): void {
|
|
145
|
+
this.#$politeArea.textContent = "";
|
|
146
|
+
this.#$assertiveArea.textContent = "";
|
|
147
|
+
}
|
|
148
|
+
}
|
|
@@ -8,12 +8,12 @@ export default abstract class AbstractAutocompleteAdapter<ItemDataType> {
|
|
|
8
8
|
query: string,
|
|
9
9
|
$input: HTMLElement,
|
|
10
10
|
): Record<string, any>;
|
|
11
|
-
|
|
11
|
+
markValue?(options: {
|
|
12
12
|
item: ItemDataType;
|
|
13
13
|
index: number;
|
|
14
14
|
query: string;
|
|
15
15
|
}): ItemDataType;
|
|
16
|
-
|
|
16
|
+
updateInputValueFromItem?(options: {
|
|
17
17
|
item: ItemDataType;
|
|
18
18
|
index: number;
|
|
19
19
|
query: string;
|
|
@@ -25,7 +25,6 @@ type AutocompleteOptions<ItemDataType> = {
|
|
|
25
25
|
results: ItemDataType[];
|
|
26
26
|
}) => void;
|
|
27
27
|
$searchField: HTMLInputElement;
|
|
28
|
-
shouldMarkValue?: boolean;
|
|
29
28
|
};
|
|
30
29
|
|
|
31
30
|
const PREVENT_DEFAULT_KEY_LIST = [
|
|
@@ -49,7 +48,6 @@ const DEFAULT_OPTIONS = {
|
|
|
49
48
|
layerPosition: "top",
|
|
50
49
|
$layerWrapper: document.body,
|
|
51
50
|
minChar: 3,
|
|
52
|
-
shouldMarkValue: false,
|
|
53
51
|
};
|
|
54
52
|
|
|
55
53
|
export default class Autocomplete<ItemDataType> {
|
|
@@ -222,13 +220,11 @@ export default class Autocomplete<ItemDataType> {
|
|
|
222
220
|
this.resetResults();
|
|
223
221
|
|
|
224
222
|
this.#currentResults = results.map((item, index) => {
|
|
225
|
-
const markedItem = this.#
|
|
226
|
-
? this.#adapter.markValue({
|
|
223
|
+
const markedItem = this.#adapter.markValue?.({
|
|
227
224
|
item,
|
|
228
225
|
index,
|
|
229
226
|
query: this.#currentQuery,
|
|
230
|
-
})
|
|
231
|
-
: item;
|
|
227
|
+
}) ?? item;
|
|
232
228
|
const $item = strToDOM(
|
|
233
229
|
this.#adapter.itemRender({
|
|
234
230
|
item: markedItem,
|
package/README.md
CHANGED
package/Types/Global.d.ts
CHANGED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
type ArrayElement<ArrayType extends readonly unknown[]> =
|
|
2
|
+
ArrayType extends readonly (infer ElementType)[] ? ElementType : never;
|
|
3
|
+
|
|
4
|
+
type MergeTypes<TypesArray extends any[], Res = {}> = TypesArray extends [
|
|
5
|
+
infer Head,
|
|
6
|
+
...infer Rem,
|
|
7
|
+
]
|
|
8
|
+
? MergeTypes<Rem, Res & Head>
|
|
9
|
+
: Res;
|
|
10
|
+
|
|
11
|
+
type OnlyFirst<F, S> = F & { [Key in keyof Omit<S, keyof F>]?: never };
|
|
12
|
+
|
|
13
|
+
type OneOf<
|
|
14
|
+
TypesArray extends any[],
|
|
15
|
+
Res = never,
|
|
16
|
+
AllProperties = MergeTypes<TypesArray>,
|
|
17
|
+
> = TypesArray extends [infer Head, ...infer Rem]
|
|
18
|
+
? OneOf<Rem, Res | OnlyFirst<Head, AllProperties>, AllProperties>
|
|
19
|
+
: Res;
|
|
20
|
+
|
|
21
|
+
type Nullable<T> = T | null | undefined;
|
|
22
|
+
|
|
23
|
+
type NullableRecord<T> = {
|
|
24
|
+
[K in keyof T]: T[K] | null | undefined;
|
|
25
|
+
};
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "@creative-web-solution/front-library",
|
|
3
3
|
"title": "Frontend library",
|
|
4
4
|
"description": "Frontend functions and modules",
|
|
5
|
-
"version": "7.1.
|
|
5
|
+
"version": "7.1.54",
|
|
6
6
|
"homepage": "https://github.com/creative-web-solution/front-library",
|
|
7
7
|
"author": "Creative Web Solution <contact@cws-studio.com> (https://www.cws-studio.com)",
|
|
8
8
|
"keywords": [],
|