vision-accessibility 1.0.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.
Files changed (55) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +225 -0
  3. package/dist/components/AccessibilityPanel/AccessibilityPanel.d.ts +12 -0
  4. package/dist/components/AccessibilityPanel/AccessibilityPanel.d.ts.map +1 -0
  5. package/dist/components/AccessibilityToggle/AccessibilityToggle.d.ts +18 -0
  6. package/dist/components/AccessibilityToggle/AccessibilityToggle.d.ts.map +1 -0
  7. package/dist/components/ColorSchemeControl/ColorSchemeControl.d.ts +18 -0
  8. package/dist/components/ColorSchemeControl/ColorSchemeControl.d.ts.map +1 -0
  9. package/dist/components/FontSizeControl/FontSizeControl.d.ts +18 -0
  10. package/dist/components/FontSizeControl/FontSizeControl.d.ts.map +1 -0
  11. package/dist/components/ImageControl/ImageControl.d.ts +18 -0
  12. package/dist/components/ImageControl/ImageControl.d.ts.map +1 -0
  13. package/dist/context/AccessibilityContext.d.ts +25 -0
  14. package/dist/context/AccessibilityContext.d.ts.map +1 -0
  15. package/dist/hooks/useAccessibilitySettings.d.ts +25 -0
  16. package/dist/hooks/useAccessibilitySettings.d.ts.map +1 -0
  17. package/dist/index.d.ts +17 -0
  18. package/dist/index.d.ts.map +1 -0
  19. package/dist/index.esm.js +1151 -0
  20. package/dist/index.esm.js.map +1 -0
  21. package/dist/index.umd.js +11 -0
  22. package/dist/index.umd.js.map +1 -0
  23. package/dist/lib/accessibility-storage.d.ts +23 -0
  24. package/dist/lib/accessibility-storage.d.ts.map +1 -0
  25. package/dist/lib/css-applier.d.ts +50 -0
  26. package/dist/lib/css-applier.d.ts.map +1 -0
  27. package/dist/lib/dom-manipulator.d.ts +29 -0
  28. package/dist/lib/dom-manipulator.d.ts.map +1 -0
  29. package/dist/lib/font-size-manager.d.ts +26 -0
  30. package/dist/lib/font-size-manager.d.ts.map +1 -0
  31. package/dist/style.css +1 -0
  32. package/dist/types/index.d.ts +61 -0
  33. package/dist/types/index.d.ts.map +1 -0
  34. package/package.json +58 -0
  35. package/src/components/AccessibilityPanel/AccessibilityPanel.module.scss +214 -0
  36. package/src/components/AccessibilityPanel/AccessibilityPanel.tsx +151 -0
  37. package/src/components/AccessibilityToggle/AccessibilityToggle.module.scss +158 -0
  38. package/src/components/AccessibilityToggle/AccessibilityToggle.tsx +85 -0
  39. package/src/components/ColorSchemeControl/ColorSchemeControl.module.scss +175 -0
  40. package/src/components/ColorSchemeControl/ColorSchemeControl.tsx +116 -0
  41. package/src/components/FontSizeControl/FontSizeControl.module.scss +175 -0
  42. package/src/components/FontSizeControl/FontSizeControl.tsx +116 -0
  43. package/src/components/ImageControl/ImageControl.module.scss +163 -0
  44. package/src/components/ImageControl/ImageControl.tsx +85 -0
  45. package/src/context/AccessibilityContext.tsx +54 -0
  46. package/src/hooks/useAccessibilitySettings.ts +228 -0
  47. package/src/index.ts +80 -0
  48. package/src/lib/accessibility-storage.ts +82 -0
  49. package/src/lib/css-applier.ts +168 -0
  50. package/src/lib/dom-manipulator.ts +75 -0
  51. package/src/lib/font-size-manager.ts +185 -0
  52. package/src/styles/global.scss +60 -0
  53. package/src/styles/variables.scss +80 -0
  54. package/src/types/index.ts +72 -0
  55. package/src/types/scss.d.ts +9 -0
