@a13y/react 0.1.1 → 0.1.3
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/components/index.d.ts +868 -2
- package/dist/components/index.js +2818 -2
- package/dist/components/index.js.map +1 -1
- package/dist/hooks/index.d.ts +144 -2
- package/dist/hooks/index.js +149 -1
- package/dist/hooks/index.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js +2840 -2
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/dist/hooks/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export { A as AccessibleButtonProps, P as PressEvent, U as UseAccessibleButtonProps, a as UseAccessibleButtonReturn, u as useAccessibleButton } from '../use-accessible-button-B0syf-Az.js';
|
|
2
|
-
import { AriaRole } from 'react';
|
|
2
|
+
import { AriaRole, RefObject } from 'react';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* @a13y/react - useAccessibleDialog
|
|
@@ -722,4 +722,146 @@ type RequireLabel<T> = T & {
|
|
|
722
722
|
label: string;
|
|
723
723
|
};
|
|
724
724
|
|
|
725
|
-
|
|
725
|
+
/**
|
|
726
|
+
* useId Hook
|
|
727
|
+
* Generates unique IDs for accessibility attributes
|
|
728
|
+
* Provides backward compatibility for React < 18
|
|
729
|
+
*/
|
|
730
|
+
/**
|
|
731
|
+
* Generates a unique ID for accessibility attributes
|
|
732
|
+
* @param prefix - Optional prefix for the ID
|
|
733
|
+
* @returns A unique ID string
|
|
734
|
+
*
|
|
735
|
+
* @example
|
|
736
|
+
* ```tsx
|
|
737
|
+
* const tooltipId = useId('tooltip');
|
|
738
|
+
* // Returns: "tooltip-1"
|
|
739
|
+
* ```
|
|
740
|
+
*/
|
|
741
|
+
declare function useId(prefix?: string): string;
|
|
742
|
+
|
|
743
|
+
/**
|
|
744
|
+
* useReducedMotion Hook
|
|
745
|
+
* Detects user's preference for reduced motion
|
|
746
|
+
*/
|
|
747
|
+
/**
|
|
748
|
+
* Detects if the user prefers reduced motion
|
|
749
|
+
* @returns true if the user prefers reduced motion
|
|
750
|
+
*
|
|
751
|
+
* @example
|
|
752
|
+
* ```tsx
|
|
753
|
+
* const prefersReducedMotion = useReducedMotion();
|
|
754
|
+
*
|
|
755
|
+
* <div style={{
|
|
756
|
+
* transition: prefersReducedMotion ? 'none' : 'all 0.3s ease'
|
|
757
|
+
* }}>
|
|
758
|
+
* Content
|
|
759
|
+
* </div>
|
|
760
|
+
* ```
|
|
761
|
+
*/
|
|
762
|
+
declare function useReducedMotion(): boolean;
|
|
763
|
+
|
|
764
|
+
/**
|
|
765
|
+
* useClickOutside Hook
|
|
766
|
+
* Detects clicks outside of a referenced element
|
|
767
|
+
*/
|
|
768
|
+
|
|
769
|
+
/**
|
|
770
|
+
* Calls a handler when user clicks outside of the referenced element
|
|
771
|
+
* @param ref - React ref to the element
|
|
772
|
+
* @param handler - Function to call when clicking outside
|
|
773
|
+
* @param enabled - Whether the hook is enabled (default: true)
|
|
774
|
+
*
|
|
775
|
+
* @example
|
|
776
|
+
* ```tsx
|
|
777
|
+
* const menuRef = useRef<HTMLDivElement>(null);
|
|
778
|
+
* useClickOutside(menuRef, () => setIsOpen(false));
|
|
779
|
+
*
|
|
780
|
+
* <div ref={menuRef}>
|
|
781
|
+
* Menu content
|
|
782
|
+
* </div>
|
|
783
|
+
* ```
|
|
784
|
+
*/
|
|
785
|
+
declare function useClickOutside(ref: RefObject<HTMLElement | null>, handler: (event: MouseEvent | TouchEvent) => void, enabled?: boolean): void;
|
|
786
|
+
|
|
787
|
+
/**
|
|
788
|
+
* useMediaQuery Hook
|
|
789
|
+
* Tracks the state of a CSS media query
|
|
790
|
+
*/
|
|
791
|
+
/**
|
|
792
|
+
* Tracks whether a media query matches
|
|
793
|
+
* @param query - CSS media query string
|
|
794
|
+
* @returns true if the media query matches
|
|
795
|
+
*
|
|
796
|
+
* @example
|
|
797
|
+
* ```tsx
|
|
798
|
+
* const isMobile = useMediaQuery('(max-width: 768px)');
|
|
799
|
+
* const prefersDark = useMediaQuery('(prefers-color-scheme: dark)');
|
|
800
|
+
*
|
|
801
|
+
* {isMobile ? <MobileView /> : <DesktopView />}
|
|
802
|
+
* ```
|
|
803
|
+
*/
|
|
804
|
+
declare function useMediaQuery(query: string): boolean;
|
|
805
|
+
|
|
806
|
+
/**
|
|
807
|
+
* useAriaLive Hook
|
|
808
|
+
* Manages ARIA live regions for dynamic announcements
|
|
809
|
+
*/
|
|
810
|
+
type AriaLivePoliteness = 'polite' | 'assertive' | 'off';
|
|
811
|
+
interface UseAriaLiveReturn {
|
|
812
|
+
/** Sets a message to be announced */
|
|
813
|
+
setMessage: (message: string) => void;
|
|
814
|
+
/** Clears the current message */
|
|
815
|
+
clearMessage: () => void;
|
|
816
|
+
/** Current message */
|
|
817
|
+
message: string;
|
|
818
|
+
/** Props to spread on the live region element */
|
|
819
|
+
liveRegionProps: {
|
|
820
|
+
role: 'status' | 'alert';
|
|
821
|
+
'aria-live': AriaLivePoliteness;
|
|
822
|
+
'aria-atomic': 'true';
|
|
823
|
+
'aria-relevant': 'additions text';
|
|
824
|
+
};
|
|
825
|
+
}
|
|
826
|
+
/**
|
|
827
|
+
* Creates a managed ARIA live region
|
|
828
|
+
* @param politeness - How urgently the message should be announced
|
|
829
|
+
* @returns Object with message management functions and props
|
|
830
|
+
*
|
|
831
|
+
* @example
|
|
832
|
+
* ```tsx
|
|
833
|
+
* const { setMessage, liveRegionProps } = useAriaLive('polite');
|
|
834
|
+
*
|
|
835
|
+
* <button onClick={() => setMessage('Item added to cart')}>
|
|
836
|
+
* Add to cart
|
|
837
|
+
* </button>
|
|
838
|
+
*
|
|
839
|
+
* <div {...liveRegionProps} className="sr-only">
|
|
840
|
+
* {message}
|
|
841
|
+
* </div>
|
|
842
|
+
* ```
|
|
843
|
+
*/
|
|
844
|
+
declare function useAriaLive(politeness?: AriaLivePoliteness): UseAriaLiveReturn;
|
|
845
|
+
|
|
846
|
+
/**
|
|
847
|
+
* useAnnounce Hook
|
|
848
|
+
* Provides a simple way to make screen reader announcements
|
|
849
|
+
*/
|
|
850
|
+
|
|
851
|
+
/**
|
|
852
|
+
* Creates a function to announce messages to screen readers
|
|
853
|
+
* @returns Function to announce messages
|
|
854
|
+
*
|
|
855
|
+
* @example
|
|
856
|
+
* ```tsx
|
|
857
|
+
* const announce = useAnnounce();
|
|
858
|
+
*
|
|
859
|
+
* const handleAddToCart = () => {
|
|
860
|
+
* addItem(item);
|
|
861
|
+
* announce('Item added to cart', 'polite');
|
|
862
|
+
* };
|
|
863
|
+
* ```
|
|
864
|
+
*/
|
|
865
|
+
declare function useAnnounce(): (message: string, politeness?: AriaLivePoliteness) => void;
|
|
866
|
+
|
|
867
|
+
export { type AriaLivePoliteness, type DialogBackdropProps, type DialogContainerProps, type DialogDescriptionProps, type DialogTitleProps, type FieldConfig, type FieldProps, type FieldValidator, type FormConfig, type FormState, type FormValidator, type NavigableItemProps, type RequireLabel, type UseAccessibleDialogProps, type UseAccessibleDialogReturn, type UseAccessibleFormReturn, type UseAriaLiveReturn, type UseFocusTrapProps, type UseFocusTrapReturn, type UseFormFieldProps, type UseFormFieldReturn, type UseKeyboardNavigationProps, type UseKeyboardNavigationReturn, useAccessibleDialog, useAccessibleForm, useAnnounce, useAriaLive, useClickOutside, useFocusTrap, useFormField, useId, useKeyboardNavigation, useMediaQuery, useReducedMotion };
|
package/dist/hooks/index.js
CHANGED
|
@@ -642,7 +642,155 @@ var useFormField = (props) => {
|
|
|
642
642
|
fieldRef
|
|
643
643
|
};
|
|
644
644
|
};
|
|
645
|
+
var idCounter = 0;
|
|
646
|
+
function useId3(prefix = "a13y") {
|
|
647
|
+
const idRef = useRef(void 0);
|
|
648
|
+
if (!idRef.current) {
|
|
649
|
+
idRef.current = `${prefix}-${++idCounter}`;
|
|
650
|
+
}
|
|
651
|
+
return idRef.current;
|
|
652
|
+
}
|
|
653
|
+
function useReducedMotion() {
|
|
654
|
+
const [prefersReducedMotion, setPrefersReducedMotion] = useState(() => {
|
|
655
|
+
if (typeof window === "undefined") return false;
|
|
656
|
+
const mediaQuery = window.matchMedia("(prefers-reduced-motion: reduce)");
|
|
657
|
+
return mediaQuery.matches;
|
|
658
|
+
});
|
|
659
|
+
useEffect(() => {
|
|
660
|
+
if (typeof window === "undefined") return;
|
|
661
|
+
const mediaQuery = window.matchMedia("(prefers-reduced-motion: reduce)");
|
|
662
|
+
const handleChange = (event) => {
|
|
663
|
+
setPrefersReducedMotion(event.matches);
|
|
664
|
+
};
|
|
665
|
+
if (mediaQuery.addEventListener) {
|
|
666
|
+
mediaQuery.addEventListener("change", handleChange);
|
|
667
|
+
return () => mediaQuery.removeEventListener("change", handleChange);
|
|
668
|
+
}
|
|
669
|
+
mediaQuery.addListener(handleChange);
|
|
670
|
+
return () => mediaQuery.removeListener(handleChange);
|
|
671
|
+
}, []);
|
|
672
|
+
return prefersReducedMotion;
|
|
673
|
+
}
|
|
674
|
+
function useClickOutside(ref, handler, enabled = true) {
|
|
675
|
+
useEffect(() => {
|
|
676
|
+
if (!enabled) return;
|
|
677
|
+
const handleClickOutside = (event) => {
|
|
678
|
+
if (!ref.current || ref.current.contains(event.target)) {
|
|
679
|
+
return;
|
|
680
|
+
}
|
|
681
|
+
handler(event);
|
|
682
|
+
};
|
|
683
|
+
document.addEventListener("mousedown", handleClickOutside);
|
|
684
|
+
document.addEventListener("touchstart", handleClickOutside);
|
|
685
|
+
return () => {
|
|
686
|
+
document.removeEventListener("mousedown", handleClickOutside);
|
|
687
|
+
document.removeEventListener("touchstart", handleClickOutside);
|
|
688
|
+
};
|
|
689
|
+
}, [ref, handler, enabled]);
|
|
690
|
+
}
|
|
691
|
+
function useMediaQuery(query) {
|
|
692
|
+
const [matches, setMatches] = useState(() => {
|
|
693
|
+
if (typeof window === "undefined") return false;
|
|
694
|
+
const mediaQuery = window.matchMedia(query);
|
|
695
|
+
return mediaQuery.matches;
|
|
696
|
+
});
|
|
697
|
+
useEffect(() => {
|
|
698
|
+
if (typeof window === "undefined") return;
|
|
699
|
+
const mediaQuery = window.matchMedia(query);
|
|
700
|
+
setMatches(mediaQuery.matches);
|
|
701
|
+
const handleChange = (event) => {
|
|
702
|
+
setMatches(event.matches);
|
|
703
|
+
};
|
|
704
|
+
if (mediaQuery.addEventListener) {
|
|
705
|
+
mediaQuery.addEventListener("change", handleChange);
|
|
706
|
+
return () => mediaQuery.removeEventListener("change", handleChange);
|
|
707
|
+
}
|
|
708
|
+
mediaQuery.addListener(handleChange);
|
|
709
|
+
return () => mediaQuery.removeListener(handleChange);
|
|
710
|
+
}, [query]);
|
|
711
|
+
return matches;
|
|
712
|
+
}
|
|
713
|
+
function useAriaLive(politeness = "polite") {
|
|
714
|
+
const [message, setMessage] = useState("");
|
|
715
|
+
const timeoutRef = useRef(void 0);
|
|
716
|
+
const clearMessage = () => {
|
|
717
|
+
setMessage("");
|
|
718
|
+
if (timeoutRef.current) {
|
|
719
|
+
clearTimeout(timeoutRef.current);
|
|
720
|
+
}
|
|
721
|
+
};
|
|
722
|
+
const setMessageWithClear = (newMessage) => {
|
|
723
|
+
setMessage(newMessage);
|
|
724
|
+
if (timeoutRef.current) {
|
|
725
|
+
clearTimeout(timeoutRef.current);
|
|
726
|
+
}
|
|
727
|
+
timeoutRef.current = setTimeout(() => {
|
|
728
|
+
setMessage("");
|
|
729
|
+
}, 5e3);
|
|
730
|
+
};
|
|
731
|
+
useEffect(() => {
|
|
732
|
+
return () => {
|
|
733
|
+
if (timeoutRef.current) {
|
|
734
|
+
clearTimeout(timeoutRef.current);
|
|
735
|
+
}
|
|
736
|
+
};
|
|
737
|
+
}, []);
|
|
738
|
+
return {
|
|
739
|
+
setMessage: setMessageWithClear,
|
|
740
|
+
clearMessage,
|
|
741
|
+
message,
|
|
742
|
+
liveRegionProps: {
|
|
743
|
+
role: politeness === "assertive" ? "alert" : "status",
|
|
744
|
+
"aria-live": politeness,
|
|
745
|
+
"aria-atomic": "true",
|
|
746
|
+
"aria-relevant": "additions text"
|
|
747
|
+
}
|
|
748
|
+
};
|
|
749
|
+
}
|
|
750
|
+
function useAnnounce() {
|
|
751
|
+
const regionRef = useRef(null);
|
|
752
|
+
useEffect(() => {
|
|
753
|
+
if (!regionRef.current) {
|
|
754
|
+
const region = document.createElement("div");
|
|
755
|
+
region.setAttribute("role", "status");
|
|
756
|
+
region.setAttribute("aria-live", "polite");
|
|
757
|
+
region.setAttribute("aria-atomic", "true");
|
|
758
|
+
region.style.position = "absolute";
|
|
759
|
+
region.style.left = "-10000px";
|
|
760
|
+
region.style.width = "1px";
|
|
761
|
+
region.style.height = "1px";
|
|
762
|
+
region.style.overflow = "hidden";
|
|
763
|
+
document.body.appendChild(region);
|
|
764
|
+
regionRef.current = region;
|
|
765
|
+
}
|
|
766
|
+
return () => {
|
|
767
|
+
if (regionRef.current) {
|
|
768
|
+
document.body.removeChild(regionRef.current);
|
|
769
|
+
regionRef.current = null;
|
|
770
|
+
}
|
|
771
|
+
};
|
|
772
|
+
}, []);
|
|
773
|
+
const announce3 = useCallback(
|
|
774
|
+
(message, politeness = "polite") => {
|
|
775
|
+
if (!regionRef.current) return;
|
|
776
|
+
regionRef.current.setAttribute("aria-live", politeness);
|
|
777
|
+
regionRef.current.textContent = "";
|
|
778
|
+
setTimeout(() => {
|
|
779
|
+
if (regionRef.current) {
|
|
780
|
+
regionRef.current.textContent = message;
|
|
781
|
+
}
|
|
782
|
+
}, 100);
|
|
783
|
+
setTimeout(() => {
|
|
784
|
+
if (regionRef.current) {
|
|
785
|
+
regionRef.current.textContent = "";
|
|
786
|
+
}
|
|
787
|
+
}, 5100);
|
|
788
|
+
},
|
|
789
|
+
[]
|
|
790
|
+
);
|
|
791
|
+
return announce3;
|
|
792
|
+
}
|
|
645
793
|
|
|
646
|
-
export { useAccessibleButton, useAccessibleDialog, useAccessibleForm, useFocusTrap, useFormField, useKeyboardNavigation };
|
|
794
|
+
export { useAccessibleButton, useAccessibleDialog, useAccessibleForm, useAnnounce, useAriaLive, useClickOutside, useFocusTrap, useFormField, useId3 as useId, useKeyboardNavigation, useMediaQuery, useReducedMotion };
|
|
647
795
|
//# sourceMappingURL=index.js.map
|
|
648
796
|
//# sourceMappingURL=index.js.map
|