UNPKG

@neuroequality/neuroadapt-mobile

Version:

Mobile accessibility features for React Native and cross-platform apps

546 lines (545 loc) 20.8 kB
import require$$0, { createContext, useState, useEffect, useContext } from "react"; import { useColorScheme, AccessibilityInfo, Platform, Dimensions } from "react-native"; var jsxRuntime = { exports: {} }; var reactJsxRuntime_production = {}; /** * @license React * react-jsx-runtime.production.js * * Copyright (c) Meta Platforms, Inc. and affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. */ var hasRequiredReactJsxRuntime_production; function requireReactJsxRuntime_production() { if (hasRequiredReactJsxRuntime_production) return reactJsxRuntime_production; hasRequiredReactJsxRuntime_production = 1; var REACT_ELEMENT_TYPE = Symbol.for("react.transitional.element"), REACT_FRAGMENT_TYPE = Symbol.for("react.fragment"); function jsxProd(type, config, maybeKey) { var key = null; void 0 !== maybeKey && (key = "" + maybeKey); void 0 !== config.key && (key = "" + config.key); if ("key" in config) { maybeKey = {}; for (var propName in config) "key" !== propName && (maybeKey[propName] = config[propName]); } else maybeKey = config; config = maybeKey.ref; return { $$typeof: REACT_ELEMENT_TYPE, type, key, ref: void 0 !== config ? config : null, props: maybeKey }; } reactJsxRuntime_production.Fragment = REACT_FRAGMENT_TYPE; reactJsxRuntime_production.jsx = jsxProd; reactJsxRuntime_production.jsxs = jsxProd; return reactJsxRuntime_production; } var reactJsxRuntime_development = {}; /** * @license React * react-jsx-runtime.development.js * * Copyright (c) Meta Platforms, Inc. and affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. */ var hasRequiredReactJsxRuntime_development; function requireReactJsxRuntime_development() { if (hasRequiredReactJsxRuntime_development) return reactJsxRuntime_development; hasRequiredReactJsxRuntime_development = 1; "production" !== process.env.NODE_ENV && function() { function getComponentNameFromType(type) { if (null == type) return null; if ("function" === typeof type) return type.$$typeof === REACT_CLIENT_REFERENCE ? null : type.displayName || type.name || null; if ("string" === typeof type) return type; switch (type) { case REACT_FRAGMENT_TYPE: return "Fragment"; case REACT_PROFILER_TYPE: return "Profiler"; case REACT_STRICT_MODE_TYPE: return "StrictMode"; case REACT_SUSPENSE_TYPE: return "Suspense"; case REACT_SUSPENSE_LIST_TYPE: return "SuspenseList"; case REACT_ACTIVITY_TYPE: return "Activity"; } if ("object" === typeof type) switch ("number" === typeof type.tag && console.error( "Received an unexpected object in getComponentNameFromType(). This is likely a bug in React. Please file an issue." ), type.$$typeof) { case REACT_PORTAL_TYPE: return "Portal"; case REACT_CONTEXT_TYPE: return (type.displayName || "Context") + ".Provider"; case REACT_CONSUMER_TYPE: return (type._context.displayName || "Context") + ".Consumer"; case REACT_FORWARD_REF_TYPE: var innerType = type.render; type = type.displayName; type || (type = innerType.displayName || innerType.name || "", type = "" !== type ? "ForwardRef(" + type + ")" : "ForwardRef"); return type; case REACT_MEMO_TYPE: return innerType = type.displayName || null, null !== innerType ? innerType : getComponentNameFromType(type.type) || "Memo"; case REACT_LAZY_TYPE: innerType = type._payload; type = type._init; try { return getComponentNameFromType(type(innerType)); } catch (x) { } } return null; } function testStringCoercion(value) { return "" + value; } function checkKeyStringCoercion(value) { try { testStringCoercion(value); var JSCompiler_inline_result = false; } catch (e) { JSCompiler_inline_result = true; } if (JSCompiler_inline_result) { JSCompiler_inline_result = console; var JSCompiler_temp_const = JSCompiler_inline_result.error; var JSCompiler_inline_result$jscomp$0 = "function" === typeof Symbol && Symbol.toStringTag && value[Symbol.toStringTag] || value.constructor.name || "Object"; JSCompiler_temp_const.call( JSCompiler_inline_result, "The provided key is an unsupported type %s. This value must be coerced to a string before using it here.", JSCompiler_inline_result$jscomp$0 ); return testStringCoercion(value); } } function getTaskName(type) { if (type === REACT_FRAGMENT_TYPE) return "<>"; if ("object" === typeof type && null !== type && type.$$typeof === REACT_LAZY_TYPE) return "<...>"; try { var name = getComponentNameFromType(type); return name ? "<" + name + ">" : "<...>"; } catch (x) { return "<...>"; } } function getOwner() { var dispatcher = ReactSharedInternals.A; return null === dispatcher ? null : dispatcher.getOwner(); } function UnknownOwner() { return Error("react-stack-top-frame"); } function hasValidKey(config) { if (hasOwnProperty.call(config, "key")) { var getter = Object.getOwnPropertyDescriptor(config, "key").get; if (getter && getter.isReactWarning) return false; } return void 0 !== config.key; } function defineKeyPropWarningGetter(props, displayName) { function warnAboutAccessingKey() { specialPropKeyWarningShown || (specialPropKeyWarningShown = true, console.error( "%s: `key` is not a prop. Trying to access it will result in `undefined` being returned. If you need to access the same value within the child component, you should pass it as a different prop. (https://react.dev/link/special-props)", displayName )); } warnAboutAccessingKey.isReactWarning = true; Object.defineProperty(props, "key", { get: warnAboutAccessingKey, configurable: true }); } function elementRefGetterWithDeprecationWarning() { var componentName = getComponentNameFromType(this.type); didWarnAboutElementRef[componentName] || (didWarnAboutElementRef[componentName] = true, console.error( "Accessing element.ref was removed in React 19. ref is now a regular prop. It will be removed from the JSX Element type in a future release." )); componentName = this.props.ref; return void 0 !== componentName ? componentName : null; } function ReactElement(type, key, self, source, owner, props, debugStack, debugTask) { self = props.ref; type = { $$typeof: REACT_ELEMENT_TYPE, type, key, props, _owner: owner }; null !== (void 0 !== self ? self : null) ? Object.defineProperty(type, "ref", { enumerable: false, get: elementRefGetterWithDeprecationWarning }) : Object.defineProperty(type, "ref", { enumerable: false, value: null }); type._store = {}; Object.defineProperty(type._store, "validated", { configurable: false, enumerable: false, writable: true, value: 0 }); Object.defineProperty(type, "_debugInfo", { configurable: false, enumerable: false, writable: true, value: null }); Object.defineProperty(type, "_debugStack", { configurable: false, enumerable: false, writable: true, value: debugStack }); Object.defineProperty(type, "_debugTask", { configurable: false, enumerable: false, writable: true, value: debugTask }); Object.freeze && (Object.freeze(type.props), Object.freeze(type)); return type; } function jsxDEVImpl(type, config, maybeKey, isStaticChildren, source, self, debugStack, debugTask) { var children = config.children; if (void 0 !== children) if (isStaticChildren) if (isArrayImpl(children)) { for (isStaticChildren = 0; isStaticChildren < children.length; isStaticChildren++) validateChildKeys(children[isStaticChildren]); Object.freeze && Object.freeze(children); } else console.error( "React.jsx: Static children should always be an array. You are likely explicitly calling React.jsxs or React.jsxDEV. Use the Babel transform instead." ); else validateChildKeys(children); if (hasOwnProperty.call(config, "key")) { children = getComponentNameFromType(type); var keys = Object.keys(config).filter(function(k) { return "key" !== k; }); isStaticChildren = 0 < keys.length ? "{key: someKey, " + keys.join(": ..., ") + ": ...}" : "{key: someKey}"; didWarnAboutKeySpread[children + isStaticChildren] || (keys = 0 < keys.length ? "{" + keys.join(": ..., ") + ": ...}" : "{}", console.error( 'A props object containing a "key" prop is being spread into JSX:\n let props = %s;\n <%s {...props} />\nReact keys must be passed directly to JSX without using spread:\n let props = %s;\n <%s key={someKey} {...props} />', isStaticChildren, children, keys, children ), didWarnAboutKeySpread[children + isStaticChildren] = true); } children = null; void 0 !== maybeKey && (checkKeyStringCoercion(maybeKey), children = "" + maybeKey); hasValidKey(config) && (checkKeyStringCoercion(config.key), children = "" + config.key); if ("key" in config) { maybeKey = {}; for (var propName in config) "key" !== propName && (maybeKey[propName] = config[propName]); } else maybeKey = config; children && defineKeyPropWarningGetter( maybeKey, "function" === typeof type ? type.displayName || type.name || "Unknown" : type ); return ReactElement( type, children, self, source, getOwner(), maybeKey, debugStack, debugTask ); } function validateChildKeys(node) { "object" === typeof node && null !== node && node.$$typeof === REACT_ELEMENT_TYPE && node._store && (node._store.validated = 1); } var React = require$$0, REACT_ELEMENT_TYPE = Symbol.for("react.transitional.element"), REACT_PORTAL_TYPE = Symbol.for("react.portal"), REACT_FRAGMENT_TYPE = Symbol.for("react.fragment"), REACT_STRICT_MODE_TYPE = Symbol.for("react.strict_mode"), REACT_PROFILER_TYPE = Symbol.for("react.profiler"); var REACT_CONSUMER_TYPE = Symbol.for("react.consumer"), REACT_CONTEXT_TYPE = Symbol.for("react.context"), REACT_FORWARD_REF_TYPE = Symbol.for("react.forward_ref"), REACT_SUSPENSE_TYPE = Symbol.for("react.suspense"), REACT_SUSPENSE_LIST_TYPE = Symbol.for("react.suspense_list"), REACT_MEMO_TYPE = Symbol.for("react.memo"), REACT_LAZY_TYPE = Symbol.for("react.lazy"), REACT_ACTIVITY_TYPE = Symbol.for("react.activity"), REACT_CLIENT_REFERENCE = Symbol.for("react.client.reference"), ReactSharedInternals = React.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE, hasOwnProperty = Object.prototype.hasOwnProperty, isArrayImpl = Array.isArray, createTask = console.createTask ? console.createTask : function() { return null; }; React = { "react-stack-bottom-frame": function(callStackForError) { return callStackForError(); } }; var specialPropKeyWarningShown; var didWarnAboutElementRef = {}; var unknownOwnerDebugStack = React["react-stack-bottom-frame"].bind( React, UnknownOwner )(); var unknownOwnerDebugTask = createTask(getTaskName(UnknownOwner)); var didWarnAboutKeySpread = {}; reactJsxRuntime_development.Fragment = REACT_FRAGMENT_TYPE; reactJsxRuntime_development.jsx = function(type, config, maybeKey, source, self) { var trackActualOwner = 1e4 > ReactSharedInternals.recentlyCreatedOwnerStacks++; return jsxDEVImpl( type, config, maybeKey, false, source, self, trackActualOwner ? Error("react-stack-top-frame") : unknownOwnerDebugStack, trackActualOwner ? createTask(getTaskName(type)) : unknownOwnerDebugTask ); }; reactJsxRuntime_development.jsxs = function(type, config, maybeKey, source, self) { var trackActualOwner = 1e4 > ReactSharedInternals.recentlyCreatedOwnerStacks++; return jsxDEVImpl( type, config, maybeKey, true, source, self, trackActualOwner ? Error("react-stack-top-frame") : unknownOwnerDebugStack, trackActualOwner ? createTask(getTaskName(type)) : unknownOwnerDebugTask ); }; }(); return reactJsxRuntime_development; } if (process.env.NODE_ENV === "production") { jsxRuntime.exports = requireReactJsxRuntime_production(); } else { jsxRuntime.exports = requireReactJsxRuntime_development(); } var jsxRuntimeExports = jsxRuntime.exports; const MobileAccessibilityContext = createContext(null); const MobileAccessibilityProvider = ({ children, initialPreferences, onPreferencesChange, hapticEnabled = true, announcements = true }) => { const [state, setState] = useState({ screenReader: { enabled: false, type: null }, reduceMotion: false, reduceTransparency: false, boldText: false, largeText: false, highContrast: false, colorScheme: useColorScheme(), deviceOrientation: "portrait", screenSize: "medium", hapticFeedback: hapticEnabled, voiceControl: false, grayscale: false }); const [preferences, setPreferences] = useState(initialPreferences || null); const [isLoading, setIsLoading] = useState(true); useEffect(() => { const initializeAccessibilityState = async () => { try { const screenReaderEnabled = await AccessibilityInfo.isScreenReaderEnabled(); const screenReaderType = Platform.OS === "ios" ? "voiceover" : "talkback"; const reduceMotionEnabled = await AccessibilityInfo.isReduceMotionEnabled?.() || false; let reduceTransparency = false; let boldText = false; let grayscale = false; if (Platform.OS === "ios") { reduceTransparency = await AccessibilityInfo.isReduceTransparencyEnabled?.() || false; boldText = await AccessibilityInfo.isBoldTextEnabled?.() || false; grayscale = await AccessibilityInfo.isGrayscaleEnabled?.() || false; } const { width, height } = Dimensions.get("window"); const screenSize = classifyScreenSize(width, height); const deviceOrientation = width > height ? "landscape" : "portrait"; setState((prevState) => ({ ...prevState, screenReader: { enabled: screenReaderEnabled, type: screenReaderEnabled ? screenReaderType : null }, reduceMotion: reduceMotionEnabled, reduceTransparency, boldText, grayscale, deviceOrientation, screenSize })); setIsLoading(false); } catch (error) { console.warn("Error initializing accessibility state:", error); setIsLoading(false); } }; initializeAccessibilityState(); const screenReaderListener = AccessibilityInfo.addEventListener( "screenReaderChanged", (enabled) => { setState((prevState) => ({ ...prevState, screenReader: { enabled, type: enabled ? Platform.OS === "ios" ? "voiceover" : "talkback" : null } })); } ); const reduceMotionListener = AccessibilityInfo.addEventListener( "reduceMotionChanged", (enabled) => { setState((prevState) => ({ ...prevState, reduceMotion: enabled })); } ); const dimensionsListener = Dimensions.addEventListener("change", ({ window }) => { const newOrientation = window.width > window.height ? "landscape" : "portrait"; const newScreenSize = classifyScreenSize(window.width, window.height); setState((prevState) => ({ ...prevState, deviceOrientation: newOrientation, screenSize: newScreenSize })); }); return () => { screenReaderListener?.remove(); reduceMotionListener?.remove(); dimensionsListener?.remove(); }; }, []); const actions = { updatePreferences: (newPreferences) => { const updatedPreferences = { ...preferences, ...newPreferences, lastUpdated: /* @__PURE__ */ new Date() }; setPreferences(updatedPreferences); onPreferencesChange?.(updatedPreferences); }, announceForAccessibility: (message) => { if (announcements && state.screenReader.enabled) { AccessibilityInfo.announceForAccessibility(message); } }, setFocusToElement: (elementRef) => { if (elementRef && elementRef.current) { AccessibilityInfo.setAccessibilityFocus(elementRef.current); } }, triggerHapticFeedback: (type) => { if (!hapticEnabled || !state.hapticFeedback) return; import("react-native").then(({ Haptics }) => { if (Haptics) { switch (type) { case "light": Haptics.impactAsync?.(Haptics.ImpactFeedbackStyle?.Light); break; case "medium": Haptics.impactAsync?.(Haptics.ImpactFeedbackStyle?.Medium); break; case "heavy": Haptics.impactAsync?.(Haptics.ImpactFeedbackStyle?.Heavy); break; case "selection": Haptics.selectionAsync?.(); break; } } }).catch(() => { }); }, adjustFontSize: (scale) => { actions.updatePreferences({ sensory: { ...preferences?.sensory, fontSize: Math.max(12, Math.min(32, (preferences?.sensory?.fontSize || 16) * scale)) } }); }, toggleHighContrast: () => { actions.updatePreferences({ sensory: { ...preferences?.sensory, highContrast: !preferences?.sensory?.highContrast } }); setState((prevState) => ({ ...prevState, highContrast: !prevState.highContrast })); }, toggleReduceMotion: () => { actions.updatePreferences({ sensory: { ...preferences?.sensory, motionReduction: !preferences?.sensory?.motionReduction } }); } }; const contextValue = { state, actions, preferences, isLoading }; return /* @__PURE__ */ jsxRuntimeExports.jsx(MobileAccessibilityContext.Provider, { value: contextValue, children }); }; const useMobileAccessibility = () => { const context = useContext(MobileAccessibilityContext); if (!context) { throw new Error("useMobileAccessibility must be used within a MobileAccessibilityProvider"); } return context; }; const useScreenReader = () => { const { state, actions } = useMobileAccessibility(); return { isEnabled: state.screenReader.enabled, type: state.screenReader.type, announce: actions.announceForAccessibility, setFocus: actions.setFocusToElement }; }; const useHapticFeedback = () => { const { state, actions } = useMobileAccessibility(); return { isEnabled: state.hapticFeedback, trigger: actions.triggerHapticFeedback }; }; const useDeviceContext = () => { const { state } = useMobileAccessibility(); return { orientation: state.deviceOrientation, screenSize: state.screenSize, isLandscape: state.deviceOrientation === "landscape", isPortrait: state.deviceOrientation === "portrait", isSmallScreen: state.screenSize === "small", isMediumScreen: state.screenSize === "medium", isLargeScreen: state.screenSize === "large" || state.screenSize === "xlarge" }; }; const classifyScreenSize = (width, height) => { const maxDimension = Math.max(width, height); if (maxDimension < 568) return "small"; if (maxDimension < 667) return "medium"; if (maxDimension < 896) return "large"; return "xlarge"; }; export { MobileAccessibilityProvider as M, useScreenReader as a, useHapticFeedback as b, useDeviceContext as c, useMobileAccessibility as u };