// // Copyright 2022 DXOS.org // import { createKeyborg } from 'keyborg'; import React, { type PropsWithChildren, createContext, useEffect, useMemo } from 'react'; import { type Density, type Elevation, type ThemeFunction, type ThemeMode } from '@dxos/ui-types'; import { type SafeAreaPadding, useSafeArea } from '../../hooks'; import { hasIosKeyboard } from '../../util'; import { DensityProvider } from '../DensityProvider'; import { ElevationProvider } from '../ElevationProvider'; import { TranslationsProvider, type TranslationsProviderProps } from './TranslationsProvider'; export type ThemeContextValue = { tx: ThemeFunction; themeMode: ThemeMode; hasIosKeyboard: boolean; safeAreaPadding?: SafeAreaPadding; noCache?: boolean; platform?: 'mobile' | 'desktop'; }; /** * @internal */ export const ThemeContext = createContext(undefined); export type ThemeProviderProps = Omit & Partial> & PropsWithChildren<{ rootDensity?: Density; rootElevation?: Elevation; }>; export const ThemeProvider = ({ children, fallback = null, resourceExtensions, appNs, tx = (_path, _styleProps, ..._options) => undefined, themeMode = 'dark', rootDensity = 'md', noCache, platform, }: ThemeProviderProps) => { useEffect(() => { if (document.defaultView) { const kb = createKeyborg(document.defaultView); kb.subscribe(handleInputModalityChange); return () => kb.unsubscribe(handleInputModalityChange); } }, []); const safeAreaPadding = useSafeArea(); // Destructure all props explicitly so useMemo deps are stable primitives, not a new `rest` object every render. const contextValue = useMemo( () => ({ tx, themeMode, hasIosKeyboard: hasIosKeyboard(), safeAreaPadding, noCache, platform }), [tx, themeMode, safeAreaPadding, noCache, platform], ); return ( {children} ); }; const handleInputModalityChange = (isUsingKeyboard: boolean) => { if (isUsingKeyboard) { document.body.setAttribute('data-w-keyboard', 'true'); } else { document.body.removeAttribute('data-w-keyboard'); } };