@codesinger0/shared-components 1.1.13 → 1.1.14
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,331 @@
|
|
|
1
|
+
// src/components/AccessibilityMenu.jsx
|
|
2
|
+
import React, { useState, useEffect } from 'react';
|
|
3
|
+
import {
|
|
4
|
+
Accessibility, ZoomIn, ZoomOut, Contrast, Sun, Moon, Link, Heading,
|
|
5
|
+
MousePointer2, X, CircleOff, Glasses, Palette, Square
|
|
6
|
+
} from 'lucide-react';
|
|
7
|
+
import { motion, AnimatePresence } from 'framer-motion';
|
|
8
|
+
|
|
9
|
+
export default function AccessibilityMenu() {
|
|
10
|
+
const [isOpen, setIsOpen] = useState(false);
|
|
11
|
+
const [activeOptions, setActiveOptions] = useState({});
|
|
12
|
+
const [fontSize, setFontSize] = useState(1);
|
|
13
|
+
|
|
14
|
+
const applyOption = (option, apply) => {
|
|
15
|
+
document.documentElement.classList.toggle(`accessibility-${option}`, apply);
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
const resetAll = () => {
|
|
19
|
+
Object.keys(activeOptions).forEach(option => {
|
|
20
|
+
if(activeOptions[option]){
|
|
21
|
+
applyOption(option, false);
|
|
22
|
+
}
|
|
23
|
+
});
|
|
24
|
+
setActiveOptions({});
|
|
25
|
+
setFontSize(1);
|
|
26
|
+
document.documentElement.style.setProperty('--font-size-multiplier', 1);
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
const toggleOption = (option, exclusiveGroup = null) => {
|
|
30
|
+
const newActiveOptions = { ...activeOptions };
|
|
31
|
+
const currentState = newActiveOptions[option];
|
|
32
|
+
|
|
33
|
+
if (exclusiveGroup) {
|
|
34
|
+
Object.keys(newActiveOptions).forEach(opt => {
|
|
35
|
+
if (opt.startsWith(exclusiveGroup) && opt !== option) {
|
|
36
|
+
newActiveOptions[opt] = false;
|
|
37
|
+
applyOption(opt, false);
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
newActiveOptions[option] = !currentState;
|
|
43
|
+
setActiveOptions(newActiveOptions);
|
|
44
|
+
applyOption(option, newActiveOptions[option]);
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
const changeFontSize = (amount) => {
|
|
48
|
+
const newSize = Math.max(0.8, Math.min(1.5, fontSize + amount));
|
|
49
|
+
setFontSize(newSize);
|
|
50
|
+
document.documentElement.style.setProperty('--font-size-multiplier', newSize);
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
const tools = [
|
|
54
|
+
{ id: 'increase-font', label: 'הגדלת גופן', icon: ZoomIn, action: () => changeFontSize(0.1) },
|
|
55
|
+
{ id: 'decrease-font', label: 'הקטנת גופן', icon: ZoomOut, action: () => changeFontSize(-0.1) },
|
|
56
|
+
{ id: 'readable-font', label: 'גופן קריא', icon: Glasses, action: () => toggleOption('readable-font') },
|
|
57
|
+
{ id: 'highlight-links', label: 'הדגשת קישורים', icon: Link, action: () => toggleOption('highlight-links') },
|
|
58
|
+
{ id: 'highlight-headers', label: 'הדגשת כותרות', icon: Heading, action: () => toggleOption('highlight-headers') },
|
|
59
|
+
{ id: 'contrast-high', label: 'ניגודיות גבוהה', icon: Contrast, action: () => toggleOption('contrast-high', 'contrast-') },
|
|
60
|
+
{ id: 'contrast-dark', label: 'רקע כהה', icon: Moon, action: () => toggleOption('contrast-dark', 'contrast-') },
|
|
61
|
+
{ id: 'contrast-light', label: 'רקע בהיר', icon: Sun, action: () => toggleOption('contrast-light', 'contrast-') },
|
|
62
|
+
{ id: 'invert-colors', label: 'היפוך צבעים', icon: Palette, action: () => toggleOption('invert-colors', 'contrast-') },
|
|
63
|
+
{ id: 'monochrome', label: 'מונוכרום', icon: CircleOff, action: () => toggleOption('monochrome', 'contrast-') },
|
|
64
|
+
{ id: 'big-cursor', label: 'סמן גדול', icon: MousePointer2, action: () => toggleOption('big-cursor') },
|
|
65
|
+
{ id: 'disable-animations', label: 'ביטול אנימציות', icon: Square, action: () => toggleOption('disable-animations') },
|
|
66
|
+
];
|
|
67
|
+
|
|
68
|
+
useEffect(() => {
|
|
69
|
+
const handleEsc = (event) => {
|
|
70
|
+
if (event.key === 'Escape' && isOpen) {
|
|
71
|
+
setIsOpen(false);
|
|
72
|
+
}
|
|
73
|
+
};
|
|
74
|
+
window.addEventListener('keydown', handleEsc);
|
|
75
|
+
return () => window.removeEventListener('keydown', handleEsc);
|
|
76
|
+
}, [isOpen]);
|
|
77
|
+
|
|
78
|
+
return (
|
|
79
|
+
<>
|
|
80
|
+
<style>{`
|
|
81
|
+
:root { --font-size-multiplier: 1; }
|
|
82
|
+
|
|
83
|
+
/* Readable Font */
|
|
84
|
+
html.accessibility-readable-font body {
|
|
85
|
+
font-family: Arial, sans-serif !important;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/* Highlight Links */
|
|
89
|
+
html.accessibility-highlight-links a {
|
|
90
|
+
background-color: yellow !important;
|
|
91
|
+
color: black !important;
|
|
92
|
+
padding: 2px 4px;
|
|
93
|
+
border: 1px solid black;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/* Highlight Headers */
|
|
97
|
+
html.accessibility-highlight-headers h1,
|
|
98
|
+
html.accessibility-highlight-headers h2,
|
|
99
|
+
html.accessibility-highlight-headers h3,
|
|
100
|
+
html.accessibility-highlight-headers h4 {
|
|
101
|
+
background-color: #add8e6 !important;
|
|
102
|
+
padding: 4px;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/* High Contrast Mode */
|
|
106
|
+
html.accessibility-contrast-high {
|
|
107
|
+
filter: contrast(200%) !important;
|
|
108
|
+
}
|
|
109
|
+
html.accessibility-contrast-high body {
|
|
110
|
+
background-color: white !important;
|
|
111
|
+
color: black !important;
|
|
112
|
+
}
|
|
113
|
+
html.accessibility-contrast-high h1,
|
|
114
|
+
html.accessibility-contrast-high h2,
|
|
115
|
+
html.accessibility-contrast-high h3,
|
|
116
|
+
html.accessibility-contrast-high h4,
|
|
117
|
+
html.accessibility-contrast-high p,
|
|
118
|
+
html.accessibility-contrast-high span,
|
|
119
|
+
html.accessibility-contrast-high div {
|
|
120
|
+
color: black !important;
|
|
121
|
+
}
|
|
122
|
+
html.accessibility-contrast-high a {
|
|
123
|
+
color: blue !important;
|
|
124
|
+
text-decoration: underline !important;
|
|
125
|
+
}
|
|
126
|
+
html.accessibility-contrast-high button {
|
|
127
|
+
background-color: black !important;
|
|
128
|
+
color: white !important;
|
|
129
|
+
border: 2px solid black !important;
|
|
130
|
+
}
|
|
131
|
+
html.accessibility-contrast-high input,
|
|
132
|
+
html.accessibility-contrast-high textarea {
|
|
133
|
+
background-color: white !important;
|
|
134
|
+
color: black !important;
|
|
135
|
+
border: 2px solid black !important;
|
|
136
|
+
}
|
|
137
|
+
html.accessibility-contrast-high [class*="bg-green"],
|
|
138
|
+
html.accessibility-contrast-high [class*="bg-sky"],
|
|
139
|
+
html.accessibility-contrast-high [class*="bg-blue"],
|
|
140
|
+
html.accessibility-contrast-high [class*="bg-gradient"] {
|
|
141
|
+
background-color: white !important;
|
|
142
|
+
color: black !important;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/* Dark Background */
|
|
146
|
+
html.accessibility-contrast-dark body {
|
|
147
|
+
background-color: #1a1a1a !important;
|
|
148
|
+
color: #ffffff !important;
|
|
149
|
+
}
|
|
150
|
+
html.accessibility-contrast-dark div,
|
|
151
|
+
html.accessibility-contrast-dark section,
|
|
152
|
+
html.accessibility-contrast-dark p,
|
|
153
|
+
html.accessibility-contrast-dark h1,
|
|
154
|
+
html.accessibility-contrast-dark h2,
|
|
155
|
+
html.accessibility-contrast-dark h3,
|
|
156
|
+
html.accessibility-contrast-dark h4,
|
|
157
|
+
html.accessibility-contrast-dark span {
|
|
158
|
+
color: #ffffff !important;
|
|
159
|
+
}
|
|
160
|
+
html.accessibility-contrast-dark [class*="bg-white"],
|
|
161
|
+
html.accessibility-contrast-dark [class*="bg-gray"],
|
|
162
|
+
html.accessibility-contrast-dark [class*="bg-green"],
|
|
163
|
+
html.accessibility-contrast-dark [class*="bg-sky"],
|
|
164
|
+
html.accessibility-contrast-dark [class*="bg-main"],
|
|
165
|
+
html.accessibility-contrast-dark [class*="glass-card"] {
|
|
166
|
+
background-color: #2a2a2a !important;
|
|
167
|
+
}
|
|
168
|
+
html.accessibility-contrast-dark a {
|
|
169
|
+
color: #87cefa !important;
|
|
170
|
+
}
|
|
171
|
+
html.accessibility-contrast-dark button {
|
|
172
|
+
background-color: #333 !important;
|
|
173
|
+
color: #fff !important;
|
|
174
|
+
border: 1px solid #666 !important;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/* Light Background */
|
|
178
|
+
html.accessibility-contrast-light body {
|
|
179
|
+
background-color: #ffffff !important;
|
|
180
|
+
color: #000000 !important;
|
|
181
|
+
}
|
|
182
|
+
html.accessibility-contrast-light div,
|
|
183
|
+
html.accessibility-contrast-light section,
|
|
184
|
+
html.accessibility-contrast-light p,
|
|
185
|
+
html.accessibility-contrast-light h1,
|
|
186
|
+
html.accessibility-contrast-light h2,
|
|
187
|
+
html.accessibility-contrast-light h3,
|
|
188
|
+
html.accessibility-contrast-light h4,
|
|
189
|
+
html.accessibility-contrast-light span {
|
|
190
|
+
color: #000000 !important;
|
|
191
|
+
}
|
|
192
|
+
html.accessibility-contrast-light [class*="bg-"] {
|
|
193
|
+
background-color: #f8f8f8 !important;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/* Invert Colors */
|
|
197
|
+
html.accessibility-invert-colors {
|
|
198
|
+
filter: invert(1) !important;
|
|
199
|
+
}
|
|
200
|
+
html.accessibility-invert-colors img,
|
|
201
|
+
html.accessibility-invert-colors video {
|
|
202
|
+
filter: invert(1) !important;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/* Monochrome */
|
|
206
|
+
html.accessibility-monochrome {
|
|
207
|
+
filter: grayscale(100%) !important;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
/* Big Cursor */
|
|
211
|
+
html.accessibility-big-cursor * {
|
|
212
|
+
cursor: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='48' height='48' viewport='0 0 48 48' style='fill:black;font-size:24px;'><path d='M 10,2 L 10,42 L 18,34 L 25,42 L 30,40 L 22,32 L 32,32 L 10,2 Z'/></svg>") 16 0, auto !important;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/* Disable Animations */
|
|
216
|
+
html.accessibility-disable-animations * {
|
|
217
|
+
animation: none !important;
|
|
218
|
+
transition: none !important;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
/* Font Size Scaling */
|
|
222
|
+
body {
|
|
223
|
+
font-size: calc(1rem * var(--font-size-multiplier));
|
|
224
|
+
}
|
|
225
|
+
h1 {
|
|
226
|
+
font-size: calc(2.25rem * var(--font-size-multiplier));
|
|
227
|
+
}
|
|
228
|
+
h2 {
|
|
229
|
+
font-size: calc(1.75rem * var(--font-size-multiplier));
|
|
230
|
+
}
|
|
231
|
+
h3 {
|
|
232
|
+
font-size: calc(1.5rem * var(--font-size-multiplier));
|
|
233
|
+
}
|
|
234
|
+
h4 {
|
|
235
|
+
font-size: calc(1.25rem * var(--font-size-multiplier));
|
|
236
|
+
}
|
|
237
|
+
p, li, span, a, button, div {
|
|
238
|
+
font-size: calc(1em * var(--font-size-multiplier));
|
|
239
|
+
}
|
|
240
|
+
`}</style>
|
|
241
|
+
|
|
242
|
+
{/* Floating Accessibility Button */}
|
|
243
|
+
<div className="fixed bottom-4 left-4 z-[100]">
|
|
244
|
+
<motion.button
|
|
245
|
+
onClick={() => setIsOpen(!isOpen)}
|
|
246
|
+
className="bg-sky-600 text-white rounded-full p-4 shadow-lg hover:bg-sky-700 transition-colors focus:outline-none focus:ring-2 focus:ring-sky-400"
|
|
247
|
+
whileHover={{ scale: 1.1 }}
|
|
248
|
+
whileTap={{ scale: 0.9 }}
|
|
249
|
+
aria-label="פתח תפריט נגישות"
|
|
250
|
+
>
|
|
251
|
+
<Accessibility className="w-6 h-6" />
|
|
252
|
+
</motion.button>
|
|
253
|
+
</div>
|
|
254
|
+
|
|
255
|
+
{/* Accessibility Menu Panel */}
|
|
256
|
+
<AnimatePresence>
|
|
257
|
+
{isOpen && (
|
|
258
|
+
<>
|
|
259
|
+
{/* Backdrop */}
|
|
260
|
+
<motion.div
|
|
261
|
+
initial={{ opacity: 0 }}
|
|
262
|
+
animate={{ opacity: 1 }}
|
|
263
|
+
exit={{ opacity: 0 }}
|
|
264
|
+
className="fixed inset-0 bg-black bg-opacity-30 z-[98]"
|
|
265
|
+
onClick={() => setIsOpen(false)}
|
|
266
|
+
/>
|
|
267
|
+
|
|
268
|
+
{/* Menu Panel */}
|
|
269
|
+
<motion.div
|
|
270
|
+
initial={{ opacity: 0, y: 50 }}
|
|
271
|
+
animate={{ opacity: 1, y: 0 }}
|
|
272
|
+
exit={{ opacity: 0, y: 50 }}
|
|
273
|
+
className="fixed bottom-20 left-4 z-[99] w-[90vw] max-w-lg"
|
|
274
|
+
dir="rtl"
|
|
275
|
+
>
|
|
276
|
+
<div className="bg-white rounded-xl shadow-2xl border-2 border-sky-200 p-4">
|
|
277
|
+
{/* Header */}
|
|
278
|
+
<div className="flex justify-between items-center mb-4">
|
|
279
|
+
<h3 className="font-bold text-lg text-sky-800">תפריט נגישות</h3>
|
|
280
|
+
<div className="flex gap-2">
|
|
281
|
+
<button
|
|
282
|
+
onClick={resetAll}
|
|
283
|
+
className="px-3 py-1 text-sm text-sky-700 hover:bg-sky-50 rounded-md transition-colors"
|
|
284
|
+
>
|
|
285
|
+
איפוס
|
|
286
|
+
</button>
|
|
287
|
+
<button
|
|
288
|
+
onClick={() => setIsOpen(false)}
|
|
289
|
+
className="p-1 hover:bg-gray-100 rounded-md transition-colors"
|
|
290
|
+
aria-label="סגור תפריט נגישות"
|
|
291
|
+
>
|
|
292
|
+
<X className="w-5 h-5" />
|
|
293
|
+
</button>
|
|
294
|
+
</div>
|
|
295
|
+
</div>
|
|
296
|
+
|
|
297
|
+
{/* Font Size Display */}
|
|
298
|
+
<div className="mb-4 text-center text-sm text-gray-600">
|
|
299
|
+
גודל גופן: {Math.round(fontSize * 100)}%
|
|
300
|
+
</div>
|
|
301
|
+
|
|
302
|
+
{/* Tools Grid */}
|
|
303
|
+
<div className="grid grid-cols-2 sm:grid-cols-3 md:grid-cols-4 gap-2">
|
|
304
|
+
{tools.map(tool => (
|
|
305
|
+
<button
|
|
306
|
+
key={tool.id}
|
|
307
|
+
onClick={tool.action}
|
|
308
|
+
className={`flex flex-col items-center justify-center h-20 text-center gap-1 rounded-lg border-2 transition-all ${
|
|
309
|
+
activeOptions[tool.id]
|
|
310
|
+
? 'bg-sky-100 border-sky-500 text-sky-800'
|
|
311
|
+
: 'bg-white border-gray-300 hover:border-sky-300 hover:bg-sky-50'
|
|
312
|
+
}`}
|
|
313
|
+
>
|
|
314
|
+
<tool.icon className="w-6 h-6 mb-1" />
|
|
315
|
+
<span className="text-xs px-1">{tool.label}</span>
|
|
316
|
+
</button>
|
|
317
|
+
))}
|
|
318
|
+
</div>
|
|
319
|
+
|
|
320
|
+
{/* Footer Info */}
|
|
321
|
+
<div className="mt-4 pt-3 border-t border-gray-200 text-center text-xs text-gray-500">
|
|
322
|
+
לחץ ESC לסגירת התפריט
|
|
323
|
+
</div>
|
|
324
|
+
</div>
|
|
325
|
+
</motion.div>
|
|
326
|
+
</>
|
|
327
|
+
)}
|
|
328
|
+
</AnimatePresence>
|
|
329
|
+
</>
|
|
330
|
+
);
|
|
331
|
+
}
|
package/dist/index.js
CHANGED
|
@@ -16,6 +16,7 @@ export { default as Menu } from './components/Menu'
|
|
|
16
16
|
export { default as ProductsDisplay } from './components/products/ProductsDisplay'
|
|
17
17
|
export { default as ProductsSidebar } from './components/products/ProductsSidebar'
|
|
18
18
|
export { default as MyOrdersDisplay } from './components/MyOrdersDisplay'
|
|
19
|
+
export { default as AccessibilityMenu } from './components/AccessibilityMenu'
|
|
19
20
|
|
|
20
21
|
// Modals
|
|
21
22
|
export { default as ItemDetailsModal } from './components/modals/ItemDetailsModal'
|