You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
88 lines
2.8 KiB
88 lines
2.8 KiB
5 days ago
|
// This is a Next.js 15 compatible version of the GluestackUIProvider
|
||
|
'use client';
|
||
|
import React, { useEffect, useLayoutEffect } from 'react';
|
||
|
import { config } from './config';
|
||
|
import { OverlayProvider } from '@gluestack-ui/core/overlay/creator';
|
||
|
import { ToastProvider } from '@gluestack-ui/core/toast/creator';
|
||
|
import { setFlushStyles } from '@gluestack-ui/utils/nativewind-utils';
|
||
|
import { script } from './script';
|
||
|
|
||
|
const variableStyleTagId = 'nativewind-style';
|
||
|
const createStyle = (styleTagId: string) => {
|
||
|
const style = document.createElement('style');
|
||
|
style.id = styleTagId;
|
||
|
style.appendChild(document.createTextNode(''));
|
||
|
return style;
|
||
|
};
|
||
|
|
||
|
export const useSafeLayoutEffect =
|
||
|
typeof window !== 'undefined' ? useLayoutEffect : useEffect;
|
||
|
|
||
|
export function GluestackUIProvider({
|
||
|
mode = 'light',
|
||
|
...props
|
||
|
}: {
|
||
|
mode?: 'light' | 'dark' | 'system';
|
||
|
children?: React.ReactNode;
|
||
|
}) {
|
||
|
let cssVariablesWithMode = ``;
|
||
|
Object.keys(config).forEach((configKey) => {
|
||
|
cssVariablesWithMode +=
|
||
|
configKey === 'dark' ? `\n .dark {\n ` : `\n:root {\n`;
|
||
|
const cssVariables = Object.keys(
|
||
|
config[configKey as keyof typeof config]
|
||
|
).reduce((acc: string, curr: string) => {
|
||
|
acc += `${curr}:${config[configKey as keyof typeof config][curr]}; `;
|
||
|
return acc;
|
||
|
}, '');
|
||
|
cssVariablesWithMode += `${cssVariables} \n}`;
|
||
|
});
|
||
|
|
||
|
setFlushStyles(cssVariablesWithMode);
|
||
|
|
||
|
const handleMediaQuery = React.useCallback((e: MediaQueryListEvent) => {
|
||
|
script(e.matches ? 'dark' : 'light');
|
||
|
}, []);
|
||
|
|
||
|
useSafeLayoutEffect(() => {
|
||
|
if (mode !== 'system') {
|
||
|
const documentElement = document.documentElement;
|
||
|
if (documentElement) {
|
||
|
documentElement.classList.add(mode);
|
||
|
documentElement.classList.remove(mode === 'light' ? 'dark' : 'light');
|
||
|
documentElement.style.colorScheme = mode;
|
||
|
}
|
||
|
}
|
||
|
}, [mode]);
|
||
|
|
||
|
useSafeLayoutEffect(() => {
|
||
|
if (mode !== 'system') return;
|
||
|
const media = window.matchMedia('(prefers-color-scheme: dark)');
|
||
|
|
||
|
media.addListener(handleMediaQuery);
|
||
|
|
||
|
return () => media.removeListener(handleMediaQuery);
|
||
|
}, [handleMediaQuery]);
|
||
|
|
||
|
useSafeLayoutEffect(() => {
|
||
|
if (typeof window !== 'undefined') {
|
||
|
const documentElement = document.documentElement;
|
||
|
if (documentElement) {
|
||
|
const head = documentElement.querySelector('head');
|
||
|
let style = head?.querySelector(`[id='${variableStyleTagId}']`);
|
||
|
if (!style) {
|
||
|
style = createStyle(variableStyleTagId);
|
||
|
style.innerHTML = cssVariablesWithMode;
|
||
|
if (head) head.appendChild(style);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}, []);
|
||
|
|
||
|
return (
|
||
|
<OverlayProvider>
|
||
|
<ToastProvider>{props.children}</ToastProvider>
|
||
|
</OverlayProvider>
|
||
|
);
|
||
|
}
|