Skip to content

Commit

Permalink
refactor(common/components): Modal add default className
Browse files Browse the repository at this point in the history
  • Loading branch information
exuanbo committed Dec 16, 2023
1 parent 48d58a2 commit cca496b
Show file tree
Hide file tree
Showing 6 changed files with 60 additions and 45 deletions.
29 changes: 17 additions & 12 deletions src/common/components/Modal.tsx
Original file line number Diff line number Diff line change
@@ -1,32 +1,37 @@
import { type ReactNode, useEffect, useState } from 'react'
import { type FC, type PropsWithChildren, useEffect, useState } from 'react'
import { createPortal } from 'react-dom'

import { type ClassItem, mergeClassNames } from '../utils'

const modalContainer = document.getElementById('modal-root')!

const modalClassName = 'fixed inset-0 flex items-center justify-center'

interface Props {
children: ReactNode
className?: ClassItem
isOpen?: boolean
className?: string
}

const Modal = ({ children, isOpen = false, className = '' }: Props): JSX.Element | null => {
const [modal, setModal] = useState<HTMLDivElement | null>(null)
const isReady = isOpen && modal !== null
const Modal: FC<PropsWithChildren<Props>> = ({ children, className, isOpen = false }) => {
const [currentModal, setCurrentModal] = useState<HTMLDivElement | null>(null)
const isReady = isOpen && !!currentModal

useEffect(() => {
if (!isOpen) {
return
}
const currentModal = Object.assign(document.createElement('div'), { className })
modalContainer.appendChild(currentModal)
setModal(currentModal)
const modal = Object.assign(document.createElement('div'), {
className: mergeClassNames(modalClassName, className),
})
modalContainer.appendChild(modal)
setCurrentModal(modal)
return () => {
modalContainer.removeChild(currentModal)
setModal(null)
modalContainer.removeChild(modal)
setCurrentModal(null)
}
}, [isOpen, className])

return isReady ? createPortal(children, modal) : null
return isReady && createPortal(children, currentModal)
}

export default Modal
33 changes: 31 additions & 2 deletions src/common/utils/classNames.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@

import type { Nullable } from './types'

type Item = Nullable<string | Record<string, Nullable<boolean>>>
export type ClassItem = Nullable<string | Record<string, Nullable<boolean>>>

export const classNames = (...items: Item[]): string => {
export const classNames = (...items: ClassItem[]): string => {
let className = ''
const count = items.length
for (let index = 0; index < count; index++) {
Expand All @@ -29,3 +29,32 @@ export const classNames = (...items: Item[]): string => {
}
return className
}

const stringToRecord = (item: string) => {
const record: Record<string, true> = {}
const keys = item.split(' ')
const count = keys.length
for (let index = 0; index < count; index++) {
const key = keys[index]
if (key) {
record[key] = true
}
}
return record
}

export const mergeClassNames = (target: ClassItem, source: ClassItem): string => {
if (!target) {
return classNames(source)
}
if (!source) {
return classNames(target)
}
if (typeof target === 'string') {
return mergeClassNames(stringToRecord(target), source)
}
if (typeof source === 'string') {
return mergeClassNames(target, stringToRecord(source))
}
return classNames({ ...target, ...source })
}
33 changes: 9 additions & 24 deletions src/features/controller/FileMenu.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import { useRef, useState } from 'react'
import { useRef } from 'react'

import { store } from '@/app/store'
import { File as FileIcon } from '@/common/components/icons'
import Modal from '@/common/components/Modal'
import { invariant } from '@/common/utils'
import { selectEditorInput, setEditorInput } from '@/features/editor/editorSlice'
import { examples, template } from '@/features/editor/examples'
Expand Down Expand Up @@ -140,35 +139,21 @@ const SaveButton = (): JSX.Element => {
)
}

const ERROR_DURATION_MS = 2000

const CopyLinkButton = (): JSX.Element => {
const [shouldShowError, setShouldShowError] = useState(false)

const handleClick: React.MouseEventHandler<HTMLDivElement> = (event) => {
navigator.clipboard.writeText(window.location.href).catch(() => {
navigator.clipboard.writeText(window.location.href).catch((reason) => {
event.stopPropagation()
setShouldShowError(true)
window.setTimeout(() => {
setShouldShowError(false)
}, ERROR_DURATION_MS)
console.error(reason)
})
}

return (
<>
<MenuItem onClick={handleClick}>
<MenuButton>
<span className="w-4" />
<span>Copy Link</span>
</MenuButton>
</MenuItem>
<Modal
className="bg-white flex bg-opacity-80 inset-0 fixed items-center justify-center"
isOpen={shouldShowError}>
<div className="border rounded bg-light-100 shadow py-2 px-4">Copy Failed</div>
</Modal>
</>
<MenuItem onClick={handleClick}>
<MenuButton>
<span className="w-4" />
<span>Copy Link</span>
</MenuButton>
</MenuItem>
)
}

Expand Down
4 changes: 1 addition & 3 deletions src/features/controller/HelpMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -69,9 +69,7 @@ const AboutModal = ({ isOpen, toggleOpen }: AboutModalProps): JSX.Element => {
}

return (
<Modal
className="bg-black flex bg-opacity-20 inset-0 fixed items-center justify-center"
isOpen={isOpen}>
<Modal className="bg-black bg-opacity-20" isOpen={isOpen}>
<div
ref={outsideClickRef}
className="rounded space-y-2 bg-light-100 shadow text-center py-4 px-8 select-text all:select-text"
Expand Down
4 changes: 1 addition & 3 deletions src/features/exception/ExceptionModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,7 @@ const ExceptionModal = (): JSX.Element => {
const outsideClickRef = useOutsideClick(handleOutsideClick)

return (
<Modal
className="bg-white flex bg-opacity-80 inset-0 z-50 fixed items-center justify-center"
isOpen={hasError}>
<Modal className="bg-white bg-opacity-80 z-50" isOpen={hasError}>
<div
ref={outsideClickRef}
className="border rounded space-y-2 bg-light-100 shadow py-2 px-4 select-text all:select-text">
Expand Down
2 changes: 1 addition & 1 deletion src/features/io/SimulatedKeyboard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ const SimulatedKeyboard = (): JSX.Element => {

return (
<Modal
className="bg-black flex bg-opacity-20 inset-0 fixed items-center justify-center animate-fade-in animate-duration-250"
className="bg-black bg-opacity-20 animate-fade-in animate-duration-250"
isOpen={shouldOpen}>
<div className="rounded bg-light-100 shadow py-4 px-8">
Waiting for keyboard input <PulseLoader />
Expand Down

0 comments on commit cca496b

Please sign in to comment.