diff --git a/src/lib/components/Carousel/index.tsx b/src/lib/components/Carousel/index.tsx index 1e59724b3..71a480e3f 100644 --- a/src/lib/components/Carousel/index.tsx +++ b/src/lib/components/Carousel/index.tsx @@ -15,6 +15,7 @@ import { import classNames from 'classnames'; import { HiOutlineChevronLeft, HiOutlineChevronRight } from 'react-icons/hi'; import ScrollContainer from 'react-indiana-drag-scroll'; +import windowExists from '../../helpers/window-exists'; export type CarouselProps = PropsWithChildren<{ slide?: boolean; @@ -35,7 +36,7 @@ export const Carousel: FC = ({ const [activeItem, setActiveItem] = useState(0); const [isDragging, setIsDragging] = useState(false); const carouselContainer = useRef(null); - const isDeviceMobile = typeof window.orientation !== 'undefined' || navigator.userAgent.indexOf('IEMobile') !== -1; + const isDeviceMobile = windowExists() && navigator.userAgent.indexOf('IEMobile') !== -1; const items = useMemo( () => diff --git a/src/lib/components/Flowbite/ThemeContext.tsx b/src/lib/components/Flowbite/ThemeContext.tsx index ecbd1ef12..26af76530 100644 --- a/src/lib/components/Flowbite/ThemeContext.tsx +++ b/src/lib/components/Flowbite/ThemeContext.tsx @@ -1,5 +1,6 @@ /* eslint-disable react-hooks/rules-of-hooks */ import { FC, ReactNode, createContext, useContext, useState, useEffect } from 'react'; +import windowExists from '../../helpers/window-exists'; import defaultTheme from '../../theme/default'; export type Mode = string | undefined | 'light' | 'dark'; @@ -36,16 +37,22 @@ export const useThemeMode = ( const savePreference = (m: string) => localStorage.setItem('theme', m); const toggleMode = () => { - if (!mode) return; + if (!mode) { + return; + } + + if (windowExists()) { + document.documentElement.classList.toggle('dark'); + } - document.documentElement.classList.toggle('dark'); savePreference(mode); setMode(mode == 'dark' ? 'light' : 'dark'); }; if (usePreferences) { useEffect(() => { - const userPreference = !!window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches; + const userPreference = + windowExists() && !!window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches; const userMode = localStorage.getItem('theme') || (userPreference ? 'dark' : 'light'); if (userMode) { @@ -54,10 +61,16 @@ export const useThemeMode = ( }, []); useEffect(() => { - if (!mode) return; + if (!mode) { + return; + } savePreference(mode); + if (!windowExists()) { + return; + } + if (mode != 'dark') { document.documentElement.classList.remove('dark'); } else { diff --git a/src/lib/components/Flowbite/index.tsx b/src/lib/components/Flowbite/index.tsx index a37c319ea..d4f71667e 100644 --- a/src/lib/components/Flowbite/index.tsx +++ b/src/lib/components/Flowbite/index.tsx @@ -2,6 +2,7 @@ import { FC, HTMLAttributes, useLayoutEffect, useMemo } from 'react'; import { ThemeContext, useThemeMode } from './ThemeContext'; import { mergeDeep } from '../../helpers/mergeDeep'; import defaultTheme from '../../theme/default'; +import windowExists from '../../helpers/window-exists'; export interface ThemeProps { config?: object; @@ -25,7 +26,10 @@ export const Flowbite: FC = ({ children, theme = {} }) => { if (setMode != null) { setMode('dark'); } - document.documentElement.classList.add('dark'); + + if (windowExists()) { + document.documentElement.classList.add('dark'); + } } }, [dark, setMode]); diff --git a/src/lib/components/Modal/index.tsx b/src/lib/components/Modal/index.tsx index 1deb24099..a5a1dfcd3 100644 --- a/src/lib/components/Modal/index.tsx +++ b/src/lib/components/Modal/index.tsx @@ -6,6 +6,7 @@ import { ModalHeader } from './ModalHeader'; import { ModalBody } from './ModalBody'; import { ModalFooter } from './ModalFooter'; import { ModalContext } from './ModalContext'; +import windowExists from '../../helpers/window-exists'; type Size = 'sm' | 'md' | 'lg' | 'xl' | '2xl' | '3xl' | '4xl' | '5xl' | '6xl' | '7xl'; type Placement = `${'top' | 'bottom'}-${'left' | 'center' | 'right'}` | `center${'' | '-left' | '-right'}`; @@ -46,17 +47,19 @@ const placementClasses: Record = { const ModalComponent: FC = ({ children, - root = document.body, + root = windowExists() ? document.body : undefined, show, popup, size = '2xl', placement = 'center', onClose, }): JSX.Element | null => { - const [container] = useState(document.createElement('div')); + const [container] = useState(windowExists() ? document.createElement('div') : undefined); useEffect(() => { - if (!show) return; + if (!container || !root || !show) { + return; + } root.appendChild(container); @@ -65,27 +68,29 @@ const ModalComponent: FC = ({ }; }, [container, root, show]); - return createPortal( - -
-
-
{children}
-
-
-
, - container, - ); + return container + ? createPortal( + +
+
+
{children}
+
+
+
, + container, + ) + : null; }; ModalComponent.displayName = 'Modal'; diff --git a/src/lib/helpers/window-exists.ts b/src/lib/helpers/window-exists.ts new file mode 100644 index 000000000..87f29f4ff --- /dev/null +++ b/src/lib/helpers/window-exists.ts @@ -0,0 +1 @@ +export default (): boolean => typeof window !== 'undefined';