Skip to content

Commit

Permalink
Add load spinner with suspense
Browse files Browse the repository at this point in the history
  • Loading branch information
davidjerleke committed Jul 1, 2024
1 parent b62cc17 commit 55d2034
Show file tree
Hide file tree
Showing 18 changed files with 105 additions and 105 deletions.
9 changes: 3 additions & 6 deletions packages/embla-carousel-docs/src/components/Header/Header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,10 @@ import { SiteNavigationToggle } from 'components/SiteNavigation/SiteNavigationTo
import { MEDIA } from 'consts/breakpoints'
import { LAYERS } from 'consts/layers'
import { COLORS } from 'consts/themes'
import { SPACINGS } from 'consts/spacings'
import { HEADER_HEIGHT, HEADER_ID } from 'consts/header'
import { BORDER_SIZES } from 'consts/border'
import { HeaderActions } from './HeaderActions'
import { HeaderLogo } from './HeaderLogo'

export const HEADER_HEIGHT = SPACINGS.TEN
export const HEADER_ID = 'site-header'
import { HeaderActions } from 'components/Header/HeaderActions'
import { HeaderLogo } from 'components/Header/HeaderLogo'

const HEIGHT = css`
height: ${HEADER_HEIGHT};
Expand Down
2 changes: 0 additions & 2 deletions packages/embla-carousel-docs/src/components/Layout/Layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import { RoutesInit } from 'components/Routes/RoutesInit'
import { ThemeInit } from 'components/Theme/ThemeInit'
import { KeyEventsInit } from 'components/KeyEvents/KeyEventsInit'
import { TableOfContentsInit } from 'components/TableOfContents/TableOfContentsInit'
import { ModalLoading } from 'components/Modal/ModalLoading'

type PropType = PropsWithChildren<PagePropType>

Expand Down Expand Up @@ -39,7 +38,6 @@ export const Layout = (props: PropType) => {
<KeyEventsSkipToContent />
<Header />
<RoutesLoading pageId={id} />
<ModalLoading />
<PageGrid layout={layout}>{children}</PageGrid>
<Footer />
</>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,26 +1,25 @@
import React, { useCallback, useEffect, useRef, useState } from 'react'
import { COLORS } from 'consts/themes'
import styled from 'styled-components'
import { useAppSelector } from 'hooks/useRedux'
import { selectModalLoading } from './modalReducer'
import { LoadSpinner } from 'components/LoadSpinner/LoadSpinner'
import { createSquareSizeStyles } from 'utils/createSquareSizeStyles'
import { HEADER_HEIGHT } from 'components/Header/Header'
import { LAYERS } from 'consts/layers'
import { BORDER_RADIUSES } from 'consts/border'
import { BORDER_RADIUSES, BORDER_SIZES } from 'consts/border'
import { useEventListener } from 'hooks/useEventListener'
import { PAGE_FRAME_SPACING } from 'components/Page/PageFrame'
import { HEADER_HEIGHT } from 'consts/header'
import { ModalPortal } from 'components/Modal/ModalPortal'

const getOpacity = (isModalLoading: boolean, showLoader: boolean): number => {
if (!isModalLoading) return 0
const getOpacity = (isVisible: boolean, showLoader: boolean): number => {
if (!isVisible) return 0
if (!showLoader) return 0
return 1
}

const WRAPPER_SIZE = '6rem'
const LOADER_SIZE = '4rem'

const ModalLoadingWrapper = styled.div<{ $opacity: number }>`
const LoadSpinnerSuspenseWrapper = styled.div<{ $opacity: number }>`
background-color: rgba(${COLORS.BACKGROUND_SITE_RGB_VALUE}, 0.9);
border-radius: ${BORDER_RADIUSES.CIRCLE};
z-index: ${LAYERS.MODAL_LOADING};
Expand All @@ -34,10 +33,16 @@ const ModalLoadingWrapper = styled.div<{ $opacity: number }>`
justify-content: center;
opacity: ${({ $opacity }) => $opacity};
transition: ${({ $opacity }) => `opacity ${$opacity === 1 ? 0 : 0.6}s`};
box-shadow: 0 0 0 ${BORDER_SIZES.DETAIL} ${COLORS.DETAIL_LOW_CONTRAST};
`
// pointer-events: none;

export const ModalLoading = () => {
const isModalLoading = useAppSelector(selectModalLoading)
type PropType = {
isVisible: boolean
}

export const LoadSpinnerSuspense = (props: PropType) => {
const { isVisible } = props
const [showLoader, setShowLoader] = useState(false)
const [opacity, setOpacity] = useState(0)
const loaderRef = useRef<HTMLDivElement>(null)
Expand All @@ -47,21 +52,23 @@ export const ModalLoading = () => {
}, [opacity])

useEffect(() => {
const newOpacity = getOpacity(isModalLoading, showLoader)
const newOpacity = getOpacity(isVisible, showLoader)
setOpacity(newOpacity)
}, [isModalLoading, showLoader])
}, [isVisible, showLoader])

useEffect(() => {
if (isModalLoading) setShowLoader(true)
}, [isModalLoading])
if (isVisible) setShowLoader(true)
}, [isVisible])

useEventListener('transitionend', onLoaderTransitionEnd, loaderRef)

if (!isModalLoading && !showLoader) return null
if (!isVisible && !showLoader) return null

return (
<ModalLoadingWrapper $opacity={opacity} ref={loaderRef}>
<LoadSpinner size={LOADER_SIZE} color={COLORS.TEXT_BODY} />
</ModalLoadingWrapper>
<ModalPortal>
<LoadSpinnerSuspenseWrapper $opacity={opacity} ref={loaderRef}>
<LoadSpinner size={LOADER_SIZE} color={COLORS.TEXT_BODY} />
</LoadSpinnerSuspenseWrapper>
</ModalPortal>
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { useEffect } from 'react'

type PropType = {
setIsLoading: React.Dispatch<React.SetStateAction<boolean>>
}

export const LoadSpinnerSuspenseTrigger = (props: PropType) => {
const { setIsLoading } = props

useEffect(() => {
setIsLoading(true)

return () => {
setIsLoading(false)
}
}, [setIsLoading])

return null
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import React, { Suspense, useState } from 'react'
import { LoadSpinnerSuspense } from 'components/LoadSpinner/LoadSpinnerSuspense'
import { LoadSpinnerSuspenseTrigger } from 'components/LoadSpinner/LoadSpinnerSuspenseTrigger'

type PropType = {
children: React.ReactNode
fallback?: React.ReactNode
}

export const LoadSpinnerWithSuspense = (props: PropType) => {
const [isLoading, setIsLoading] = useState(false)
const { children, fallback } = props

return (
<>
<LoadSpinnerSuspense isVisible={isLoading} />
<Suspense
fallback={
<>
<LoadSpinnerSuspenseTrigger setIsLoading={setIsLoading} />
{fallback && fallback}
</>
}
>
{children}
</Suspense>
</>
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { COLORS } from 'consts/themes'
import { FONT_SIZES, FONT_WEIGHTS } from 'consts/fontSizes'
import { MEDIA } from 'consts/breakpoints'
import { SPACINGS } from 'consts/spacings'
import { HEADER_HEIGHT } from 'components/Header/Header'
import { HEADER_HEIGHT } from 'consts/header'

export const HEADING_TOP_SPACING = SPACINGS.EIGHT
const ANCHOR_SVG_SIZE = SPACINGS.CUSTOM(({ THREE }) => THREE - 0.2)
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -3,29 +3,17 @@ import { ModalsType } from 'consts/modal'
import { AppStateType } from 'consts/redux'

export type ModalsStateType = {
loadingModals: ModalsType[]
openModal: ModalsType | null
}

const initialState: ModalsStateType = {
loadingModals: [],
openModal: null
}

const modalsSlice = createSlice({
name: 'modal',
initialState,
reducers: {
setModalIsLoading: (state, action: PayloadAction<ModalsType>): void => {
const modal = action.payload
if (state.loadingModals.includes(modal)) return
state.loadingModals = state.loadingModals.concat(action.payload)
},
removeModalIsLoading: (state, action: PayloadAction<ModalsType>): void => {
const modal = action.payload
if (!state.loadingModals.includes(modal)) return
state.loadingModals = state.loadingModals.filter((key) => key !== modal)
},
setModalOpen: (state, action: PayloadAction<ModalsType>): void => {
const modal = action.payload
state.openModal = modal
Expand All @@ -41,15 +29,7 @@ const modalsSlice = createSlice({
const { name, reducer } = modalsSlice
export { name as modalName, reducer as modalReducer }

export const {
setModalIsLoading,
removeModalIsLoading,
setModalOpen,
setModalClosed
} = modalsSlice.actions

export const selectModalLoading = (state: AppStateType): boolean =>
state.modal.loadingModals.length > 0
export const { setModalOpen, setModalClosed } = modalsSlice.actions

export const selectIsModalOpen =
(modal: ModalsType) =>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@ import React, { useEffect, useRef, useState } from 'react'
import styled from 'styled-components'
import { useAppDispatch, useAppSelector } from 'hooks/useRedux'
import { BRAND_GRADIENT_BACKGROUND_STYLES } from 'consts/gradients'
import { HEADER_HEIGHT } from 'components/Header/Header'
import { LAYERS } from 'consts/layers'
import { MEDIA } from 'consts/breakpoints'
import { useCallback } from 'react'
import { useEventListener } from 'hooks/useEventListener'
import { ROUTES_LOADING_BAR_HEIGHT } from 'consts/routes'
import { HEADER_HEIGHT } from 'consts/header'
import { MODALS } from 'consts/modal'
import {
selectIsModalOpen,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import React, { Suspense, lazy, useCallback, useRef } from 'react'
import React, { lazy, useCallback, useRef } from 'react'
import styled from 'styled-components'
import { SandboxSelectionType } from 'consts/sandbox'
import { LoadSpinnerWithSuspense } from 'components/LoadSpinner/LoadSpinnerWithSuspense'
import { ButtonBare } from 'components/Button/ButtonBare'
import { BRAND_GRADIENT_TEXT_STYLES } from 'consts/gradients'
import { IconWithText, IconWithTextText } from 'components/Icon/IconWithText'
import { COLORS } from 'consts/themes'
import { SPACINGS } from 'consts/spacings'
import { BORDER_RADIUSES } from 'consts/border'
import { FONT_SIZES, FONT_WEIGHTS } from 'consts/fontSizes'
import { ModalLoadingTrigger } from 'components/Modal/ModalLoadingTrigger'
import { useEventListener } from 'hooks/useEventListener'
import { useAppDispatch, useAppSelector } from 'hooks/useRedux'
import { MODALS } from 'consts/modal'
Expand Down Expand Up @@ -93,19 +93,20 @@ export const SandboxSelection = (props: PropType) => {
aria-expanded={isOpen}
aria-label="Show Select CodeSandbox Dialog"
onClick={openModal}
type="button"
>
<IconWithText iconSvg="pen" iconSize="1.4rem">
Edit Code
</IconWithText>
</SandboxSelectionOpenModalButton>

{isOpen && (
<Suspense fallback={<ModalLoadingTrigger modal={modalId.current} />}>
<LoadSpinnerWithSuspense>
<SandboxSelectionModalLazy
sandboxes={sandboxes}
closeModal={closeModal}
/>
</Suspense>
</LoadSpinnerWithSuspense>
)}
</SandboxSelectionWrapper>
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ import { BORDER_RADIUSES } from 'consts/border'
export const SELECT_CODESANDBOX_DIALOG_ID = 'select-codesandbox-dialog'
const MODAL_MAX_WIDTH = '36rem'
const DESKTOP_END_SPACING = SPACINGS.TEN

const BUTTON_SIZE = '4rem'
const ICON_SIZE = '1.8rem'

Expand Down
19 changes: 8 additions & 11 deletions packages/embla-carousel-docs/src/components/Search/Search.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React, { lazy, Suspense, useCallback, useEffect, useRef } from 'react'
import React, { lazy, useCallback, useEffect, useRef } from 'react'
import { SearchAlgoliaToggle } from 'components/Search/SearchAlgoliaToggle'
import { ModalLoadingTrigger } from 'components/Modal/ModalLoadingTrigger'
import { useAppDispatch, useAppSelector } from 'hooks/useRedux'
import { LoadSpinnerWithSuspense } from 'components/LoadSpinner/LoadSpinnerWithSuspense'
import { MODALS } from 'consts/modal'
import {
selectIsModalOpen,
Expand Down Expand Up @@ -42,18 +42,15 @@ export const Search = () => {
}

return (
<Suspense
<LoadSpinnerWithSuspense
fallback={
<>
<SearchAlgoliaToggle
toggleSearch={toggleSearch}
closeSearch={closeSearch}
/>
<ModalLoadingTrigger modal={MODALS.SITE_SEARCH} />
</>
<SearchAlgoliaToggle
toggleSearch={toggleSearch}
closeSearch={closeSearch}
/>
}
>
<SearchAlgoliaLazy />
</Suspense>
</LoadSpinnerWithSuspense>
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,8 @@ export const SearchAlgoliaToggle = (props: PropType) => {
ref={toggleElement}
$isKeyNavigating={isKeyNavigating}
onClick={toggleSearch}
aria-label="Search"
type="button"
>
<SearchButtonIcon svg="search" />
</SearchButton>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,23 +1,17 @@
import React, {
PropsWithChildren,
Suspense,
lazy,
useCallback,
useEffect
} from 'react'
import React, { PropsWithChildren, lazy, useCallback, useEffect } from 'react'
import styled, { css } from 'styled-components'
import { useAppDispatch, useAppSelector } from 'hooks/useRedux'
import FocusTrap from 'focus-trap-react'
import { useEventListener } from 'hooks/useEventListener'
import { useBreakpoints } from 'hooks/useBreakpoints'
import { MEDIA } from 'consts/breakpoints'
import { LAYERS } from 'consts/layers'
import { HEADER_HEIGHT, HEADER_ID } from 'components/Header/Header'
import { MODALS } from 'consts/modal'
import { SPACINGS } from 'consts/spacings'
import { HEADER_HEIGHT, HEADER_ID } from 'consts/header'
import { isBrowser } from 'utils/isBrowser'
import { ModalLoadingTrigger } from 'components/Modal/ModalLoadingTrigger'
import { SiteNavigationMenuDesktop } from 'components/SiteNavigation/SiteNavigationMenuDesktop'
import { LoadSpinnerWithSuspense } from 'components/LoadSpinner/LoadSpinnerWithSuspense'
import {
selectIsModalOpen,
setModalClosed
Expand Down Expand Up @@ -110,11 +104,9 @@ export const SiteNavigation = (props: PropType) => {
<SiteNavigationMenuDesktop />

{isOpen && (
<Suspense
fallback={<ModalLoadingTrigger modal={MODALS.SITE_NAVIGATION} />}
>
<LoadSpinnerWithSuspense>
<SiteNavigationMenuCompactLazy />
</Suspense>
</LoadSpinnerWithSuspense>
)}
</SiteNavigationWrapper>
</FocusTrap>
Expand Down
Loading

0 comments on commit 55d2034

Please sign in to comment.