ngp-accessibility 1.0.2 → 1.0.5

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.esm.js CHANGED
@@ -2,46 +2,81 @@ import React, { createContext, useState, useEffect, useContext } from 'react';
2
2
 
3
3
  const translations = {
4
4
  en: {
5
- increaseText: 'Increase Text',
6
- decreaseText: 'Decrease Text',
7
- highContrast: 'High Contrast',
8
- negativeContrast: 'Negative Contrast',
9
- lightBackground: 'Light Background',
10
- underlineLinks: 'Underline Links',
11
- readableFont: 'Readable Font',
12
- voiceCommand: 'Voice Command',
5
+ increaseText: "Increase Text",
6
+ decreaseText: "Decrease Text",
7
+ textMagnifier: "Text Magnifier",
8
+ highContrast: "High Contrast",
9
+ negativeContrast: "Negative Contrast",
10
+ lightBackground: "Light Background",
11
+ underlineLinks: "Underline Links",
12
+ highlightTitles: "Highlight Titles",
13
+ readableFont: "Readable Font",
14
+ pauseAnimations: "Pause Animations",
15
+ readingGuide: "Reading Guide",
16
+ voiceCommand: "Voice Command",
13
17
  },
14
18
  tl: {
15
- increaseText: 'Palakihin ang Teksto',
16
- decreaseText: 'Paliitin ang Teksto',
17
- highContrast: 'Mataas na Contrast',
18
- negativeContrast: 'Negatibong Contrast',
19
- lightBackground: 'Maliwanag na Background',
20
- underlineLinks: 'May Guhit na Links',
21
- readableFont: 'Madaling Basahin na Font',
22
- voiceCommand: 'Voice Command',
19
+ increaseText: "Palakihin ang Teksto",
20
+ decreaseText: "Paliitin ang Teksto",
21
+ textMagnifier: "Text Magnifier",
22
+ highContrast: "Mataas na Contrast",
23
+ negativeContrast: "Negatibong Contrast",
24
+ lightBackground: "Maliwanag na Background",
25
+ underlineLinks: "May Guhit na Links",
26
+ highlightTitles: "I-highlight ang Mga Pamagat",
27
+ readableFont: "Madaling Basahin na Font",
28
+ pauseAnimations: "I-pause ang mga Animation",
29
+ readingGuide: "Reading Guide",
30
+ voiceCommand: "Voice Command",
23
31
  },
24
32
  ceb: {
25
- increaseText: 'Padak-on ang Teksto',
26
- decreaseText: 'Pagamay-on ang Teksto',
27
- highContrast: 'Taas nga Contrast',
28
- negativeContrast: 'Negatibo nga Contrast',
29
- lightBackground: 'Hayag nga Background',
30
- underlineLinks: 'Linya sa Ubos sa Links',
31
- readableFont: 'Sayon Basahon nga Font',
32
- voiceCommand: 'Voice Command',
33
+ increaseText: "Padak-on ang Teksto",
34
+ decreaseText: "Pagamay-on ang Teksto",
35
+ textMagnifier: "Text Magnifier",
36
+ highContrast: "Taas nga Contrast",
37
+ negativeContrast: "Negatibo nga Contrast",
38
+ lightBackground: "Hayag nga Background",
39
+ underlineLinks: "Linya sa Ubos sa Links",
40
+ highlightTitles: "I-highlight ang mga Ulohan",
41
+ readableFont: "Sayon Basahon nga Font",
42
+ pauseAnimations: "Ihunong ang mga Animation",
43
+ readingGuide: "Reading Guide",
44
+ voiceCommand: "Voice Command",
33
45
  },
34
46
  };
35
47
 
48
+ const normalizeMagnifierText = (value) => value.replace(/\s+/g, " ").trim();
49
+ const magnifierSelector = "p, li, button, h1, h2, h3, h4, h5, h6, [role=heading]";
50
+ const getMagnifierElement = (target) => {
51
+ if (!(target instanceof HTMLElement))
52
+ return null;
53
+ if (target.closest(".a11y-root"))
54
+ return null;
55
+ const element = target.closest(magnifierSelector);
56
+ return element instanceof HTMLElement ? element : null;
57
+ };
58
+ const getMagnifierText = (target) => {
59
+ const element = getMagnifierElement(target);
60
+ if (!element)
61
+ return "";
62
+ const ariaLabel = normalizeMagnifierText(element.getAttribute("aria-label") || "");
63
+ if (ariaLabel)
64
+ return ariaLabel;
65
+ return normalizeMagnifierText(element.innerText || element.textContent || "");
66
+ };
36
67
  const AccessibilityContext = createContext(undefined);