@@ -0,0 +1,116 @@
1
+ /**
2
+ * Компонент управления размером шрифта
3
+ * Предоставляет радио-кнопки для выбора размера шрифта
4
+ *
5
+ * Реализует требования:
6
+ * - 3.1: Уменьшение размера шрифта на 10%
7
+ * - 3.2: Увеличение размера шрифта на 10%
8
+ * - 3.3: Возврат к стандартному размеру шрифта
9
+ */
10
+
11
+ import React from 'react';
12
+ import { FontSize } from '../../types';
13
+ import { useAccessibilityContext } from '../../context/AccessibilityContext';
14
+ import styles from './FontSizeControl.module.scss';
15
+
16
+ /**
17
+ * Свойства компонента FontSizeControl
18
+ */
19
+ interface FontSizeControlProps {
20
+ /** CSS класс для дополнительной стилизации */
21
+ className?: string;
22
+ }
23
+
24
+ /**
25
+ * Опции размеров шрифта с описаниями
26
+ */
27
+ const FONT_SIZE_OPTIONS: Array<{
28
+ value: FontSize;
29
+ label: string;
30
+ description: string;
31
+ }> = [
32
+ {
33
+ value: 'small',
34
+ label: 'Мелкий',
35
+ description: 'Уменьшенный размер текста (80%)'
36
+ },
37
+ {
38
+ value: 'standard',
39
+ label: 'Стандартный',
40
+ description: 'Обычный размер текста'
41
+ },
42
+ {
43
+ value: 'large',
44
+ label: 'Крупный',
45
+ description: 'Увеличенный размер текста (130%)'
46
+ }
47
+ ];
48
+
49
+ /**
50
+ * Компонент управления размером шрифта
51
+ *
52
+ * @param props - Свойства компонента
53
+ * @returns JSX элемент с радио-кнопками для выбора размера шрифта
54
+ */
55
+ export const FontSizeControl: React.FC<FontSizeControlProps> = ({
56
+ className
57
+ }) => {
58
+ // Используем контекст для получения состояния и методов управления
59
+ const { settings, updateSettings } = useAccessibilityContext();
60
+ const fontSize = settings.fontSize;
61
+ const isEnabled = settings.isEnabled;
62
+
63
+ /**
64
+ * Обработчик изменения размера шрифта
65
+ *
66
+ * @param event - Событие изменения радио-кнопки
67
+ */
68
+ const handleFontSizeChange = (event: React.ChangeEvent<HTMLInputElement>) => {
69
+ const newFontSize = event.target.value as FontSize;
70
+ updateSettings({ fontSize: newFontSize });
71
+ };
72
+
73
+ return (
74
+ <div className={`${styles.fontSizeControl} ${className || ''} ${!isEnabled ? styles.disabled : ''}`}>
75
+ <h4 className={styles.title}>
76
+ Размер шрифта
77
+ </h4>
78
+
79
+ <div className={styles.optionsContainer} role="radiogroup" aria-labelledby="font-size-title">
80
+ {FONT_SIZE_OPTIONS.map((option) => (
81
+ <label
82
+ key={option.value}
83
+ className={`${styles.option} ${fontSize === option.value ? styles.selected : ''} ${!isEnabled ? styles.optionDisabled : ''}`}
84
+ >
85
+ <input
86
+ type="radio"
87
+ name="fontSize"
88
+ value={option.value}
89
+ checked={fontSize === option.value}
90
+ onChange={handleFontSizeChange}
91
+ className={styles.radioInput}
92
+ aria-describedby={`font-size-${option.value}-description`}
93
+ disabled={!isEnabled}
94
+ />
95
+
96
+ <div className={styles.optionContent}>
97
+ <span className={styles.optionLabel}>
98
+ {option.label}
99
+ </span>
100
+ <span
101
+ id={`font-size-${option.value}-description`}
102
+ className={styles.optionDescription}
103
+ >
104
+ {option.description}
105
+ </span>
106
+ </div>
107
+
108
+ <div className={styles.radioIndicator} aria-hidden="true" />
109
+ </label>
110
+ ))}
111
+ </div>
112
+ </div>
113
+ );
114
+ };
115
+
116
+ export default FontSizeControl;
@@ -0,0 +1,163 @@
1
+ /**
2
+ * Стили для компонента управления отображением изображений
3
+ * Реализует доступный переключатель для управления видимостью изображений
4
+ */
5
+
6
+ .imageControl {
7
+ margin-bottom: $spacing-lg;
8
+
9
+ &.disabled {
10
+ .title {
11
+ opacity: 0.6;
12
+ }
13
+ }
14
+ }
15
+
16
+ .title {
17
+ font-size: 14px;
18
+ font-weight: 600;
19
+ margin: 0 0 $spacing-md 0;
20
+ color: $gray-800;
21
+ }
22
+
23
+ .switchContainer {
24
+ display: flex;
25
+ flex-direction: column;
26
+ gap: $spacing-sm;
27
+ }
28
+
29
+ .switchLabel {
30
+ display: flex;
31
+ align-items: center;
32
+ gap: $spacing-md;
33
+ cursor: pointer;
34
+ padding: $spacing-md;
35
+ border: 1px solid $gray-300;
36
+ border-radius: $border-radius;
37
+ transition: all $transition-fast;
38
+ background: $white;
39
+
40
+ &:hover {
41
+ background: $gray-100;
42
+ border-color: $primary;
43
+ }
44
+
45
+ &:focus-within {
46
+ background: $gray-100;
47
+ border-color: $primary;
48
+ box-shadow: 0 0 0 2px rgba(0, 123, 255, 0.25);
49
+ }
50
+
51
+ &.switchDisabled {
52
+ opacity: 0.5;
53
+ cursor: not-allowed;
54
+ pointer-events: none;
55
+
56
+ &:hover {
57
+ background: $white;
58
+ border-color: $gray-300;
59
+ }
60
+ }
61
+ }
62
+
63
+ .switchInput {
64
+ position: absolute;
65
+ opacity: 0;
66
+ width: 0;
67
+ height: 0;
68
+ margin: 0;
69
+
70
+ &:focus + .switchSlider {
71
+ box-shadow: 0 0 0 2px rgba(0, 123, 255, 0.25);
72
+ }
73
+
74
+ &:checked + .switchSlider {
75
+ background: $primary;
76
+
77
+ .switchThumb {
78
+ transform: translateX(20px);
79
+ }
80
+ }
81
+ }
82
+
83
+ .switchSlider {
84
+ position: relative;
85
+ width: 44px;
86
+ height: 24px;
87
+ background: $gray-400;
88
+ border-radius: 12px;
89
+ transition: all $transition-fast;
90
+ flex-shrink: 0;
91
+ }
92
+
93
+ .switchThumb {
94
+ position: absolute;
95
+ top: 2px;
96
+ left: 2px;
97
+ width: 20px;
98
+ height: 20px;
99
+ background: $white;
100
+ border-radius: 50%;
101
+ transition: transform $transition-fast;
102
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
103
+ }
104
+
105
+ .switchText {
106
+ font-size: 14px;
107
+ font-weight: 500;
108
+ color: $gray-800;
109
+ line-height: 1.4;
110
+ flex: 1;
111
+ }
112
+
113
+ .description {
114
+ font-size: 12px;
115
+ color: $gray-600;
116
+ line-height: 1.3;
117
+ margin: 0;
118
+ padding-left: 56px; // Выравнивание с текстом переключателя
119
+ }
120
+
121
+ // Высококонтрастная тема - адаптация стилей
122
+ :global(.accessibility-high-contrast) .imageControl {
123
+ .title {
124
+ color: $white !important;
125
+ }
126
+
127
+ .switchLabel {
128
+ background: $black !important;
129
+ border-color: $white !important;
130
+ color: $white !important;
131
+
132
+ &:hover,
133
+ &:focus-within {
134
+ background: $gray-800 !important;
135
+ }
136
+ }
137
+
138
+ .switchText {
139
+ color: $white !important;
140
+ }
141
+
142
+ .description {
143
+ color: $gray-400 !important;
144
+ }
145
+
146
+ .switchSlider {
147
+ background: $gray-600 !important;
148
+ border: 1px solid $white !important;
149
+ }
150
+
151
+ .switchInput:checked + .switchSlider {
152
+ background: $white !important;
153
+
154
+ .switchThumb {
155
+ background: $black !important;
156
+ }
157
+ }
158
+
159
+ .switchThumb {
160
+ background: $white !important;
161
+ border: 1px solid $black !important;
162
+ }
163
+ }
@@ -0,0 +1,85 @@
1
+ /**
2
+ * Компонент управления отображением изображений
3
+ * Предоставляет переключатель для скрытия/показа изображений
4
+ *
5
+ * Реализует требования:
6
+ * - 4.1: Скрытие изображений через CSS свойство hidden
7
+ * - 4.2: Восстановление видимости изображений
8
+ */
9
+
10
+ import React from 'react';
11
+ import { useAccessibilityContext } from '../../context/AccessibilityContext';
12
+ import styles from './ImageControl.module.scss';
13
+
14
+ /**
15
+ * Свойства компонента ImageControl
16
+ */
17
+ interface ImageControlProps {
18
+ /** CSS класс для дополнительной стилизации */
19
+ className?: string;
20
+ }
21
+
22
+ /**
23
+ * Компонент управления отображением изображений
24
+ *
25
+ * @param props - Свойства компонента
26
+ * @returns JSX элемент с переключателем для управления изображениями
27
+ */
28
+ export const ImageControl: React.FC<ImageControlProps> = ({
29
+ className
30
+ }) => {
31
+ // Используем контекст для получения состояния и методов управления
32
+ const { settings, updateSettings } = useAccessibilityContext();
33
+ const showImages = settings.showImages;
34
+ const isEnabled = settings.isEnabled;
35
+
36
+ /**
37
+ * Обработчик изменения настройки отображения изображений
38
+ *
39
+ * @param event - Событие изменения чекбокса
40
+ */
41
+ const handleImageVisibilityChange = (event: React.ChangeEvent<HTMLInputElement>) => {
42
+ updateSettings({ showImages: event.target.checked });
43
+ };
44
+
45
+ return (
46
+ <div className={`${styles.imageControl} ${className || ''} ${!isEnabled ? styles.disabled : ''}`}>
47
+ <h4 className={styles.title}>
48
+ Изображения
49
+ </h4>
50
+
51
+ <div className={styles.switchContainer}>
52
+ <label className={`${styles.switchLabel} ${!isEnabled ? styles.switchDisabled : ''}`}>
53
+ <input
54
+ type="checkbox"
55
+ checked={showImages}
56
+ onChange={handleImageVisibilityChange}
57
+ className={styles.switchInput}
58
+ aria-describedby="image-control-description"
59
+ disabled={!isEnabled}
60
+ />
61
+
62
+ <span className={styles.switchSlider} aria-hidden="true">
63
+ <span className={styles.switchThumb} />
64
+ </span>
65
+
66
+ <span className={styles.switchText}>
67
+ {showImages ? 'Показывать изображения' : 'Скрывать изображения'}
68
+ </span>
69
+ </label>
70
+
71
+ <p
72
+ id="image-control-description"
73
+ className={styles.description}
74
+ >
75
+ {showImages
76
+ ? 'Изображения отображаются на странице'
77
+ : 'Изображения скрыты для лучшей концентрации на тексте'
78
+ }
79
+ </p>
80
+ </div>
81
+ </div>
82
+ );
83
+ };
84
+
85
+ export default ImageControl;
@@ -0,0 +1,54 @@
1
+ /**
2
+ * Контекст для управления настройками доступности
3
+ * Обеспечивает единое состояние для всех компонентов панели доступности
4
+ */
5
+
6
+ import React, { createContext, useContext, ReactNode } from 'react';
7
+ import { AccessibilityContextType } from '../types';
8
+ import { useAccessibilitySettings } from '../hooks/useAccessibilitySettings';
9
+
10
+ /**
11
+ * Контекст настроек доступности
12
+ */
13
+ const AccessibilityContext = createContext<AccessibilityContextType | null>(null);
14
+
15
+ /**
16
+ * Свойства провайдера контекста
17
+ */
18
+ interface AccessibilityProviderProps {
19
+ children: ReactNode;
20
+ }
21
+
22
+ /**
23
+ * Провайдер контекста настроек доступности
24
+ *
25
+ * @param props - Свойства провайдера
26
+ * @returns JSX элемент с провайдером контекста
27
+ */
28
+ export const AccessibilityProvider: React.FC<AccessibilityProviderProps> = ({ children }) => {
29
+ const accessibilityState = useAccessibilitySettings();
30
+
31
+ return (
32
+ <AccessibilityContext.Provider value={accessibilityState}>
33
+ {children}
34
+ </AccessibilityContext.Provider>
35
+ );
36
+ };
37
+
38
+ /**
39
+ * Хук для использования контекста настроек доступности
40
+ *
41
+ * @returns Контекст настроек доступности
42
+ * @throws Ошибка, если хук используется вне провайдера
43
+ */
44
+ export const useAccessibilityContext = (): AccessibilityContextType => {
45
+ const context = useContext(AccessibilityContext);
46
+
47
+ if (!context) {
48
+ throw new Error('useAccessibilityContext должен использоваться внутри AccessibilityProvider');
49
+ }
50
+
51
+ return context;
52
+ };
53
+
54
+ export default AccessibilityProvider;
@@ -0,0 +1,228 @@
1
+ /**
2
+ * Хук для управления настройками доступности
3
+ * Обеспечивает централизованное управление состоянием настроек доступности
4
+ * с автоматическим сохранением в localStorage и применением к DOM
5
+ *
6
+ * Реализует требования:
7
+ * - 5.1: Включение режима доступности с активацией всех сохраненных настроек
8
+ * - 5.2: Выключение режима доступности с удалением всех CSS классов и стилей
9
+ * - 6.2: Загрузка настроек из localStorage при инициализации
10
+ * - 6.3: Автоматическое применение сохраненных настроек при загрузке страницы
11
+ */
12
+
13
+ import { useState, useEffect, useCallback } from 'react';
14
+ import {
15
+ AccessibilitySettings,
16
+ DEFAULT_ACCESSIBILITY_SETTINGS,
17
+ AccessibilityContextType
18
+ } from '../types';
19
+ import {
20
+ saveToStorage,
21
+ loadFromStorage,
22
+ clearStorage
23
+ } from '../lib/accessibility-storage';
24
+ import {
25
+ applyAllSettings,
26
+ removeAllAccessibilityStyles
27
+ } from '../lib/css-applier';
28
+ import { updateNewElementsFontSize } from '../lib/font-size-manager';
29
+
30
+ /**
31
+ * Хук для управления настройками доступности
32
+ *
33
+ * @returns Объект с текущими настройками и методами для их управления
34
+ */
35
+ export const useAccessibilitySettings = (): AccessibilityContextType => {
36
+ // Состояние настроек доступности
37
+ const [settings, setSettings] = useState<AccessibilitySettings>(DEFAULT_ACCESSIBILITY_SETTINGS);
38
+
39
+ // Флаг загрузки настроек из localStorage (для SSR совместимости)
40
+ const [isLoaded, setIsLoaded] = useState(false);
41
+
42
+ /**
43
+ * Наблюдатель за изменениями DOM для обработки динамически добавляемых элементов
44
+ */
45
+ useEffect(() => {
46
+ if (typeof window === 'undefined' || !isLoaded || !settings.isEnabled) return;
47
+
48
+ const observer = new MutationObserver((mutations) => {
49
+ let hasNewTextElements = false;
50
+
51
+ mutations.forEach((mutation) => {
52
+ if (mutation.type === 'childList') {
53
+ mutation.addedNodes.forEach((node) => {
54
+ if (node.nodeType === Node.ELEMENT_NODE) {
55
+ const element = node as Element;
56
+ // Проверяем, есть ли текстовые элементы среди добавленных
57
+ if (element.matches('h1, h2, h3, h4, h5, h6, p, span, a, button, label, li, div') ||
58
+ element.querySelector('h1, h2, h3, h4, h5, h6, p, span, a, button, label, li, div')) {
59
+ hasNewTextElements = true;
60
+ }
61
+ }
62
+ });
63
+ }
64
+ });
65
+
66
+ // Если добавлены новые текстовые элементы, применяем к ним текущие настройки размера шрифта
67
+ if (hasNewTextElements && settings.fontSize !== 'standard') {
68
+ setTimeout(() => {
69
+ updateNewElementsFontSize(settings.fontSize);
70
+ }, 100); // Небольшая задержка для завершения рендеринга
71
+ }
72
+ });
73
+
74
+ observer.observe(document.body, {
75
+ childList: true,
76
+ subtree: true
77
+ });
78
+
79
+ return () => {
80
+ observer.disconnect();
81
+ };
82
+ }, [isLoaded, settings.isEnabled, settings.fontSize]);
83
+
84
+ /**
85
+ * Загрузка настроек из localStorage при инициализации
86
+ * Реализует требования 6.2, 6.3: Восстановление сохраненных настроек
87
+ * Обеспечивает SSR совместимость с проверкой window
88
+ */
89
+ useEffect(() => {
90
+ // SSR совместимость: выполняем только на клиенте
91
+ if (typeof window === 'undefined') {
92
+ return;
93
+ }
94
+
95
+ try {
96
+ // Требование 6.2: Загрузка настроек из localStorage
97
+ const savedSettings = loadFromStorage();
98
+
99
+ if (savedSettings) {
100
+ setSettings(savedSettings);
101
+
102
+ // Требование 6.3: Автоматическое применение сохраненных настроек
103
+ // Применяем настройки только если режим доступности включен
104
+ if (savedSettings.isEnabled) {
105
+ applyAllSettings(savedSettings);
106
+ }
107
+ }
108
+ } catch (error) {
109
+ console.warn('Ошибка при загрузке настроек доступности:', error);
110
+ // При ошибке используем настройки по умолчанию
111
+ setSettings(DEFAULT_ACCESSIBILITY_SETTINGS);
112
+ } finally {
113
+ // Отмечаем, что настройки загружены
114
+ setIsLoaded(true);
115
+ }
116
+ }, []);
117
+
118
+ /**
119
+ * Сохранение настроек в localStorage при изменении
120
+ * Реализует автоматическое сохранение всех изменений настроек
121
+ */
122
+ useEffect(() => {
123
+ // Сохраняем только после первоначальной загрузки
124
+ if (!isLoaded) {
125
+ return;
126
+ }
127
+
128
+ try {
129
+ // Сохраняем настройки в localStorage
130
+ saveToStorage(settings);
131
+
132
+ // Применяем настройки к DOM только если режим доступности включен
133
+ if (settings.isEnabled) {
134
+ applyAllSettings(settings);
135
+ } else {
136
+ // Если режим выключен, удаляем все стили доступности
137
+ removeAllAccessibilityStyles();
138
+ }
139
+ } catch (error) {
140
+ console.warn('Ошибка при сохранении настроек доступности:', error);
141
+ }
142
+ }, [settings, isLoaded]);
143
+
144
+ /**
145
+ * Обновление настроек доступности
146
+ * Позволяет частично обновить настройки, сохраняя остальные значения
147
+ *
148
+ * @param updates - Частичные обновления настроек
149
+ */
150
+ const updateSettings = useCallback((updates: Partial<AccessibilitySettings>): void => {
151
+ setSettings(prevSettings => {
152
+ const newSettings = { ...prevSettings, ...updates };
153
+
154
+ // Если выключается общий режим доступности, удаляем все стили немедленно
155
+ if (prevSettings.isEnabled && !newSettings.isEnabled) {
156
+ // Требование 5.2: Выключение режима доступности с удалением всех стилей
157
+ removeAllAccessibilityStyles();
158
+ }
159
+
160
+ return newSettings;
161
+ });
162
+ }, []);
163
+
164
+ /**
165
+ * Сброс настроек к значениям по умолчанию
166
+ * Удаляет все примененные стили и очищает localStorage
167
+ */
168
+ const resetSettings = useCallback((): void => {
169
+ try {
170
+ // Удаляем все примененные стили доступности
171
+ removeAllAccessibilityStyles();
172
+
173
+ // Сбрасываем настройки к значениям по умолчанию
174
+ setSettings(DEFAULT_ACCESSIBILITY_SETTINGS);
175
+
176
+ // Очищаем localStorage
177
+ clearStorage();
178
+ } catch (error) {
179
+ console.warn('Ошибка при сбросе настроек доступности:', error);
180
+ }
181
+ }, []);
182
+
183
+ return {
184
+ settings,
185
+ updateSettings,
186
+ resetSettings,
187
+ isLoaded,
188
+ };
189
+ };
190
+
191
+ /**
192
+ * Хук для управления конкретной настройкой доступности
193
+ * Предоставляет удобный интерфейс для работы с отдельными настройками
194
+ *
195
+ * @param settingKey - Ключ настройки для управления
196
+ * @returns Значение настройки и функция для её обновления
197
+ */
198
+ export const useAccessibilitySetting = <K extends keyof AccessibilitySettings>(
199
+ settingKey: K
200
+ ): [AccessibilitySettings[K], (value: AccessibilitySettings[K]) => void] => {
201
+ const { settings, updateSettings } = useAccessibilitySettings();
202
+
203
+ const setValue = useCallback((value: AccessibilitySettings[K]) => {
204
+ updateSettings({ [settingKey]: value } as Partial<AccessibilitySettings>);
205
+ }, [settingKey, updateSettings]);
206
+
207
+ return [settings[settingKey], setValue];
208
+ };
209
+
210
+ /**
211
+ * Хук для управления общим режимом доступности
212
+ * Предоставляет удобный интерфейс для включения/выключения всех настроек
213
+ *
214
+ * @returns Состояние режима доступности и функция для его переключения
215
+ */
216
+ export const useAccessibilityToggle = (): [boolean, (enabled: boolean) => void] => {
217
+ const { settings, updateSettings } = useAccessibilitySettings();
218
+
219
+ const setEnabled = useCallback((enabled: boolean) => {
220
+ // Требование 5.1: Включение режима доступности с активацией всех настроек
221
+ // Требование 5.2: Выключение режима доступности с удалением всех стилей
222
+ updateSettings({ isEnabled: enabled });
223
+ }, [updateSettings]);
224
+
225
+ return [settings.isEnabled, setEnabled];
226
+ };
227
+
228
+ export default useAccessibilitySettings;