pl-fe: use adoptedStyleSheets and break a bunch of other stuff
Signed-off-by: nicole mikołajczyk <git@mkljczk.pl>
This commit is contained in:
46
packages/pl-fe/src/components/inline-style.tsx
Normal file
46
packages/pl-fe/src/components/inline-style.tsx
Normal file
@ -0,0 +1,46 @@
|
||||
import React, { useEffect, useRef } from 'react';
|
||||
|
||||
interface IInlineStyle {
|
||||
children: string;
|
||||
}
|
||||
|
||||
const InlineStyle: React.FC<IInlineStyle> = ({ children }) => {
|
||||
// eslint-disable-next-line compat/compat
|
||||
const sheet = useRef(document.adoptedStyleSheets ? new CSSStyleSheet() : document.createElement('style'));
|
||||
|
||||
useEffect(() => {
|
||||
if (sheet.current) {
|
||||
const stylesheet = sheet.current;
|
||||
if (stylesheet instanceof CSSStyleSheet) {
|
||||
stylesheet.replaceSync(children);
|
||||
} else {
|
||||
stylesheet.textContent = children;
|
||||
}
|
||||
}
|
||||
}, [children]);
|
||||
|
||||
useEffect(() => {
|
||||
const stylesheet = sheet.current;
|
||||
if (stylesheet) {
|
||||
if (stylesheet instanceof CSSStyleSheet) {
|
||||
document.adoptedStyleSheets = [...document.adoptedStyleSheets, stylesheet];
|
||||
} else {
|
||||
document.head.appendChild(stylesheet);
|
||||
}
|
||||
}
|
||||
|
||||
return () => {
|
||||
if (stylesheet) {
|
||||
if (stylesheet instanceof CSSStyleSheet) {
|
||||
document.adoptedStyleSheets = document.adoptedStyleSheets.filter(s => s !== stylesheet);
|
||||
} else {
|
||||
document.head.removeChild(stylesheet);
|
||||
}
|
||||
}
|
||||
};
|
||||
}, []);
|
||||
|
||||
return <></>;
|
||||
};
|
||||
|
||||
export { InlineStyle as default };
|
||||
@ -2,10 +2,10 @@ import clsx from 'clsx';
|
||||
import React from 'react';
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
|
||||
import InlineStyle from 'pl-fe/components/inline-style';
|
||||
import BackgroundShapes from 'pl-fe/features/ui/components/background-shapes';
|
||||
import { useSystemTheme } from 'pl-fe/hooks/use-system-theme';
|
||||
import { useThemeCss } from 'pl-fe/hooks/use-theme-css';
|
||||
import { useSettingsStore } from 'pl-fe/stores/settings';
|
||||
|
||||
import type { PlFeConfig } from 'pl-fe/normalizers/pl-fe/pl-fe-config';
|
||||
|
||||
@ -16,9 +16,7 @@ interface ISitePreview {
|
||||
|
||||
/** Renders a preview of the website's style with the configuration applied. */
|
||||
const SitePreview: React.FC<ISitePreview> = ({ plFe }) => {
|
||||
const { defaultSettings } = useSettingsStore();
|
||||
|
||||
const userTheme = defaultSettings.themeMode;
|
||||
const userTheme = plFe.defaultSettings.themeMode;
|
||||
const systemTheme = useSystemTheme();
|
||||
|
||||
const dark = ['dark', 'black'].includes(userTheme as string) || (userTheme === 'system' && systemTheme === 'black');
|
||||
@ -32,13 +30,14 @@ const SitePreview: React.FC<ISitePreview> = ({ plFe }) => {
|
||||
'h-40 overflow-hidden rounded-lg',
|
||||
{
|
||||
'bg-white': !dark,
|
||||
'bg-gray-900': dark,
|
||||
'bg-gray-900': dark && userTheme !== 'black',
|
||||
'bg-black': userTheme === 'black',
|
||||
});
|
||||
|
||||
return (
|
||||
<div className={bodyClass}>
|
||||
<style>{`.site-preview {${themeCss}}`}</style>
|
||||
<BackgroundShapes position='absolute' />
|
||||
<InlineStyle>{`.site-preview {${themeCss}}`}</InlineStyle>
|
||||
<BackgroundShapes position='absolute' hidden={userTheme === 'black'} />
|
||||
|
||||
<div className='absolute z-[2] self-center overflow-hidden rounded-lg bg-accent-500 p-2 text-white'>
|
||||
<FormattedMessage id='site_preview.preview' defaultMessage='Preview' />
|
||||
|
||||
@ -4,11 +4,18 @@ import React from 'react';
|
||||
interface IBackgroundShapes {
|
||||
/** Whether the shapes should be absolute positioned or fixed. */
|
||||
position?: 'fixed' | 'absolute';
|
||||
/** Override visibility. */
|
||||
hidden?: boolean;
|
||||
}
|
||||
|
||||
/** Gradient that appears in the background of the UI. */
|
||||
const BackgroundShapes: React.FC<IBackgroundShapes> = ({ position = 'fixed' }) => (
|
||||
<div className={clsx(position, 'pointer-events-none inset-x-0 top-0 flex justify-center overflow-hidden black:hidden')}>
|
||||
const BackgroundShapes: React.FC<IBackgroundShapes> = ({ position = 'fixed', hidden }) => (
|
||||
<div
|
||||
className={clsx(position, 'pointer-events-none inset-x-0 top-0 flex justify-center overflow-hidden ', {
|
||||
'hidden': hidden,
|
||||
'black:hidden': hidden === undefined,
|
||||
})}
|
||||
>
|
||||
<div className='bg-gradient-sm lg:bg-gradient-light lg:dark:bg-gradient-dark h-screen w-screen' />
|
||||
</div>
|
||||
);
|
||||
|
||||
@ -80,7 +80,7 @@ const useThemeCss = (overwriteConfig?: PlFeConfig) => {
|
||||
} catch (_) {
|
||||
return generateThemeCss({});
|
||||
}
|
||||
}, [demo, plFeConfig, theme]);
|
||||
}, [overwriteConfig, demo, plFeConfig, theme]);
|
||||
};
|
||||
|
||||
export { normalizeColors, useThemeCss };
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
import clsx from 'clsx';
|
||||
import React, { useEffect } from 'react';
|
||||
|
||||
import InlineStyle from 'pl-fe/components/inline-style';
|
||||
import { useLocale, useLocaleDirection } from 'pl-fe/hooks/use-locale';
|
||||
import { usePlFeConfig } from 'pl-fe/hooks/use-pl-fe-config';
|
||||
import { useSettings } from 'pl-fe/hooks/use-settings';
|
||||
@ -39,19 +40,23 @@ const PlFeHead = () => {
|
||||
}, [dsn]);
|
||||
|
||||
return (
|
||||
<Helmet>
|
||||
<html
|
||||
lang={locale}
|
||||
className={clsx('h-full', `text-${themeSettings?.interfaceSize || 'md'}`, {
|
||||
'dark': theme === 'dark',
|
||||
'dark black': theme === 'black',
|
||||
})}
|
||||
// @ts-ignore
|
||||
style={themeCss + (['dark', 'black'].includes(theme) ? 'color-scheme: dark;' : '')}
|
||||
/>
|
||||
<body className={bodyClass} dir={direction} />
|
||||
<meta name='theme-color' content={plFeConfig.brandColor} />
|
||||
</Helmet>
|
||||
<>
|
||||
<Helmet>
|
||||
<html
|
||||
lang={locale}
|
||||
className={clsx('h-full', `text-${themeSettings?.interfaceSize || 'md'}`, {
|
||||
'dark': theme === 'dark',
|
||||
'dark black': theme === 'black',
|
||||
})}
|
||||
/>
|
||||
<body className={bodyClass} dir={direction} />
|
||||
<meta name='theme-color' content={plFeConfig.brandColor} />
|
||||
</Helmet>
|
||||
<InlineStyle>{`:root { ${themeCss} }`}</InlineStyle>
|
||||
{['dark', 'black'].includes(theme) && (
|
||||
<InlineStyle>{':root { color-scheme: dark; }'}</InlineStyle>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@ -22,6 +22,7 @@ import Toggle from 'pl-fe/components/ui/toggle';
|
||||
import CryptoAddressInput from 'pl-fe/features/pl-fe-config/components/crypto-address-input';
|
||||
import FooterLinkInput from 'pl-fe/features/pl-fe-config/components/footer-link-input';
|
||||
import PromoPanelInput from 'pl-fe/features/pl-fe-config/components/promo-panel-input';
|
||||
import SitePreview from 'pl-fe/features/pl-fe-config/components/site-preview';
|
||||
import ThemeSelector from 'pl-fe/features/ui/components/theme-selector';
|
||||
import { useAppDispatch } from 'pl-fe/hooks/use-app-dispatch';
|
||||
import { useAppSelector } from 'pl-fe/hooks/use-app-selector';
|
||||
@ -179,7 +180,7 @@ const PlFeConfigEditor: React.FC = () => {
|
||||
<Column label={intl.formatMessage(messages.heading)}>
|
||||
<Form onSubmit={handleSubmit}>
|
||||
<fieldset className='space-y-6' disabled={isLoading}>
|
||||
{/* <SitePreview plFe={plFe} /> */}
|
||||
<SitePreview plFe={plFe} />
|
||||
|
||||
<CardHeader>
|
||||
<CardTitle title={<FormattedMessage id='plfe_config.headings.theme' defaultMessage='Theme' />} />
|
||||
|
||||
@ -55,6 +55,7 @@ const ThemeEditorPage: React.FC<IThemeEditor> = () => {
|
||||
const rawConfig = useAppSelector(state => state.plfe);
|
||||
|
||||
const [colors, setColors] = useState(normalizeColors(plFeConfig));
|
||||
const [isDefault, setIsDefault] = useState(false);
|
||||
const [submitting, setSubmitting] = useState(false);
|
||||
const [resetKey, setResetKey] = useState(crypto.randomUUID());
|
||||
|
||||
@ -63,6 +64,7 @@ const ThemeEditorPage: React.FC<IThemeEditor> = () => {
|
||||
const updateColors = (key: string) => (newColors: ColorGroup) => {
|
||||
if (typeof colors[key] === 'string') return;
|
||||
|
||||
setIsDefault(false);
|
||||
setColors({
|
||||
...colors,
|
||||
[key]: {
|
||||
@ -73,6 +75,7 @@ const ThemeEditorPage: React.FC<IThemeEditor> = () => {
|
||||
};
|
||||
|
||||
const updateColor = (key: string) => (hex: string) => {
|
||||
setIsDefault(false);
|
||||
setColors({
|
||||
...colors,
|
||||
[key]: hex,
|
||||
@ -81,6 +84,7 @@ const ThemeEditorPage: React.FC<IThemeEditor> = () => {
|
||||
|
||||
const setTheme = (theme: any) => {
|
||||
setResetKey(crypto.randomUUID());
|
||||
setIsDefault(false);
|
||||
setTimeout(() => setColors(theme));
|
||||
};
|
||||
|
||||
@ -89,13 +93,19 @@ const ThemeEditorPage: React.FC<IThemeEditor> = () => {
|
||||
};
|
||||
|
||||
const updateTheme = async () => {
|
||||
const params = { ...rawConfig, colors };
|
||||
let params;
|
||||
if (isDefault) {
|
||||
params = { ...rawConfig, colors: undefined, brandColor: undefined, accentColor: undefined };
|
||||
} else {
|
||||
params = { ...rawConfig, colors };
|
||||
}
|
||||
await dispatch(updatePlFeConfig(params));
|
||||
};
|
||||
|
||||
const restoreDefaultTheme = () => {
|
||||
const config = v.parse(plFeConfigSchema, { brandColor: '#d80482' });
|
||||
setTheme(normalizeColors(config));
|
||||
setIsDefault(true);
|
||||
};
|
||||
|
||||
const exportTheme = () => {
|
||||
|
||||
Reference in New Issue
Block a user