Extract Dialog into its own component

This commit is contained in:
Justin Edmund 2023-01-15 10:36:20 -08:00
parent 26a30ad89c
commit b4f217071f
15 changed files with 310 additions and 282 deletions

View file

@ -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) => {
</li>
</DialogTrigger>
<DialogContent
className="Account Dialog"
className="Account"
onOpenAutoFocus={(event: Event) => {}}
onEscapeKeyDown={onEscapeKeyDown}
>

View file

@ -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 (
<Dialog open={open} onOpenChange={openChange}>
<DialogContent
className="Conflict Dialog"
className="Conflict"
onOpenAutoFocus={(event) => event.preventDefault()}
onEscapeKeyDown={close}
>

View file

@ -1,4 +1,4 @@
.Character.Dialog {
.Character.DialogContent {
min-width: 480px;
@include breakpoint(phone) {

View file

@ -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 = ({
<Dialog open={open} onOpenChange={openChange}>
<DialogTrigger asChild>{children}</DialogTrigger>
<DialogContent
className="Character Dialog"
className="Character"
onOpenAutoFocus={(event) => event.preventDefault()}
onEscapeKeyDown={() => {}}
>

View file

@ -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;
}
}
}
}
}
}

View file

@ -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>,
HTMLDivElement
> {
onEscapeKeyDown: (event: KeyboardEvent) => void
onOpenAutoFocus: (event: Event) => void
open: boolean
onOpenChange: (open: boolean) => void
}
export const DialogContent = React.forwardRef<HTMLDivElement, Props>(
function dialog({ children, ...props }, forwardedRef) {
const classes = classNames(
{
Dialog: true,
},
props.className
)
export const Dialog = React.forwardRef<HTMLDivElement, Props>(function dialog(
{ children, ...props },
forwardedRef
) {
const [locked, setLocked] = useLockedBody(false, 'root')
return (
<DialogPrimitive.Portal>
<DialogPrimitive.Content
className={classes}
{...props}
onOpenAutoFocus={props.onOpenAutoFocus}
onEscapeKeyDown={props.onEscapeKeyDown}
ref={forwardedRef}
>
{children}
</DialogPrimitive.Content>
<Overlay visible={true} open={true} />
</DialogPrimitive.Portal>
)
}
)
export const Dialog = DialogPrimitive.Root
function onOpenChange(open: boolean) {
props.onOpenChange(open)
}
return (
<DialogPrimitive.Root open={props.open} onOpenChange={onOpenChange}>
{children}
</DialogPrimitive.Root>
)
})
export const DialogTitle = DialogPrimitive.Title
export const DialogTrigger = DialogPrimitive.Trigger
export const DialogClose = DialogPrimitive.Close

View file

@ -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;
}
}
}
}
}
}
}

View file

@ -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>,
HTMLDivElement
> {
onEscapeKeyDown: (event: KeyboardEvent) => void
onOpenAutoFocus: (event: Event) => void
}
const DialogContent = React.forwardRef<HTMLDivElement, Props>(function dialog(
{ children, ...props },
forwardedRef
) {
const classes = classNames(props.className, {
DialogContent: true,
})
return (
<DialogPrimitive.Portal>
<dialog className="Dialog">
<DialogPrimitive.Content
{...props}
className={classes}
onOpenAutoFocus={props.onOpenAutoFocus}
onEscapeKeyDown={props.onEscapeKeyDown}
ref={forwardedRef}
>
{children}
</DialogPrimitive.Content>
</dialog>
<Overlay visible={true} open={true} />
</DialogPrimitive.Portal>
)
})
export default DialogContent

View file

@ -1,4 +1,4 @@
.Login.Dialog form {
.Login.DialogContent form {
display: flex;
flex-direction: column;
gap: calc($unit / 2);

View file

@ -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 = () => {
</li>
</DialogTrigger>
<DialogContent
className="Login Dialog"
className="Login"
onEscapeKeyDown={onEscapeKeyDown}
onOpenAutoFocus={onOpenAutoFocus}
>

View file

@ -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'

View file

@ -1,4 +1,4 @@
.Signup.Dialog form {
.Signup.DialogContent form {
display: flex;
flex-direction: column;
gap: calc($unit / 2);

View file

@ -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) => {
</li>
</DialogTrigger>
<DialogContent
className="Signup Dialog"
className="Signup"
onEscapeKeyDown={onEscapeKeyDown}
onOpenAutoFocus={onOpenAutoFocus}
>

View file

@ -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 (
<Dialog open={open} onOpenChange={openChange}>
<DialogContent
className="Conflict Dialog"
className="Conflict"
onOpenAutoFocus={(event) => event.preventDefault()}
onEscapeKeyDown={close}
>

View file

@ -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) => {
<Dialog open={open} onOpenChange={openChange}>
<DialogTrigger asChild>{props.children}</DialogTrigger>
<DialogContent
className="Weapon Dialog"
className="Weapon"
onOpenAutoFocus={(event) => event.preventDefault()}
onEscapeKeyDown={onEscapeKeyDown}
>