From b4f217071fea180f572ce94c8a08891fa72e6de4 Mon Sep 17 00:00:00 2001 From: Justin Edmund Date: Sun, 15 Jan 2023 10:36:20 -0800 Subject: [PATCH] Extract Dialog into its own component --- components/AccountModal/index.tsx | 6 +- components/CharacterConflictModal/index.tsx | 5 +- components/CharacterModal/index.scss | 2 +- components/CharacterModal/index.tsx | 4 +- components/Dialog/index.scss | 216 ------------------- components/Dialog/index.tsx | 46 ++-- components/DialogContent/index.scss | 225 ++++++++++++++++++++ components/DialogContent/index.tsx | 43 ++++ components/LoginModal/index.scss | 2 +- components/LoginModal/index.tsx | 11 +- components/SearchModal/index.tsx | 9 +- components/SignupModal/index.scss | 2 +- components/SignupModal/index.tsx | 11 +- components/WeaponConflictModal/index.tsx | 5 +- components/WeaponModal/index.tsx | 5 +- 15 files changed, 310 insertions(+), 282 deletions(-) create mode 100644 components/DialogContent/index.scss create mode 100644 components/DialogContent/index.tsx diff --git a/components/AccountModal/index.tsx b/components/AccountModal/index.tsx index c5da21ef..ede03821 100644 --- a/components/AccountModal/index.tsx +++ b/components/AccountModal/index.tsx @@ -2,14 +2,15 @@ import React, { useEffect, useState } from 'react' import { getCookie, setCookie } from 'cookies-next' import { useRouter } from 'next/router' import { useTranslation } from 'next-i18next' +import { useTheme } from 'next-themes' import { Dialog, DialogClose, - DialogContent, DialogTitle, DialogTrigger, } from '~components/Dialog' +import DialogContent from '~components/DialogContent' import Button from '~components/Button' import SelectItem from '~components/SelectItem' import PictureSelectItem from '~components/PictureSelectItem' @@ -23,7 +24,6 @@ import { pictureData } from '~utils/pictureData' import CrossIcon from '~public/icons/Cross.svg' import './index.scss' -import { useTheme } from 'next-themes' type StateVariables = { [key: string]: boolean @@ -285,7 +285,7 @@ const AccountModal = (props: Props) => { {}} onEscapeKeyDown={onEscapeKeyDown} > diff --git a/components/CharacterConflictModal/index.tsx b/components/CharacterConflictModal/index.tsx index b14fed31..285260a5 100644 --- a/components/CharacterConflictModal/index.tsx +++ b/components/CharacterConflictModal/index.tsx @@ -2,7 +2,8 @@ import React, { useEffect, useState } from 'react' import { useRouter } from 'next/router' import { Trans, useTranslation } from 'next-i18next' -import { Dialog, DialogContent } from '~components/Dialog' +import { Dialog } from '~components/Dialog' +import DialogContent from '~components/DialogContent' import Button from '~components/Button' import Overlay from '~components/Overlay' @@ -71,7 +72,7 @@ const CharacterConflictModal = (props: Props) => { return ( event.preventDefault()} onEscapeKeyDown={close} > diff --git a/components/CharacterModal/index.scss b/components/CharacterModal/index.scss index 98944c83..6de35952 100644 --- a/components/CharacterModal/index.scss +++ b/components/CharacterModal/index.scss @@ -1,4 +1,4 @@ -.Character.Dialog { +.Character.DialogContent { min-width: 480px; @include breakpoint(phone) { diff --git a/components/CharacterModal/index.tsx b/components/CharacterModal/index.tsx index f6dde745..e8855066 100644 --- a/components/CharacterModal/index.tsx +++ b/components/CharacterModal/index.tsx @@ -8,10 +8,10 @@ import { AxiosResponse } from 'axios' import { Dialog, DialogClose, - DialogContent, DialogTitle, DialogTrigger, } from '~components/Dialog' +import DialogContent from '~components/DialogContent' import Button from '~components/Button' import SelectWithInput from '~components/SelectWithInput' import AwakeningSelect from '~components/AwakeningSelect' @@ -259,7 +259,7 @@ const CharacterModal = ({ {children} event.preventDefault()} onEscapeKeyDown={() => {}} > diff --git a/components/Dialog/index.scss b/components/Dialog/index.scss index 29e95084..e69de29b 100644 --- a/components/Dialog/index.scss +++ b/components/Dialog/index.scss @@ -1,216 +0,0 @@ -.Dialog { - $multiplier: 4; - - animation: 0.5s cubic-bezier(0.16, 1, 0.3, 1) 0s 1 normal none running - openModalDesktop; - background: var(--dialog-bg); - border-radius: $card-corner; - box-sizing: border-box; - display: flex; - flex-direction: column; - gap: $unit * $multiplier; - height: auto; - min-width: $unit * 48; - min-height: $unit-12x; - min-width: 580px; - padding: $unit * $multiplier; - position: absolute; - top: 50%; - left: 50%; - transform: translate(-50%, -50%); - z-index: 40; - - a:hover { - text-decoration: underline; - } - - @include breakpoint(phone) { - animation: 0.5s cubic-bezier(0.16, 1, 0.3, 1) 0s 1 normal forwards running - openModalMobile; - min-width: inherit; - min-height: 80vh; - transform: initial; - left: 0; - right: 0; - top: 0; - height: auto; - width: 100%; - } - - .DialogHeader { - display: flex; - align-items: center; - gap: $unit-2x; - justify-content: space-between; - - .left { - display: flex; - flex-direction: column; - flex-grow: 1; - gap: $unit; - - p { - font-size: $font-small; - line-height: 1.25; - } - } - - .DialogImage { - border-radius: $input-corner; - width: $unit-10x; - } - } - - .DialogClose { - background: transparent; - border: none; - - &:hover { - cursor: pointer; - - svg { - fill: $error; - } - } - - svg { - fill: $grey-50; - float: right; - height: 24px; - width: 24px; - } - } - - .DialogTitle { - color: var(--text-primary); - font-size: $font-xlarge; - - h1 { - color: var(--text-primary); - font-size: $font-xlarge; - font-weight: $medium; - text-align: left; - } - } - - .DialogTop { - display: flex; - flex-direction: column; - flex-grow: 1; - gap: calc($unit / 2); - - .SubTitle { - color: var(--text-secondary); - font-size: $font-small; - font-weight: $medium; - } - } - - .DialogDescription { - color: var(--text-secondary); - flex-grow: 1; - } - - .actions { - display: flex; - justify-content: flex-end; - width: 100%; - } - - &.Conflict.Dialog { - $weapon-diameter: 14rem; - - & > p { - line-height: 1.2; - max-width: 400px; - - strong { - font-weight: $bold; - } - - &:lang(ja) { - line-height: 1.4; - } - } - - .weapon, - .character { - display: flex; - flex-direction: column; - gap: $unit; - text-align: center; - width: $weapon-diameter; - font-weight: $medium; - - img { - border-radius: 1rem; - width: $weapon-diameter; - height: auto; - } - - span { - line-height: 1.3; - } - } - - .Diagram { - display: grid; - grid-template-columns: 1fr auto 1fr; - align-items: flex-start; - - &.CharacterDiagram { - align-items: center; - } - - ul { - align-items: center; - display: flex; - flex-direction: column; - gap: $unit * 2; - } - - .wrapper { - display: flex; - justify-content: center; - width: 100%; - } - - .arrow { - align-items: center; - color: $grey-55; - display: flex; - font-size: 4rem; - text-align: center; - height: $weapon-diameter; - justify-content: center; - } - } - - footer { - display: flex; - flex-direction: row; - gap: $unit; - - .Button { - font-size: $font-regular; - padding: ($unit * 1.5) ($unit * 2); - width: 100%; - - &.btn-disabled { - background: $grey-90; - color: $grey-70; - cursor: not-allowed; - } - - &:not(.btn-disabled) { - background: $grey-90; - color: $grey-50; - - &:hover { - background: $grey-80; - } - } - } - } - } -} diff --git a/components/Dialog/index.tsx b/components/Dialog/index.tsx index 8f544fa8..36a3dc25 100644 --- a/components/Dialog/index.tsx +++ b/components/Dialog/index.tsx @@ -1,46 +1,36 @@ import React from 'react' import * as DialogPrimitive from '@radix-ui/react-dialog' -import classNames from 'classnames' import './index.scss' -import Overlay from '~components/Overlay' interface Props extends React.DetailedHTMLProps< React.DialogHTMLAttributes, HTMLDivElement > { - onEscapeKeyDown: (event: KeyboardEvent) => void - onOpenAutoFocus: (event: Event) => void + open: boolean + onOpenChange: (open: boolean) => void } -export const DialogContent = React.forwardRef( - function dialog({ children, ...props }, forwardedRef) { - const classes = classNames( - { - Dialog: true, - }, - props.className - ) +export const Dialog = React.forwardRef(function dialog( + { children, ...props }, + forwardedRef +) { + const [locked, setLocked] = useLockedBody(false, 'root') - return ( - - - {children} - - - - ) } -) -export const Dialog = DialogPrimitive.Root + function onOpenChange(open: boolean) { + props.onOpenChange(open) + } + + return ( + + {children} + + ) +}) + export const DialogTitle = DialogPrimitive.Title export const DialogTrigger = DialogPrimitive.Trigger export const DialogClose = DialogPrimitive.Close diff --git a/components/DialogContent/index.scss b/components/DialogContent/index.scss new file mode 100644 index 00000000..7bcd484a --- /dev/null +++ b/components/DialogContent/index.scss @@ -0,0 +1,225 @@ +.Dialog { + position: fixed; + background: none; + border: 0; + inset: 0; + display: grid; + place-items: center; + min-height: 100vh; + min-width: 100vw; + overflow-y: auto; + color: inherit; + z-index: 40; + + .DialogContent { + $multiplier: 4; + + animation: 0.5s cubic-bezier(0.16, 1, 0.3, 1) 0s 1 normal none running + openModalDesktop; + background: var(--dialog-bg); + border-radius: $card-corner; + box-sizing: border-box; + display: flex; + flex-direction: column; + gap: $unit * $multiplier; + height: auto; + min-width: $unit * 48; + min-height: $unit-12x; + min-width: 580px; + padding: $unit * $multiplier; + + a:hover { + text-decoration: underline; + } + + @include breakpoint(phone) { + animation: 0.5s cubic-bezier(0.16, 1, 0.3, 1) 0s 1 normal forwards running + openModalMobile; + min-width: inherit; + min-height: 80vh; + transform: initial; + left: 0; + right: 0; + top: 0; + height: auto; + width: 100%; + } + + .DialogHeader { + display: flex; + align-items: center; + gap: $unit-2x; + justify-content: space-between; + + .left { + display: flex; + flex-direction: column; + flex-grow: 1; + gap: $unit; + + p { + font-size: $font-small; + line-height: 1.25; + } + } + + .DialogImage { + border-radius: $input-corner; + width: $unit-10x; + } + } + + .DialogClose { + background: transparent; + border: none; + + &:hover { + cursor: pointer; + + svg { + fill: $error; + } + } + + svg { + fill: $grey-50; + float: right; + height: 24px; + width: 24px; + } + } + + .DialogTitle { + color: var(--text-primary); + font-size: $font-xlarge; + + h1 { + color: var(--text-primary); + font-size: $font-xlarge; + font-weight: $medium; + text-align: left; + } + } + + .DialogTop { + display: flex; + flex-direction: column; + flex-grow: 1; + gap: calc($unit / 2); + + .SubTitle { + color: var(--text-secondary); + font-size: $font-small; + font-weight: $medium; + } + } + + .DialogDescription { + color: var(--text-secondary); + flex-grow: 1; + } + + .actions { + display: flex; + justify-content: flex-end; + width: 100%; + } + + &.Conflict { + $weapon-diameter: 14rem; + + & > p { + line-height: 1.2; + max-width: 400px; + + strong { + font-weight: $bold; + } + + &:lang(ja) { + line-height: 1.4; + } + } + + .weapon, + .character { + display: flex; + flex-direction: column; + gap: $unit; + text-align: center; + width: $weapon-diameter; + font-weight: $medium; + + img { + border-radius: 1rem; + width: $weapon-diameter; + height: auto; + } + + span { + line-height: 1.3; + } + } + + .Diagram { + display: grid; + grid-template-columns: 1fr auto 1fr; + align-items: flex-start; + + &.CharacterDiagram { + align-items: center; + } + + ul { + align-items: center; + display: flex; + flex-direction: column; + gap: $unit * 2; + } + + .wrapper { + display: flex; + justify-content: center; + width: 100%; + } + + .arrow { + align-items: center; + color: $grey-55; + display: flex; + font-size: 4rem; + text-align: center; + height: $weapon-diameter; + justify-content: center; + } + } + + footer { + display: flex; + flex-direction: row; + gap: $unit; + + .Button { + font-size: $font-regular; + padding: ($unit * 1.5) ($unit * 2); + width: 100%; + + &.btn-disabled { + background: $grey-90; + color: $grey-70; + cursor: not-allowed; + } + + &:not(.btn-disabled) { + background: $grey-90; + color: $grey-50; + + &:hover { + background: $grey-80; + } + } + } + } + } + } +} diff --git a/components/DialogContent/index.tsx b/components/DialogContent/index.tsx new file mode 100644 index 00000000..6086da4c --- /dev/null +++ b/components/DialogContent/index.tsx @@ -0,0 +1,43 @@ +import React from 'react' +import * as DialogPrimitive from '@radix-ui/react-dialog' +import classNames from 'classnames' + +import './index.scss' +import Overlay from '~components/Overlay' + +interface Props + extends React.DetailedHTMLProps< + React.DialogHTMLAttributes, + HTMLDivElement + > { + onEscapeKeyDown: (event: KeyboardEvent) => void + onOpenAutoFocus: (event: Event) => void +} + +const DialogContent = React.forwardRef(function dialog( + { children, ...props }, + forwardedRef +) { + const classes = classNames(props.className, { + DialogContent: true, + }) + + return ( + + + + {children} + + + + + ) +}) + +export default DialogContent diff --git a/components/LoginModal/index.scss b/components/LoginModal/index.scss index f51192e5..05c0df75 100644 --- a/components/LoginModal/index.scss +++ b/components/LoginModal/index.scss @@ -1,4 +1,4 @@ -.Login.Dialog form { +.Login.DialogContent form { display: flex; flex-direction: column; gap: calc($unit / 2); diff --git a/components/LoginModal/index.tsx b/components/LoginModal/index.tsx index 5c4b454b..60252c43 100644 --- a/components/LoginModal/index.tsx +++ b/components/LoginModal/index.tsx @@ -10,13 +10,8 @@ import { accountState } from '~utils/accountState' import Button from '~components/Button' import Input from '~components/LabelledInput' -import { - Dialog, - DialogTrigger, - DialogContent, - DialogClose, -} from '~components/Dialog' - +import { Dialog, DialogTrigger, DialogClose } from '~components/Dialog' +import DialogContent from '~components/DialogContent' import changeLanguage from '~utils/changeLanguage' import CrossIcon from '~public/icons/Cross.svg' @@ -203,7 +198,7 @@ const LoginModal = () => { diff --git a/components/SearchModal/index.tsx b/components/SearchModal/index.tsx index cf1a396a..3de50529 100644 --- a/components/SearchModal/index.tsx +++ b/components/SearchModal/index.tsx @@ -6,13 +6,8 @@ import InfiniteScroll from 'react-infinite-scroll-component' import api from '~utils/api' -import { - Dialog, - DialogTrigger, - DialogContent, - DialogClose, -} from '~components/Dialog' - +import { Dialog, DialogTrigger, DialogClose } from '~components/Dialog' +import DialogContent from '~components/DialogContent' import Input from '~components/LabelledInput' import CharacterSearchFilterBar from '~components/CharacterSearchFilterBar' import WeaponSearchFilterBar from '~components/WeaponSearchFilterBar' diff --git a/components/SignupModal/index.scss b/components/SignupModal/index.scss index a7db71a9..51586a6b 100644 --- a/components/SignupModal/index.scss +++ b/components/SignupModal/index.scss @@ -1,4 +1,4 @@ -.Signup.Dialog form { +.Signup.DialogContent form { display: flex; flex-direction: column; gap: calc($unit / 2); diff --git a/components/SignupModal/index.tsx b/components/SignupModal/index.tsx index 95ab5bd3..3c745913 100644 --- a/components/SignupModal/index.tsx +++ b/components/SignupModal/index.tsx @@ -10,13 +10,8 @@ import { accountState } from '~utils/accountState' import Button from '~components/Button' import Input from '~components/LabelledInput' -import { - Dialog, - DialogTrigger, - DialogContent, - DialogClose, -} from '~components/Dialog' - +import { Dialog, DialogTrigger, DialogClose } from '~components/Dialog' +import DialogContent from '~components/DialogContent' import CrossIcon from '~public/icons/Cross.svg' import './index.scss' @@ -283,7 +278,7 @@ const SignupModal = (props: Props) => { diff --git a/components/WeaponConflictModal/index.tsx b/components/WeaponConflictModal/index.tsx index ce7b6620..d1a9a3c7 100644 --- a/components/WeaponConflictModal/index.tsx +++ b/components/WeaponConflictModal/index.tsx @@ -2,7 +2,8 @@ import React, { useEffect, useState } from 'react' import { useRouter } from 'next/router' import { Trans, useTranslation } from 'react-i18next' -import { Dialog, DialogContent } from '~components/Dialog' +import { Dialog } from '~components/Dialog' +import DialogContent from '~components/DialogContent' import Button from '~components/Button' import Overlay from '~components/Overlay' @@ -65,7 +66,7 @@ const WeaponConflictModal = (props: Props) => { return ( event.preventDefault()} onEscapeKeyDown={close} > diff --git a/components/WeaponModal/index.tsx b/components/WeaponModal/index.tsx index cb88311f..45816ce8 100644 --- a/components/WeaponModal/index.tsx +++ b/components/WeaponModal/index.tsx @@ -7,11 +7,10 @@ import { AxiosResponse } from 'axios' import { Dialog, DialogClose, - DialogContent, DialogTitle, DialogTrigger, } from '~components/Dialog' - +import DialogContent from '~components/DialogContent' import AXSelect from '~components/AxSelect' import AwakeningSelect from '~components/AwakeningSelect' import ElementToggle from '~components/ElementToggle' @@ -344,7 +343,7 @@ const WeaponModal = (props: Props) => { {props.children} event.preventDefault()} onEscapeKeyDown={onEscapeKeyDown} >