ayushsahu45 commited on
Commit
833bc6e
·
verified ·
1 Parent(s): a8a148d

Upload 2 files

Browse files
src/hooks/useDebounce.ts ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { useState, useEffect } from 'react';
2
+
3
+ export function useDebounce<T>(value: T, delay: number): T {
4
+ const [debouncedValue, setDebouncedValue] = useState<T>(value);
5
+
6
+ useEffect(() => {
7
+ const timer = setTimeout(() => {
8
+ setDebouncedValue(value);
9
+ }, delay);
10
+
11
+ return () => {
12
+ clearTimeout(timer);
13
+ };
14
+ }, [value, delay]);
15
+
16
+ return debouncedValue;
17
+ }
src/hooks/useKeyboardShortcuts.ts ADDED
@@ -0,0 +1,91 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { useEffect, useCallback, RefObject } from 'react';
2
+
3
+ export interface KeyboardShortcut {
4
+ key: string;
5
+ ctrl?: boolean;
6
+ shift?: boolean;
7
+ alt?: boolean;
8
+ handler: () => void;
9
+ description?: string;
10
+ }
11
+
12
+ export function useKeyboardShortcuts(shortcuts: KeyboardShortcut[], enabled = true) {
13
+ const handleKeyDown = useCallback((e: KeyboardEvent) => {
14
+ if (!enabled) return;
15
+ if (
16
+ e.target instanceof HTMLInputElement ||
17
+ e.target instanceof HTMLTextAreaElement ||
18
+ (e.target as HTMLElement).isContentEditable
19
+ ) {
20
+ return;
21
+ }
22
+
23
+ for (const shortcut of shortcuts) {
24
+ const keyMatch = e.key.toLowerCase() === shortcut.key.toLowerCase() ||
25
+ e.code.toLowerCase() === shortcut.key.toLowerCase();
26
+ const ctrlMatch = shortcut.ctrl ? (e.ctrlKey || e.metaKey) : true;
27
+ const shiftMatch = shortcut.shift ? e.shiftKey : !e.shiftKey;
28
+ const altMatch = shortcut.alt ? e.altKey : !e.altKey;
29
+
30
+ if (keyMatch && ctrlMatch && shiftMatch && altMatch) {
31
+ e.preventDefault();
32
+ shortcut.handler();
33
+ break;
34
+ }
35
+ }
36
+ }, [shortcuts, enabled]);
37
+
38
+ useEffect(() => {
39
+ window.addEventListener('keydown', handleKeyDown);
40
+ return () => window.removeEventListener('keydown', handleKeyDown);
41
+ }, [handleKeyDown]);
42
+ }
43
+
44
+ export function useFocusTrap<T extends HTMLElement>(ref: RefObject<T | null>, active: boolean) {
45
+ useEffect(() => {
46
+ if (!active || !ref.current) return;
47
+
48
+ const focusableElements = ref.current.querySelectorAll<HTMLElement>(
49
+ 'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
50
+ );
51
+ const firstElement = focusableElements[0];
52
+ const lastElement = focusableElements[focusableElements.length - 1];
53
+
54
+ const handleTabKey = (e: KeyboardEvent) => {
55
+ if (e.key !== 'Tab') return;
56
+
57
+ if (e.shiftKey) {
58
+ if (document.activeElement === firstElement) {
59
+ e.preventDefault();
60
+ lastElement?.focus();
61
+ }
62
+ } else {
63
+ if (document.activeElement === lastElement) {
64
+ e.preventDefault();
65
+ firstElement?.focus();
66
+ }
67
+ }
68
+ };
69
+
70
+ ref.current.addEventListener('keydown', handleTabKey);
71
+ firstElement?.focus();
72
+
73
+ return () => {
74
+ ref.current?.removeEventListener('keydown', handleTabKey);
75
+ };
76
+ }, [ref, active]);
77
+ }
78
+
79
+ export function announceToScreenReader(message: string, priority: 'polite' | 'assertive' = 'polite') {
80
+ const el = document.createElement('div');
81
+ el.setAttribute('role', 'status');
82
+ el.setAttribute('aria-live', priority);
83
+ el.setAttribute('aria-atomic', 'true');
84
+ el.style.cssText = 'position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0;';
85
+ document.body.appendChild(el);
86
+
87
+ setTimeout(() => {
88
+ el.textContent = message;
89
+ setTimeout(() => document.body.removeChild(el), 1000);
90
+ }, 100);
91
+ }