37
- const AccessibilityProvider = ({ children, translations: customTranslations }) => {
68
+ const AccessibilityProvider = ({ children, translations: customTranslations, }) => {
38
69
  const [state, setState] = useState({
39
- language: 'en',
70
+ language: "en",
40
71
  textSize: 100,
72
+ textMagnifier: false,
73
+ pauseAnimations: false,
74
+ readingGuide: false,
41
75
  highContrast: false,
42
76
  negativeContrast: false,
43
77
  lightBackground: false,
44
78
  underlineLinks: false,
79
+ highlightTitles: false,
45
80
  readableFont: false,
46
81
  voiceEnabled: false,
47
82
  });
@@ -49,57 +84,258 @@ const AccessibilityProvider = ({ children, translations: customTranslations }) =
49
84
  useEffect(() => {
50
85
  const root = document.documentElement;
51
86
  root.style.fontSize = `${state.textSize}%`;
52
- root.classList.toggle('high-contrast', state.highContrast);
53
- root.classList.toggle('negative-contrast', state.negativeContrast);
54
- root.classList.toggle('light-background', state.lightBackground);
55
- root.classList.toggle('underline-links', state.underlineLinks);
56
- root.classList.toggle('readable-font', state.readableFont);
87
+ root.classList.toggle("high-contrast", state.highContrast);
88
+ root.classList.toggle("negative-contrast", state.negativeContrast);
89
+ root.classList.toggle("light-background", state.lightBackground);
90
+ root.classList.toggle("underline-links", state.underlineLinks);
91
+ root.classList.toggle("highlight-titles", state.highlightTitles);
92
+ root.classList.toggle("readable-font", state.readableFont);
93
+ root.classList.toggle("pause-animations", state.pauseAnimations);
57
94
  }, [state]);
58
95
  useEffect(() => {
59
- if (!state.voiceEnabled || !('webkitSpeechRecognition' in window || 'SpeechRecognition' in window))
96
+ if (!state.readingGuide)
97
+ return;
98
+ const guide = document.createElement("div");
99
+ guide.className = "a11y-reading-guide";
100
+ guide.setAttribute("aria-hidden", "true");
101
+ guide.innerHTML = `
102
+ <div class="a11y-reading-guide-top"></div>
103
+ <div class="a11y-reading-guide-focus"></div>
104
+ <div class="a11y-reading-guide-line"></div>
105
+ <div class="a11y-reading-guide-bottom"></div>
106
+ `;
107
+ document.body.appendChild(guide);
108
+ const topMask = guide.querySelector(".a11y-reading-guide-top");
109
+ const focusBand = guide.querySelector(".a11y-reading-guide-focus");
110
+ const focusLine = guide.querySelector(".a11y-reading-guide-line");
111
+ const bottomMask = guide.querySelector(".a11y-reading-guide-bottom");
112
+ const bandHeight = 72;
113
+ const halfBandHeight = bandHeight / 2;
114
+ const updateGuide = (clientY) => {
115
+ const safeY = Math.min(Math.max(halfBandHeight + 12, clientY), window.innerHeight - halfBandHeight - 12);
116
+ const top = Math.max(0, safeY - halfBandHeight);
117
+ const bottom = Math.min(window.innerHeight, safeY + halfBandHeight);
118
+ topMask.style.height = `${top}px`;
119
+ focusBand.style.top = `${top}px`;
120
+ focusBand.style.height = `${bottom - top}px`;
121
+ focusLine.style.top = `${safeY}px`;
122
+ bottomMask.style.top = `${bottom}px`;
123
+ bottomMask.style.height = `${Math.max(0, window.innerHeight - bottom)}px`;
124
+ };
125
+ const resetGuide = () => updateGuide(window.innerHeight / 2);
126
+ const handleMouseMove = (event) => updateGuide(event.clientY);
127
+ const handleTouchStart = (event) => {
128
+ const touch = event.touches[0];
129
+ if (touch)
130
+ updateGuide(touch.clientY);
131
+ };
132
+ const handleTouchMove = (event) => {
133
+ const touch = event.touches[0];
134
+ if (touch)
135
+ updateGuide(touch.clientY);
136
+ };
137
+ resetGuide();
138
+ document.addEventListener("mousemove", handleMouseMove, true);
139
+ document.addEventListener("touchstart", handleTouchStart, true);
140
+ document.addEventListener("touchmove", handleTouchMove, true);
141
+ window.addEventListener("resize", resetGuide);
142
+ return () => {
143
+ document.removeEventListener("mousemove", handleMouseMove, true);
144
+ document.removeEventListener("touchstart", handleTouchStart, true);
145
+ document.removeEventListener("touchmove", handleTouchMove, true);
146
+ window.removeEventListener("resize", resetGuide);
147
+ guide.remove();
148
+ };
149
+ }, [state.readingGuide]);
150
+ useEffect(() => {
151
+ if (!state.textMagnifier)
152
+ return;
153
+ const tooltip = document.createElement("div");
154
+ tooltip.className = "a11y-text-magnifier-tooltip";
155
+ tooltip.setAttribute("aria-hidden", "true");
156
+ document.body.appendChild(tooltip);
157
+ const hideTooltip = () => {
158
+ tooltip.removeAttribute("data-visible");
159
+ tooltip.textContent = "";
160
+ };
161
+ const showTooltip = (text) => {
162
+ tooltip.textContent = text;
163
+ tooltip.setAttribute("data-visible", "true");
164
+ };
165
+ const updateTooltipPosition = (clientX, clientY) => {
166
+ const tooltipWidth = tooltip.offsetWidth || 320;
167
+ const tooltipHeight = tooltip.offsetHeight || 56;
168
+ const left = Math.min(Math.max(12, clientX + 18), window.innerWidth - tooltipWidth - 12);
169
+ const top = clientY + tooltipHeight + 18 > window.innerHeight
170
+ ? Math.max(12, clientY - tooltipHeight - 18)
171
+ : clientY + 18;
172
+ tooltip.style.left = `${left}px`;
173
+ tooltip.style.top = `${top}px`;
174
+ };
175
+ const handleMouseMove = (event) => {
176
+ const text = getMagnifierText(event.target);
177
+ if (!text) {
178
+ hideTooltip();
179
+ return;
180
+ }
181
+ showTooltip(text);
182
+ updateTooltipPosition(event.clientX, event.clientY);
183
+ };
184
+ let touchTimer = null;
185
+ let activeTouchId = null;
186
+ let activeText = "";
187
+ let pressPoint = null;
188
+ const clearTouchTimer = () => {
189
+ if (touchTimer !== null) {
190
+ window.clearTimeout(touchTimer);
191
+ touchTimer = null;
192
+ }
193
+ };
194
+ const resetTouchState = () => {
195
+ clearTouchTimer();
196
+ activeTouchId = null;
197
+ activeText = "";
198
+ pressPoint = null;
199
+ hideTooltip();
200
+ };
201
+ const getTrackedTouch = (touches) => {
202
+ if (activeTouchId === null)
203
+ return null;
204
+ for (const touch of Array.from(touches)) {
205
+ if (touch.identifier === activeTouchId)
206
+ return touch;
207
+ }
208
+ return null;
209
+ };
210
+ const handleTouchStart = (event) => {
211
+ const touch = event.changedTouches[0];
212
+ if (!touch)
213
+ return;
214
+ const text = getMagnifierText(event.target);
215
+ if (!text) {
216
+ resetTouchState();
217
+ return;
218
+ }
219
+ clearTouchTimer();
220
+ activeTouchId = touch.identifier;
221
+ activeText = text;
222
+ pressPoint = { x: touch.clientX, y: touch.clientY };
223
+ touchTimer = window.setTimeout(() => {
224
+ showTooltip(activeText);
225
+ updateTooltipPosition(touch.clientX, touch.clientY);
226
+ touchTimer = null;
227
+ }, 450);
228
+ };
229
+ const handleTouchMove = (event) => {
230
+ const touch = getTrackedTouch(event.touches);
231
+ if (!touch)
232
+ return;
233
+ if (!tooltip.hasAttribute("data-visible")) {
234
+ if (pressPoint &&
235
+ (Math.abs(touch.clientX - pressPoint.x) > 10 ||
236
+ Math.abs(touch.clientY - pressPoint.y) > 10)) {
237
+ resetTouchState();
238
+ }
239
+ return;
240
+ }
241
+ updateTooltipPosition(touch.clientX, touch.clientY);
242
+ };
243
+ const handleTouchEnd = () => resetTouchState();
244
+ const handleTouchCancel = () => resetTouchState();
245
+ const handlePointerLeave = () => hideTooltip();
246
+ document.addEventListener("mousemove", handleMouseMove, true);
247
+ document.addEventListener("touchstart", handleTouchStart, true);
248
+ document.addEventListener("touchmove", handleTouchMove, true);
249
+ document.addEventListener("touchend", handleTouchEnd, true);
250
+ document.addEventListener("touchcancel", handleTouchCancel, true);
251
+ document.addEventListener("scroll", hideTooltip, true);
252
+ window.addEventListener("blur", handlePointerLeave);
253
+ return () => {
254
+ document.removeEventListener("mousemove", handleMouseMove, true);
255
+ document.removeEventListener("touchstart", handleTouchStart, true);
256
+ document.removeEventListener("touchmove", handleTouchMove, true);
257
+ document.removeEventListener("touchend", handleTouchEnd, true);
258
+ document.removeEventListener("touchcancel", handleTouchCancel, true);
259
+ document.removeEventListener("scroll", hideTooltip, true);
260
+ window.removeEventListener("blur", handlePointerLeave);
261
+ clearTouchTimer();
262
+ tooltip.remove();
263
+ };
264
+ }, [state.textMagnifier]);
265
+ useEffect(() => {
266
+ if (!state.voiceEnabled ||
267
+ !("webkitSpeechRecognition" in window || "SpeechRecognition" in window))
60
268
  return;
61
- const SpeechRecognition = window.SpeechRecognition || window.webkitSpeechRecognition;
269
+ const SpeechRecognition = window.SpeechRecognition ||
270
+ window.webkitSpeechRecognition;
62
271
  const recognition = new SpeechRecognition();
63
272
  recognition.continuous = true;
64
- recognition.lang = state.language === 'tl' ? 'tl-PH' : state.language === 'ceb' ? 'ceb-PH' : 'en-US';
273
+ recognition.lang =
274
+ state.language === "tl"
275
+ ? "tl-PH"
276
+ : state.language === "ceb"
277
+ ? "ceb-PH"
278
+ : "en-US";
65
279
  recognition.onresult = (event) => {
66
280
  const transcript = event.results[event.results.length - 1][0].transcript.toLowerCase();
67
- if (transcript.includes('increase text') || transcript.includes('palakihin'))
281
+ if (transcript.includes("increase text") ||
282
+ transcript.includes("palakihin"))
68
283
  increaseText();
69
- if (transcript.includes('decrease text') || transcript.includes('paliitin'))
284
+ if (transcript.includes("decrease text") ||
285
+ transcript.includes("paliitin"))
70
286
  decreaseText();
71
- if (transcript.includes('high contrast') || transcript.includes('mataas'))
287
+ if (transcript.includes("high contrast") || transcript.includes("mataas"))
72
288
  toggleHighContrast();
73
- if (transcript.includes('negative contrast') || transcript.includes('negatibo'))
289
+ if (transcript.includes("negative contrast") ||
290
+ transcript.includes("negatibo"))
74
291
  toggleNegativeContrast();
75
- if (transcript.includes('light background') || transcript.includes('maliwanag'))
292
+ if (transcript.includes("light background") ||
293
+ transcript.includes("maliwanag"))
76
294
  toggleLightBackground();
77
- if (transcript.includes('underline') || transcript.includes('guhit'))
295
+ if (transcript.includes("underline") || transcript.includes("guhit"))
78
296
  toggleUnderlineLinks();
79
- if (transcript.includes('readable font') || transcript.includes('madaling'))
297
+ if (transcript.includes("highlight titles") ||
298
+ transcript.includes("title highlight"))
299
+ toggleHighlightTitles();
300
+ if (transcript.includes("pause animations") ||
301
+ transcript.includes("stop animations"))
302
+ togglePauseAnimations();
303
+ if (transcript.includes("reading guide") ||
304
+ transcript.includes("reading line"))
305
+ toggleReadingGuide();
306
+ if (transcript.includes("readable font") ||
307
+ transcript.includes("madaling"))
80
308
  toggleReadableFont();
81
309
  };
82
310
  recognition.start();
83
311
  return () => recognition.stop();
84
312
  }, [state.voiceEnabled, state.language]);
85
- const setLanguage = (lang) => setState(s => (Object.assign(Object.assign({}, s), { language: lang })));
86
- const increaseText = () => setState(s => (Object.assign(Object.assign({}, s), { textSize: Math.min(s.textSize + 10, 200) })));
87
- const decreaseText = () => setState(s => (Object.assign(Object.assign({}, s), { textSize: Math.max(s.textSize - 10, 80) })));
88
- const toggleHighContrast = () => setState(s => (Object.assign(Object.assign({}, s), { highContrast: !s.highContrast, negativeContrast: false })));
89
- const toggleNegativeContrast = () => setState(s => (Object.assign(Object.assign({}, s), { negativeContrast: !s.negativeContrast, highContrast: false })));
90
- const toggleLightBackground = () => setState(s => (Object.assign(Object.assign({}, s), { lightBackground: !s.lightBackground })));
91
- const toggleUnderlineLinks = () => setState(s => (Object.assign(Object.assign({}, s), { underlineLinks: !s.underlineLinks })));
92
- const toggleReadableFont = () => setState(s => (Object.assign(Object.assign({}, s), { readableFont: !s.readableFont })));
93
- const toggleVoice = () => setState(s => (Object.assign(Object.assign({}, s), { voiceEnabled: !s.voiceEnabled })));
313
+ const setLanguage = (lang) => setState((s) => (Object.assign(Object.assign({}, s), { language: lang })));
314
+ const increaseText = () => setState((s) => (Object.assign(Object.assign({}, s), { textSize: Math.min(s.textSize + 10, 200) })));
315
+ const decreaseText = () => setState((s) => (Object.assign(Object.assign({}, s), { textSize: Math.max(s.textSize - 10, 80) })));
316
+ const toggleTextMagnifier = () => setState((s) => (Object.assign(Object.assign({}, s), { textMagnifier: !s.textMagnifier })));
317
+ const togglePauseAnimations = () => setState((s) => (Object.assign(Object.assign({}, s), { pauseAnimations: !s.pauseAnimations })));
318
+ const toggleReadingGuide = () => setState((s) => (Object.assign(Object.assign({}, s), { readingGuide: !s.readingGuide })));
319
+ const toggleHighContrast = () => setState((s) => (Object.assign(Object.assign({}, s), { highContrast: !s.highContrast, negativeContrast: false })));
320
+ const toggleNegativeContrast = () => setState((s) => (Object.assign(Object.assign({}, s), { negativeContrast: !s.negativeContrast, highContrast: false })));
321
+ const toggleLightBackground = () => setState((s) => (Object.assign(Object.assign({}, s), { lightBackground: !s.lightBackground })));
322
+ const toggleUnderlineLinks = () => setState((s) => (Object.assign(Object.assign({}, s), { underlineLinks: !s.underlineLinks })));
323
+ const toggleHighlightTitles = () => setState((s) => (Object.assign(Object.assign({}, s), { highlightTitles: !s.highlightTitles })));
324
+ const toggleReadableFont = () => setState((s) => (Object.assign(Object.assign({}, s), { readableFont: !s.readableFont })));
325
+ const toggleVoice = () => setState((s) => (Object.assign(Object.assign({}, s), { voiceEnabled: !s.voiceEnabled })));
94
326
  const translate = (key) => translations[state.language][key];
95
327
  const t = (key) => { var _a; return ((_a = allTranslations[state.language]) === null || _a === void 0 ? void 0 : _a[key]) || key; };
96
328
  return (React.createElement(AccessibilityContext.Provider, { value: Object.assign(Object.assign({}, state), { setLanguage,
97
329
  increaseText,
98
330
  decreaseText,
331
+ toggleTextMagnifier,
332
+ togglePauseAnimations,
333
+ toggleReadingGuide,
99
334
  toggleHighContrast,
100
335
  toggleNegativeContrast,
101
336
  toggleLightBackground,
102
337
  toggleUnderlineLinks,
338
+ toggleHighlightTitles,
103
339
  toggleReadableFont,
104
340
  toggleVoice,
105
341
  translate,
@@ -108,61 +344,91 @@ const AccessibilityProvider = ({ children, translations: customTranslations }) =
108
344
  const useAccessibility = () => {
109
345
  const context = useContext(AccessibilityContext);
110
346
  if (!context)
111
- throw new Error('useAccessibility must be used within AccessibilityProvider');
347
+ throw new Error("useAccessibility must be used within AccessibilityProvider");
112
348
  return context;
113
349
  };
114
350
 
115
- const AccessibilityToolbar = () => {
116
- const { language, setLanguage, increaseText, decreaseText, toggleHighContrast, toggleNegativeContrast, toggleLightBackground, toggleUnderlineLinks, toggleReadableFont, toggleVoice, translate, highContrast, negativeContrast, lightBackground, underlineLinks, readableFont, voiceEnabled, } = useAccessibility();
117
- return (React.createElement("div", { className: "accessibility-toolbar" },
118
- React.createElement("select", { value: language, onChange: (e) => setLanguage(e.target.value) },
351
+ const cn$1 = (...parts) => parts.filter(Boolean).join(" ");
352
+ const AccessibilityToolbar = ({ className, style, classes, }) => {
353
+ const { language, setLanguage, increaseText, decreaseText, toggleTextMagnifier, togglePauseAnimations, toggleReadingGuide, toggleHighContrast, toggleNegativeContrast, toggleLightBackground, toggleUnderlineLinks, toggleHighlightTitles, toggleReadableFont, toggleVoice, translate, textMagnifier, pauseAnimations, readingGuide, highContrast, negativeContrast, lightBackground, underlineLinks, highlightTitles, readableFont, voiceEnabled, } = useAccessibility();
354
+ return (React.createElement("div", { className: cn$1("accessibility-toolbar", classes === null || classes === void 0 ? void 0 : classes.root, className), style: style },
355
+ React.createElement("select", { className: cn$1(classes === null || classes === void 0 ? void 0 : classes.select), value: language, onChange: (e) => setLanguage(e.target.value) },
119
356
  React.createElement("option", { value: "en" }, "English"),
120
357
  React.createElement("option", { value: "tl" }, "Tagalog"),
121
358
  React.createElement("option", { value: "ceb" }, "Cebuano")),
122
- React.createElement("button", { onClick: increaseText },
359
+ React.createElement("button", { className: cn$1(classes === null || classes === void 0 ? void 0 : classes.button), onClick: increaseText },
123
360
  "+ ",
124
- translate('increaseText')),
125
- React.createElement("button", { onClick: decreaseText },
361
+ translate("increaseText")),
362
+ React.createElement("button", { className: cn$1(classes === null || classes === void 0 ? void 0 : classes.button), onClick: decreaseText },
126
363
  "- ",
127
- translate('decreaseText')),
128
- React.createElement("button", { onClick: toggleHighContrast, className: highContrast ? 'active' : '' }, translate('highContrast')),
129
- React.createElement("button", { onClick: toggleNegativeContrast, className: negativeContrast ? 'active' : '' }, translate('negativeContrast')),
130
- React.createElement("button", { onClick: toggleLightBackground, className: lightBackground ? 'active' : '' }, translate('lightBackground')),
131
- React.createElement("button", { onClick: toggleUnderlineLinks, className: underlineLinks ? 'active' : '' }, translate('underlineLinks')),
132
- React.createElement("button", { onClick: toggleReadableFont, className: readableFont ? 'active' : '' }, translate('readableFont')),
133
- React.createElement("button", { onClick: toggleVoice, className: voiceEnabled ? 'active' : '' },
364
+ translate("decreaseText")),
365
+ React.createElement("button", { className: cn$1(classes === null || classes === void 0 ? void 0 : classes.button, textMagnifier && "active", textMagnifier && (classes === null || classes === void 0 ? void 0 : classes.activeButton)), "data-active": textMagnifier, onClick: toggleTextMagnifier }, translate("textMagnifier")),
366
+ React.createElement("button", { className: cn$1(classes === null || classes === void 0 ? void 0 : classes.button, highContrast && "active", highContrast && (classes === null || classes === void 0 ? void 0 : classes.activeButton)), "data-active": highContrast, onClick: toggleHighContrast }, translate("highContrast")),
367
+ React.createElement("button", { className: cn$1(classes === null || classes === void 0 ? void 0 : classes.button, negativeContrast && "active", negativeContrast && (classes === null || classes === void 0 ? void 0 : classes.activeButton)), "data-active": negativeContrast, onClick: toggleNegativeContrast }, translate("negativeContrast")),
368
+ React.createElement("button", { className: cn$1(classes === null || classes === void 0 ? void 0 : classes.button, lightBackground && "active", lightBackground && (classes === null || classes === void 0 ? void 0 : classes.activeButton)), "data-active": lightBackground, onClick: toggleLightBackground }, translate("lightBackground")),
369
+ React.createElement("button", { className: cn$1(classes === null || classes === void 0 ? void 0 : classes.button, underlineLinks && "active", underlineLinks && (classes === null || classes === void 0 ? void 0 : classes.activeButton)), "data-active": underlineLinks, onClick: toggleUnderlineLinks }, translate("underlineLinks")),
370
+ React.createElement("button", { className: cn$1(classes === null || classes === void 0 ? void 0 : classes.button, highlightTitles && "active", highlightTitles && (classes === null || classes === void 0 ? void 0 : classes.activeButton)), "data-active": highlightTitles, onClick: toggleHighlightTitles }, translate("highlightTitles")),
371
+ React.createElement("button", { className: cn$1(classes === null || classes === void 0 ? void 0 : classes.button, readableFont && "active", readableFont && (classes === null || classes === void 0 ? void 0 : classes.activeButton)), "data-active": readableFont, onClick: toggleReadableFont }, translate("readableFont")),
372
+ React.createElement("button", { className: cn$1(classes === null || classes === void 0 ? void 0 : classes.button, pauseAnimations && "active", pauseAnimations && (classes === null || classes === void 0 ? void 0 : classes.activeButton)), "data-active": pauseAnimations, onClick: togglePauseAnimations }, translate("pauseAnimations")),
373
+ React.createElement("button", { className: cn$1(classes === null || classes === void 0 ? void 0 : classes.button, readingGuide && "active", readingGuide && (classes === null || classes === void 0 ? void 0 : classes.activeButton)), "data-active": readingGuide, onClick: toggleReadingGuide }, translate("readingGuide")),
374
+ React.createElement("button", { className: cn$1(classes === null || classes === void 0 ? void 0 : classes.button, classes === null || classes === void 0 ? void 0 : classes.voiceButton, voiceEnabled && "active", voiceEnabled && (classes === null || classes === void 0 ? void 0 : classes.activeButton)), "data-active": voiceEnabled, onClick: toggleVoice },
134
375
  "\uD83C\uDFA4 ",
135
- translate('voiceCommand'))));
376
+ translate("voiceCommand"))));
136
377
  };
137
378
 
138
- const AccessibilityDropdown = () => {
379
+ const cn = (...parts) => parts.filter(Boolean).join(" ");
380
+ const AccessibilityDropdown = ({ className, style, classes, triggerLabel = "♿ Accessibility", renderTrigger, }) => {
139
381
  const [isOpen, setIsOpen] = useState(false);
140
- const { language, setLanguage, increaseText, decreaseText, toggleHighContrast, toggleNegativeContrast, toggleLightBackground, toggleUnderlineLinks, toggleReadableFont, toggleVoice, translate, highContrast, negativeContrast, lightBackground, underlineLinks, readableFont, voiceEnabled, } = useAccessibility();
141
- return (React.createElement("div", { className: "accessibility-dropdown" },
142
- React.createElement("button", { className: "accessibility-dropdown-toggle", onClick: () => setIsOpen(!isOpen) }, "\u267F Accessibility"),
143
- isOpen && (React.createElement("div", { className: "accessibility-dropdown-menu" },
144
- React.createElement("div", { className: "accessibility-dropdown-section" },
145
- React.createElement("label", null, "Language:"),
146
- React.createElement("select", { value: language, onChange: (e) => setLanguage(e.target.value) },
382
+ const { language, setLanguage, textSize, increaseText, decreaseText, toggleTextMagnifier, togglePauseAnimations, toggleReadingGuide, toggleHighContrast, toggleNegativeContrast, toggleLightBackground, toggleUnderlineLinks, toggleHighlightTitles, toggleReadableFont, toggleVoice, translate, textMagnifier, pauseAnimations, readingGuide, highContrast, negativeContrast, lightBackground, underlineLinks, highlightTitles, readableFont, voiceEnabled, } = useAccessibility();
383
+ const toggle = () => setIsOpen(!isOpen);
384
+ const closePanel = () => setIsOpen(false);
385
+ const textScaleDelta = textSize - 100;
386
+ const textScaleLabel = textScaleDelta === 0
387
+ ? "Default"
388
+ : `${textScaleDelta > 0 ? "+" : ""}${textScaleDelta}%`;
389
+ return (React.createElement("div", { className: cn("a11y-root", classes === null || classes === void 0 ? void 0 : classes.root, className), style: style },
390
+ renderTrigger ? (renderTrigger({
391
+ isOpen,
392
+ toggle,
393
+ className: cn("a11y-trigger", classes === null || classes === void 0 ? void 0 : classes.trigger),
394
+ })) : (React.createElement("button", { className: cn("a11y-trigger", classes === null || classes === void 0 ? void 0 : classes.trigger), onClick: toggle }, triggerLabel)),
395
+ isOpen && (React.createElement("div", { className: cn("a11y-panel", classes === null || classes === void 0 ? void 0 : classes.panel) },
396
+ React.createElement("div", { className: cn("a11y-panel-header", classes === null || classes === void 0 ? void 0 : classes.panelHeader) },
397
+ React.createElement("div", { className: "a11y-panel-heading" },
398
+ React.createElement("span", { className: "a11y-panel-eyebrow" }, "NGP"),
399
+ React.createElement("label", { className: "a11y-panel-title" }, "Accessibility Options")),
400
+ React.createElement("button", { type: "button", className: cn("a11y-panel-close", classes === null || classes === void 0 ? void 0 : classes.closeButton), onClick: closePanel, "aria-label": "Close accessibility panel" }, "\u00D7")),
401
+ React.createElement("div", { className: "a11y-language" },
402
+ React.createElement("label", { htmlFor: "a11y-language-select" }, "Language"),
403
+ React.createElement("select", { id: "a11y-language-select", value: language, onChange: (e) => setLanguage(e.target.value) },
147
404
  React.createElement("option", { value: "en" }, "English"),
148
405
  React.createElement("option", { value: "tl" }, "Tagalog"),
149
406
  React.createElement("option", { value: "ceb" }, "Cebuano"))),
150
- React.createElement("div", { className: "accessibility-dropdown-section" },
151
- React.createElement("button", { onClick: increaseText },
152
- "+ ",
153
- translate('increaseText')),
154
- React.createElement("button", { onClick: decreaseText },
155
- "- ",
156
- translate('decreaseText'))),
157
- React.createElement("div", { className: "accessibility-dropdown-section" },
158
- React.createElement("button", { onClick: toggleHighContrast, className: highContrast ? 'active' : '' }, translate('highContrast')),
159
- React.createElement("button", { onClick: toggleNegativeContrast, className: negativeContrast ? 'active' : '' }, translate('negativeContrast')),
160
- React.createElement("button", { onClick: toggleLightBackground, className: lightBackground ? 'active' : '' }, translate('lightBackground')),
161
- React.createElement("button", { onClick: toggleUnderlineLinks, className: underlineLinks ? 'active' : '' }, translate('underlineLinks')),
162
- React.createElement("button", { onClick: toggleReadableFont, className: readableFont ? 'active' : '' }, translate('readableFont')),
163
- React.createElement("button", { onClick: toggleVoice, className: voiceEnabled ? 'active' : '' },
164
- "\uD83C\uDFA4 ",
165
- translate('voiceCommand')))))));
407
+ React.createElement("section", { className: "a11y-section" },
408
+ React.createElement("h3", { className: "a11y-section-title" }, "Content Adjustments"),
409
+ React.createElement("div", { className: "a11y-card" },
410
+ React.createElement("span", { className: "a11y-card-title" }, "Content Scaling"),
411
+ React.createElement("div", { className: "a11y-stepper" },
412
+ React.createElement("button", { onClick: decreaseText }, "\u2212"),
413
+ React.createElement("span", { className: "a11y-stepper-value" }, textScaleLabel),
414
+ React.createElement("button", { onClick: increaseText }, "+"))),
415
+ React.createElement("div", { className: "a11y-grid" },
416
+ React.createElement("button", { className: cn("a11y-card", textMagnifier && "active"), onClick: toggleTextMagnifier }, translate("textMagnifier")),
417
+ React.createElement("button", { className: cn("a11y-card", readableFont && "active"), onClick: toggleReadableFont }, "Readable Font"),
418
+ React.createElement("button", { className: cn("a11y-card", underlineLinks && "active"), onClick: toggleUnderlineLinks }, "Highlight Links"),
419
+ React.createElement("button", { className: cn("a11y-card", highlightTitles && "active"), onClick: toggleHighlightTitles }, "Highlight Titles"))),
420
+ React.createElement("section", { className: "a11y-section" },
421
+ React.createElement("h3", { className: "a11y-section-title" }, "Color Adjustments"),
422
+ React.createElement("div", { className: "a11y-grid" },
423
+ React.createElement("button", { className: cn("a11y-card", highContrast && "active"), onClick: toggleHighContrast }, translate("highContrast")),
424
+ React.createElement("button", { className: cn("a11y-card", negativeContrast && "active"), onClick: toggleNegativeContrast }, translate("negativeContrast")),
425
+ React.createElement("button", { className: cn("a11y-card", lightBackground && "active"), onClick: toggleLightBackground }, translate("lightBackground")))),
426
+ React.createElement("section", { className: "a11y-section" },
427
+ React.createElement("h3", { className: "a11y-section-title" }, "Accessibility Tools"),
428
+ React.createElement("div", { className: "a11y-grid" },
429
+ React.createElement("button", { className: cn("a11y-card", pauseAnimations && "active"), onClick: togglePauseAnimations }, translate("pauseAnimations")),
430
+ React.createElement("button", { className: cn("a11y-card", readingGuide && "active"), onClick: toggleReadingGuide }, translate("readingGuide")),
431
+ React.createElement("button", { className: cn("a11y-card", voiceEnabled && "active"), onClick: toggleVoice }, translate("voiceCommand"))))))));
166
432
  };
167
433
 
168
434
  const T = ({ k, children }) => {