-
Notifications
You must be signed in to change notification settings - Fork 1
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[#134] 대회 페이지 시뮬레이션 영역 모달로 분리하기 #146
The head ref may contain hidden characters: "134-\uB300\uD68C-\uD398\uC774\uC9C0-\uC2DC\uBBAC\uB808\uC774\uC158-\uC601\uC5ED-\uBAA8\uB2EC\uB85C-\uBD84\uB9AC\uD558\uAE30"
Changes from all commits
7e231e7
ff6eeff
ccbdb44
4ac1297
0fb46c5
8497c60
cb8c4b1
c5bba50
121ca30
35a6cf1
3cd7413
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
import { css, cx } from '@style/css'; | ||
|
||
import type { HTMLAttributes, MouseEvent } from 'react'; | ||
import { useContext, useEffect, useRef } from 'react'; | ||
import ReactDOM from 'react-dom'; | ||
|
||
import { ModalContext } from './ModalContext'; | ||
import { ModalProvider } from './ModalProvider'; | ||
|
||
export interface Props extends HTMLAttributes<HTMLDialogElement> { | ||
onBackdropPressed?: () => void; | ||
} | ||
|
||
export function Modal({ onBackdropPressed, children, ...props }: Props) { | ||
const modal = useContext(ModalContext); | ||
const $dialog = useRef<HTMLDialogElement>(null); | ||
|
||
const handleClickBackdrop = (e: MouseEvent<HTMLDialogElement>) => { | ||
const $target = e.target as HTMLDialogElement; | ||
|
||
if ($target.nodeName !== 'DIALOG') return; | ||
|
||
if (onBackdropPressed instanceof Function) { | ||
onBackdropPressed(); | ||
} | ||
Comment on lines
+23
to
+25
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. common 아래에 모달이라 onBackdropPressed가 있을수도 있고 없을 수도 있다는 고 있으면 실행해라는 의미로 해석됩니다. typeof onBackdropPressed === 'function'로 안 하신 이유가 있을까요? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 별 의미는 없었습니다. typeof를 쓰는 것이 더 유리한가요? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. String으로 타입 비교 안 하고 'string'하는 것처럼 함수도 'function'으로 하는 게 일관성 있어 보여서 물어봤습니다. 찾아 봤는데, 아무런 문제 없어보이네요! |
||
}; | ||
|
||
useEffect(() => { | ||
if (modal.isOpen) { | ||
$dialog.current?.showModal(); | ||
} else { | ||
$dialog.current?.close(); | ||
} | ||
}, [modal.isOpen]); | ||
|
||
return ReactDOM.createPortal( | ||
<dialog | ||
ref={$dialog} | ||
className={cx(style, dialogStyle)} | ||
aria-modal="true" | ||
aria-labelledby="dialog-title" | ||
onClick={handleClickBackdrop} | ||
{...props} | ||
> | ||
Comment on lines
+36
to
+44
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. createPortal은 처음 보는데 이렇게도 쓸 수 있군요, 감사합니다!:D There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 네 app이 되는 |
||
<div className={contentStyle}>{children}</div> | ||
</dialog>, | ||
document.body, | ||
); | ||
} | ||
|
||
Modal.Context = ModalContext; | ||
Modal.Provider = ModalProvider; | ||
|
||
const style = css({ | ||
borderRadius: '0.5rem', | ||
}); | ||
|
||
const dialogStyle = css({ | ||
position: 'fixed', | ||
left: '50%', | ||
top: '50%', | ||
transform: 'translate(-50%,-50%)', | ||
width: '500px', | ||
height: '400px', | ||
_backdrop: { | ||
background: 'rgba(00,00,00,0.5)', | ||
backdropFilter: 'blur(1rem)', | ||
Comment on lines
+65
to
+67
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 모달 만들때 꿀팁이네요. |
||
}, | ||
}); | ||
|
||
const contentStyle = css({ | ||
width: '100%', | ||
height: '100%', | ||
}); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
import { createContext } from 'react'; | ||
|
||
interface ModalContextProps { | ||
isOpen: boolean; | ||
close: () => void; | ||
open: () => void; | ||
} | ||
|
||
export const ModalContext = createContext<ModalContextProps>({ | ||
isOpen: false, | ||
close: () => {}, | ||
open: () => {}, | ||
}); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
import type { ReactNode } from 'react'; | ||
import { useState } from 'react'; | ||
|
||
import { ModalContext } from './ModalContext'; | ||
|
||
export function ModalProvider({ children }: { children: ReactNode }) { | ||
const [isOpen, setIsOpen] = useState<boolean>(false); | ||
const close = () => { | ||
setIsOpen(false); | ||
}; | ||
const open = () => { | ||
setIsOpen(true); | ||
}; | ||
|
||
return ( | ||
<ModalContext.Provider | ||
value={{ | ||
isOpen, | ||
close, | ||
open, | ||
}} | ||
> | ||
{children} | ||
</ModalContext.Provider> | ||
); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
export { Modal } from './Modal'; | ||
export type { Props as ModalProps } from './Modal'; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,2 @@ | ||
export { Input } from './Input'; | ||
export * from './Modal'; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
import { useContext, useState } from 'react'; | ||
|
||
import type { SimulationInput } from '@/hooks/simulation'; | ||
import { deepCopy } from '@/utils/copy'; | ||
|
||
import { Modal, type ModalProps } from '../Common'; | ||
import { SimulationInputList } from './SimulationInputList'; | ||
|
||
interface Props extends ModalProps { | ||
simulationInputs: SimulationInput[]; | ||
onSave: (inputs: SimulationInput[]) => void; | ||
} | ||
|
||
export function SimulationInputModal({ simulationInputs, onSave, ...props }: Props) { | ||
const modal = useContext(Modal.Context); | ||
const [inputs, setInputs] = useState<SimulationInput[]>(deepCopy(simulationInputs)); | ||
|
||
const handleCloseModal = () => { | ||
setInputs(simulationInputs); | ||
modal.close(); | ||
}; | ||
|
||
const handleSave = () => { | ||
onSave(deepCopy(inputs)); | ||
modal.close(); | ||
Comment on lines
+20
to
+25
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. useContext안에 모달을 닫고 여는 함수들의 네이밍(close,open)이 부실하다 생각했는데 |
||
}; | ||
|
||
const handleChangeInput = (targetId: number, newParam: string) => { | ||
const changedSimulation = inputs.find(({ id }) => id === targetId); | ||
if (changedSimulation) { | ||
changedSimulation.input = newParam; | ||
} | ||
setInputs([...inputs]); | ||
}; | ||
|
||
return ( | ||
<Modal {...props}> | ||
<SimulationInputList | ||
inputList={inputs} | ||
onChangeInput={handleChangeInput} | ||
></SimulationInputList> | ||
<button onClick={handleCloseModal}>닫기</button> | ||
<button onClick={handleSave}>저장</button> | ||
</Modal> | ||
); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,2 @@ | ||
export * from './useSimulations'; | ||
export * from './useSimulation'; | ||
export * from './types'; |
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 여기 변경사항은 useSimulations -> useSimulation으로 바뀌면서 생긴 것들이 다수고, 전체적으로 simulationInputs... 이렇게 시작하던 이름을 inputs 이렇게 바꾼 것들이 다에요 |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,94 @@ | ||
import { useEffect, useMemo, useState } from 'react'; | ||
|
||
import evaluator from '@/modules/evaluator'; | ||
|
||
import type { SimulationInput, SimulationResult } from './types'; | ||
|
||
export const useSimulation = () => { | ||
const [inputs, setInputs] = useState<SimulationInput[]>([ | ||
{ id: 1, input: '' }, | ||
{ id: 2, input: '' }, | ||
{ id: 3, input: '' }, | ||
{ id: 4, input: '' }, | ||
{ id: 5, input: '' }, | ||
]); | ||
const [results, setResults] = useState<SimulationResult[]>([ | ||
{ id: 1, isDone: true, input: '', output: '' }, | ||
{ id: 2, isDone: true, input: '', output: '' }, | ||
{ id: 3, isDone: true, input: '', output: '' }, | ||
{ id: 4, isDone: true, input: '', output: '' }, | ||
{ id: 5, isDone: true, input: '', output: '' }, | ||
]); | ||
const isRunning = useMemo(() => { | ||
return results.some((result) => !result.isDone); | ||
}, [results]); | ||
|
||
useEffect(() => { | ||
return evaluator.subscribe(({ result: output, error, task }) => { | ||
if (!task) return; | ||
|
||
setResults((results) => { | ||
return results.map((result) => { | ||
if (result.id !== task.clientId) return result; | ||
|
||
if (error) { | ||
return { | ||
...result, | ||
isDone: true, | ||
output: `${error.name}: ${error.message} \n${error.stack}`, | ||
}; | ||
} | ||
return { | ||
...result, | ||
isDone: true, | ||
output, | ||
}; | ||
}); | ||
}); | ||
}); | ||
}, []); | ||
|
||
function run(code: string) { | ||
const tasks = inputs.map(({ id, input }) => evaluator.createEvalMessage(id, code, input)); | ||
|
||
const isRequestSuccess = evaluator.evaluate(tasks); | ||
|
||
if (!isRequestSuccess) { | ||
return; | ||
} | ||
|
||
setResults((results) => { | ||
return results | ||
.map((result, index) => ({ | ||
...result, | ||
input: inputs[index].input, | ||
})) | ||
.map(toEvaluatingState); | ||
}); | ||
} | ||
|
||
function changeInputs(inputs: SimulationInput[]) { | ||
setInputs([...inputs]); | ||
} | ||
|
||
function cancel() { | ||
evaluator.cancelEvaluation(); | ||
} | ||
|
||
return { | ||
inputs, | ||
results, | ||
isRunning, | ||
run, | ||
cancel, | ||
changeInputs, | ||
}; | ||
}; | ||
|
||
const toEvaluatingState = (simulation: SimulationResult) => { | ||
return { | ||
...simulation, | ||
output: '계산중...', | ||
isDone: false, | ||
}; | ||
}; |
This file was deleted.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
여기에서 dialog 앞에 $을 붙이신 이유가 뭔가요?:
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
DOM을 나타내는 경우엔 $를 붙여서 구분짓고 있습니다. 개인적인 코딩 습관? 이에요
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
오오 감사합니다