diff --git a/src/components/DynamicBlur/DynamicBlur.module.scss b/src/components/DynamicBlur/DynamicBlur.module.scss new file mode 100644 index 000000000..fcf0f4e2f --- /dev/null +++ b/src/components/DynamicBlur/DynamicBlur.module.scss @@ -0,0 +1,13 @@ +@use '../../styles/variables'; +@use '../../styles/theme'; + +.BlurBackground { + position: fixed; + height: 100vh; + width: 100vw; + background-position: center; + background-size: cover; + box-sizing: border-box; + filter: blur(30px); + z-index: -1; +} diff --git a/src/components/DynamicBlur/DynamicBlur.test.tsx b/src/components/DynamicBlur/DynamicBlur.test.tsx new file mode 100644 index 000000000..2f2b6791a --- /dev/null +++ b/src/components/DynamicBlur/DynamicBlur.test.tsx @@ -0,0 +1,12 @@ +import React from 'react'; +import { render } from '@testing-library/react'; + +import DynamicBlur from './DynamicBlur'; + +describe('', () => { + test('renders and matches snapshot', () => { + const { container } = render(); + + expect(container).toMatchSnapshot(); + }); +}); diff --git a/src/components/DynamicBlur/DynamicBlur.tsx b/src/components/DynamicBlur/DynamicBlur.tsx new file mode 100644 index 000000000..064fc63db --- /dev/null +++ b/src/components/DynamicBlur/DynamicBlur.tsx @@ -0,0 +1,66 @@ +import React, { memo, useEffect, useRef, useState } from 'react'; + +import { debounce } from '../../utils/common'; + +import styles from './DynamicBlur.module.scss'; + +type ImgState = { + current: 'first' | 'second' | 'none'; + srcFirst: string; + srcSecond: string; +}; + +const defaultImgState: ImgState = { + current: 'none', + srcFirst: '', + srcSecond: '', +}; + +type Props = { + url: string; + transitionTime?: number; +}; + +const DynamicBlur: React.FC = ({ url, transitionTime = 1 }: Props) => { + const [imgState, setImgState] = useState(defaultImgState); + const loadImgRef = useRef(debounce((url: string, imgState: ImgState) => loadImage(url, imgState), 350)); + + const loadImage = (url: string, imgState: ImgState) => { + const img = document.createElement('img'); + img.onload = () => { + setImgState({ + current: imgState.current === 'first' ? 'second' : 'first', + srcFirst: imgState.current === 'first' ? imgState.srcFirst : url, + srcSecond: imgState.current === 'second' ? imgState.srcSecond : url, + }); + }; + if (url) img.src = url; + }; + + useEffect(() => { + if (url !== imgState.srcFirst && url !== imgState.srcSecond) loadImgRef.current(url, imgState); + }, [url, imgState]); + + return ( + +
+
+ + ); +}; + +export default memo(DynamicBlur); diff --git a/src/components/DynamicBlur/__snapshots__/DynamicBlur.test.tsx.snap b/src/components/DynamicBlur/__snapshots__/DynamicBlur.test.tsx.snap new file mode 100644 index 000000000..772e8cc4d --- /dev/null +++ b/src/components/DynamicBlur/__snapshots__/DynamicBlur.test.tsx.snap @@ -0,0 +1,14 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[` renders and matches snapshot 1`] = ` +
+
+
+
+`; diff --git a/src/components/Layout/Layout.tsx b/src/components/Layout/Layout.tsx index d4265ef61..d1c3b9ed3 100644 --- a/src/components/Layout/Layout.tsx +++ b/src/components/Layout/Layout.tsx @@ -1,19 +1,26 @@ -import React, { ReactNode, FC, useState } from 'react'; +import React, { ReactNode, FC, useState, useContext } from 'react'; import Header from '../Header/Header'; import SideBar from '../SideBar/SideBar'; - +import DynamicBlur from '../DynamicBlur/DynamicBlur'; import styles from './Layout.module.scss'; +import type { Config } from 'types/Config'; +import { ConfigContext } from '../../providers/configProvider'; +import { UIStateContext } from '../../providers/uiStateProvider'; type LayoutProps = { children?: ReactNode; }; const Layout: FC = ({ children }) => { + const config: Config = useContext(ConfigContext); + const { blurImage } = useContext(UIStateContext); const [sideBarOpen, setSideBarOpen] = useState(false); + const hasDynamicBlur = config?.options.dynamicBlur === true; return (
+ {hasDynamicBlur && blurImage && }
setSideBarOpen(true)} /> setSideBarOpen(false)} /> {children}