@equinor/fusion-react-styles
Version:
style lib inspired by @material-ui/styles
118 lines (117 loc) • 3.84 kB
JavaScript
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
import { useContext, useMemo } from 'react';
import { styles as defaultTheme } from '@equinor/fusion-web-theme';
import { ThemeContext } from './utils/contexts';
import { EdsTokens } from './EdsTokens';
import '@equinor/fusion-wc-theme';
/**
* Provides theme values to child components
*
* This component wraps children with a theme context and the fwc-theme web component.
* When nested, the theme can be a function that receives the outer theme and returns
* a merged or customized theme.
*
* Supports extending FusionTheme with custom properties for application-specific themes.
*
* @template T - Extended theme type that extends FusionTheme
* @param props - Theme provider configuration
* @returns A React element that provides theme context to children
*
* @example
* ```tsx
* <ThemeProvider theme={myTheme}>
* <App />
* </ThemeProvider>
* ```
*
* @example
* ```tsx
* // Nested theme with function
* <ThemeProvider theme={baseTheme}>
* <ThemeProvider theme={(outer) => ({ ...outer, custom: true })}>
* <App />
* </ThemeProvider>
* </ThemeProvider>
* ```
*
* @example
* ```tsx
* // Extended theme with custom properties
* interface MyAppTheme extends FusionTheme {
* customProperty: string;
* }
*
* const extendedTheme: MyAppTheme = {
* ...theme,
* customProperty: 'value'
* };
*
* <ThemeProvider<MyAppTheme> theme={extendedTheme}>
* <App />
* </ThemeProvider>
* ```
*/
export function ThemeProvider(props) {
const { children, theme: localTheme } = props;
// Get theme from parent ThemeProvider (if nested)
const outerTheme = useContext(ThemeContext);
// Resolve theme: if function, call with outer theme; otherwise use directly or default
// Memoization prevents unnecessary re-renders when dependencies haven't changed
const theme = useMemo(() => {
if (typeof localTheme === 'function') {
// Theme function receives outer theme and returns new theme (enables theme composition)
// This allows nested themes to extend or override parent themes
return localTheme(outerTheme);
}
// Use provided theme as-is, or fall back to default theme
// Partial themes will be merged at the type level, but runtime uses provided theme directly
// Type casting is safe because TypeScript ensures type compatibility
return (localTheme ?? defaultTheme);
}, [localTheme, outerTheme]);
return (_jsxs(ThemeContext.Provider, { value: theme, children: [outerTheme === null && _jsx(EdsTokens, {}), _jsx("fwc-theme", { children: children })] }));
}
ThemeProvider.displayName = 'ThemeProvider';
/**
* Hook to access the current theme from ThemeProvider context
*
* Supports extended themes that extend FusionTheme. When used with an extended theme,
* the generic type parameter should match the theme type used in ThemeProvider.
*
* @template Theme - The type of the theme (defaults to FusionTheme, but can be extended)
* @returns The current theme value or null if no ThemeProvider is present
*
* @example
* ```tsx
* function Component() {
* const theme = useTheme();
* if (!theme) {
* return <div>No theme available</div>;
* }
* return (
* <div style={{
* color: theme.colors.text.static_icons__default.getVariable('color')
* }}>
* Hello
* </div>
* );
* }
* ```
*
* @example
* ```tsx
* // With extended theme type
* interface MyAppTheme extends FusionTheme {
* customProperty: string;
* }
*
* function Component() {
* const theme = useTheme<MyAppTheme>();
* return <div>{theme?.customProperty}</div>;
* }
* ```
*/
export function useTheme() {
const theme = useContext(ThemeContext);
return theme;
}
export default ThemeProvider;