Skip to content

Commit

Permalink
feat: 変数情報を操作盤面の下に移動する (#157)
Browse files Browse the repository at this point in the history
Co-authored-by: Sakamoto, Kazunori <[email protected]>
Co-authored-by: Sakamoto, Kazunori <[email protected]>
  • Loading branch information
3 people authored Sep 26, 2024
1 parent 543e9e7 commit 0dc1438
Show file tree
Hide file tree
Showing 4 changed files with 109 additions and 92 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import {
HStack,
Icon,
IconButton,
Spacer,
VStack,
} from '../../../../../../../../infrastructures/useClient/chakra';
import type { Problem } from '../../../../../../../../problems/generateProblem';
Expand All @@ -33,15 +34,15 @@ interface TurtleGraphicsProps {
problem: Problem;
currentTraceItemIndex: number;
previousTraceItemIndex: number;
handleClickSubmitButton: () => Promise<void>;
}

export interface TurtleGraphicsHandle {
initialize(): void;
isCorrect(): boolean;
}

export const BoardEditor = forwardRef<TurtleGraphicsHandle, TurtleGraphicsProps>(
({ currentTraceItemIndex, previousTraceItemIndex, problem }, ref) => {
({ currentTraceItemIndex, handleClickSubmitButton, previousTraceItemIndex, problem }, ref) => {
const [board, updateBoard] = useImmer<ColorChar[][]>([]);
const [turtles, updateTurtles] = useImmer<TurtleTrace[]>([]);
const [selectedCell, setSelectedCell] = useState<SelectedCell>();
Expand All @@ -51,7 +52,6 @@ export const BoardEditor = forwardRef<TurtleGraphicsHandle, TurtleGraphicsProps>

const initialize = useCallback(
(keepSelectedCell = false): void => {
console.log('initialize:', problem, previousTraceItem);
const initialBoard = parseBoard(previousTraceItem.board);
updateBoard(initialBoard);
updateTurtles(Object.values(previousTraceItem.vars ?? {}).filter(isTurtleTrace));
Expand All @@ -61,7 +61,6 @@ export const BoardEditor = forwardRef<TurtleGraphicsHandle, TurtleGraphicsProps>
);

useImperativeHandle(ref, () => ({
initialize,
isCorrect,
}));

Expand Down Expand Up @@ -158,9 +157,11 @@ export const BoardEditor = forwardRef<TurtleGraphicsHandle, TurtleGraphicsProps>

const selectedPosition = selectedCell ? { x: selectedCell.x, y: selectedCell.y } : undefined;

useShortcutKeys(handleClickSubmitButton);

return (
<HStack align="stretch" bgColor="gray.50" overflow="hidden" rounded="md">
<Center flexBasis={0} flexGrow={2} minW={0} px={4} py={16}>
<Center flexBasis={0} flexGrow={2} minW={0} px={4} py={20}>
<BoardViewer
enableTransitions
board={board.map((cells) => cells.join('')).join('\n')}
Expand Down Expand Up @@ -263,12 +264,30 @@ export const BoardEditor = forwardRef<TurtleGraphicsHandle, TurtleGraphicsProps>
</Box>
)}
</VStack>
<Spacer />
<Button colorScheme="brand" variant="outline" onClick={() => initialize()}>
盤面をリセット
</Button>

<Button
colorScheme="brand"
rightIcon={
<Box as="span" color="whiteAlpha.800" fontSize="sm" fontWeight="bold">
(Enter)
</Box>
}
onClick={() => handleClickSubmitButton()}
>
提出
</Button>
</VStack>
</HStack>
);
}
);

BoardEditor.displayName = 'BoardEditor';

function canPutTurtle(turtlesTraces: TurtleTrace[], x: number, y: number): boolean {
return 0 <= x && x < COLUMNS && 0 <= y && y < ROWS && !turtlesTraces.some((t) => t.x === x && t.y === y);
}
Expand All @@ -281,4 +300,16 @@ function parseBoard(boardString: string): ColorChar[][] {
.map((line) => [...line.trim()]) as ColorChar[][];
}

BoardEditor.displayName = 'BoardEditor';
function useShortcutKeys(handleClickAnswerButton: () => Promise<void>): void {
useEffect(() => {
const handleKeyDown = (event: KeyboardEvent): void => {
if (event.key === 'Enter') {
event.preventDefault();
void handleClickAnswerButton();
}
};
window.addEventListener('keydown', handleKeyDown);
return () => window.removeEventListener('keydown', handleKeyDown);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import {
Box,
Button,
Card,
Flex,
Heading,
HStack,
Tag,
Expand Down Expand Up @@ -131,46 +132,48 @@ export const ProblemBody: React.FC<Props> = (props) => {

return (
<>
<VStack align="stretch" flexBasis={0} flexGrow={1} minW={0} spacing={4}>
<VStack align="stretch" as={Card} overflow="hidden" spacing={0}>
<VStack align="stretch" borderBottomWidth="1px" p={5}>
<HStack justifyContent="space-between">
<Heading size="md">問題</Heading>
{problemType === 'step' && (
<Tag colorScheme="brand" fontWeight="bold" size="sm" variant="solid">
ステップ実行モード
</Tag>
)}
{problemType === 'executionResult' && (
<Tooltip label="減点になりますが、確実に問題を解けます。">
<Button
colorScheme="brand"
variant="outline"
onClick={() => {
void props.updateProblemSession('step', 1);
}}
>
諦めてステップ実行モードに移る
</Button>
</Tooltip>
)}
</HStack>

<Box>
<Box as="span" fontWeight="bold">
{problemType === 'executionResult' ? (
'プログラムを実行した後'
) : (
<>
<Box as="span" border="2px solid #f56565" px={0.5} rounded="sm">
{props.problem.sidToLineIndex.get(props.problem.traceItems[currentTraceItemIndex].sid)}行目
</Box>
を実行した後
</>
<Flex gap={6}>
<VStack align="stretch" flexBasis={0} flexGrow={1} minW={0} spacing={4}>
<VStack align="stretch" as={Card} overflow="hidden" spacing={0}>
<VStack align="stretch" borderBottomWidth="1px" p={5}>
<HStack justifyContent="space-between">
<Heading size="md">問題</Heading>
{problemType === 'step' && (
<Tag colorScheme="brand" fontWeight="bold" size="sm" variant="solid">
ステップ実行モード
</Tag>
)}
{problemType === 'executionResult' && (
<Tooltip label="減点になりますが、確実に問題を解けます。">
<Button
colorScheme="brand"
variant="outline"
onClick={() => {
void props.updateProblemSession('step', 1);
}}
>
諦めてステップ実行モードに移る
</Button>
</Tooltip>
)}
</HStack>

<Box>
<Box as="span" fontWeight="bold">
{problemType === 'executionResult' ? (
'プログラムを実行した後'
) : (
<>
<Box as="span" border="2px solid #f56565" px={0.5} rounded="sm">
{props.problem.sidToLineIndex.get(props.problem.traceItems[currentTraceItemIndex].sid)}行目
</Box>
を実行した後
</>
)}
</Box>
の盤面を作成し、提出ボタンを押してください。
</Box>
の盤面を作成し、提出ボタンを押してください。
</Box>
</VStack>
</VStack>

<SyntaxHighlighter
Expand All @@ -189,12 +192,23 @@ export const ProblemBody: React.FC<Props> = (props) => {
/>
</VStack>

{problemType !== 'executionResult' &&
props.problem.sidToLineIndex.get(props.problem.traceItems[previousTraceItemIndex].sid) && (
<VStack align="stretch" as={Card} bg="gray.50" p={5} spacing={6}>
<VStack align="stretch" flexBasis={0} flexGrow={1} spacing="4">
<BoardEditor
ref={turtleGraphicsRef}
currentTraceItemIndex={currentTraceItemIndex}
handleClickSubmitButton={handleClickSubmitButton}
previousTraceItemIndex={previousTraceItemIndex}
problem={props.problem}
/>
</VStack>
</Flex>

{problemType !== 'executionResult' &&
props.problem.sidToLineIndex.get(props.problem.traceItems[previousTraceItemIndex].sid) && (
<HStack alignItems="flex-start" as={Card} bg="gray.50" p={5}>
<VStack align="stretch" flexBasis={0} flexGrow={2} spacing={6}>
<VStack align="stretch">
<Heading size="md">
参考:
<Box as="span" bgColor="orange.100" px={0.5} rounded="sm">
{props.problem.sidToLineIndex.get(props.problem.traceItems[previousTraceItemIndex].sid)}行目
</Box>
Expand All @@ -213,38 +227,12 @@ export const ProblemBody: React.FC<Props> = (props) => {
isTurtleTrace
)}
/>

<Variables traceItemVars={props.problem.traceItems[previousTraceItemIndex].vars} />
</VStack>
)}
</VStack>

<VStack align="stretch" flexBasis={0} flexGrow={1} spacing="4">
<BoardEditor
ref={turtleGraphicsRef}
currentTraceItemIndex={currentTraceItemIndex}
previousTraceItemIndex={previousTraceItemIndex}
problem={props.problem}
/>

<HStack justify="space-between">
<Button colorScheme="brand" variant="outline" onClick={() => turtleGraphicsRef.current?.initialize()}>
盤面をリセット
</Button>

<Button
colorScheme="brand"
rightIcon={
<Box as="span" color="whiteAlpha.800" fontSize="sm" fontWeight="bold">
(Enter)
</Box>
}
onClick={() => handleClickSubmitButton()}
>
提出
</Button>
</HStack>
</VStack>
<Box flexBasis={0} flexGrow={3} pt={8}>
<Variables traceItemVars={props.problem.traceItems[currentTraceItemIndex].vars} />
</Box>
</HStack>
)}

<AlertDialog
closeOnEsc={true}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,19 +39,20 @@ export const Variables: React.FC<VariablesProps> = ({ traceItemVars }) => {

return (
<HStack align="flex-start" divider={<StackDivider />} mx={-5}>
<TableContainer flexBasis={0} flexGrow={1} maxW="unset">
<TableContainer flexBasis={0} flexGrow={1} maxW="unset" overflowX="visible">
<Table>
<Thead>
<Tr>
<Th>タートルの変数名</Th>
<Th w="0">線の色</Th>
<Th w="0">向き</Th>
<Th w="0">線の色</Th>
</Tr>
</Thead>
<Tbody>
{characterVars?.map((variable) => (
<Tr key={variable.key}>
<Td fontFamily="mono">{variable.key}</Td>
<Td>{dirCharToJapanese[variable.value.dir as keyof typeof dirCharToJapanese]}</Td>
<Td py={0}>
<Box
bg={charToColor[variable.value.color as keyof typeof charToColor]}
Expand All @@ -60,7 +61,6 @@ export const Variables: React.FC<VariablesProps> = ({ traceItemVars }) => {
w="1.5em"
/>
</Td>
<Td>{dirCharToJapanese[variable.value.dir as keyof typeof dirCharToJapanese]}</Td>
</Tr>
))}
</Tbody>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import {
MIN_INTERVAL_MS_OF_ACTIVE_EVENTS,
} from '../../../../../../../../constants';
import { backendTrpcReact } from '../../../../../../../../infrastructures/trpcBackend/client';
import { Flex, Heading, HStack, Link, Text, VStack } from '../../../../../../../../infrastructures/useClient/chakra';
import { Heading, HStack, Link, Text, VStack } from '../../../../../../../../infrastructures/useClient/chakra';
import type { Problem } from '../../../../../../../../problems/generateProblem';
import type { CourseId, ProblemId } from '../../../../../../../../problems/problemData';
import { courseIdToLectureIds, courseIdToName, problemIdToName } from '../../../../../../../../problems/problemData';
Expand Down Expand Up @@ -79,15 +79,13 @@ export const ProblemPageOnClient: React.FC<Props> = (props) => {
<Heading as="h1">{problemIdToName[props.params.problemId]}</Heading>
</VStack>

<Flex gap={6}>
<ProblemBody
createSubmissionUpdatingProblemSession={createSubmissionUpdatingProblemSession}
params={props.params}
problem={props.problem}
problemSession={problemSession}
updateProblemSession={updateProblemSession}
/>
</Flex>
<ProblemBody
createSubmissionUpdatingProblemSession={createSubmissionUpdatingProblemSession}
params={props.params}
problem={props.problem}
problemSession={problemSession}
updateProblemSession={updateProblemSession}
/>
</VStack>
);
};
Expand Down

0 comments on commit 0dc1438

Please sign in to comment.