Skip to content

Commit

Permalink
Merge pull request #31 from Suke-H/28-yaml形式の変更
Browse files Browse the repository at this point in the history
#28 yaml形式の変更
  • Loading branch information
Suke-H authored Jan 24, 2025
2 parents 6f5b492 + e554c24 commit 3d1e082
Show file tree
Hide file tree
Showing 9 changed files with 277 additions and 102 deletions.
17 changes: 9 additions & 8 deletions frontend/src/components/editor/cell-type-selector.tsx
Original file line number Diff line number Diff line change
@@ -1,31 +1,32 @@
import { Button } from '@/components/ui/button';
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
import { CellType } from '../types';
import { CELL_TYPES } from '../../constants/cell-types';
import { CellDefinitions } from '../types';
import { CELL_DEFINITIONS } from '../../constants/cell-types';

interface CellTypeSelectorProps {
selectedCellType: CellType;
onCellTypeChange: (type: CellType) => void;
selectedCellType: CellDefinitions;
onCellTypeChange: (type: CellDefinitions) => void;
}

export const CellTypeSelector: React.FC<CellTypeSelectorProps> = ({
selectedCellType,
onCellTypeChange
onCellTypeChange,
}) => {
return (
<Card className="w-full max-w-32 mx-auto bg-[#B3B9D1]">
<CardHeader>
<CardTitle>セル種類</CardTitle>
</CardHeader>
<CardContent className="flex flex-col gap-2">
{(Object.keys(CELL_TYPES) as CellType[]).map((type) => (

{(Object.keys(CELL_DEFINITIONS) as CellDefinitions[]).map((type) => (
<Button
key={type}
variant={selectedCellType === type ? 'default' : 'outline'}
className={`w-full ${CELL_TYPES[type].color} ${CELL_TYPES[type].color === "bg-white" ? 'text-black' : 'text-white'} truncate`} // 文字の省略を防止
className={`w-full ${CELL_DEFINITIONS[type].color} ${CELL_DEFINITIONS[type].color === "bg-white" ? 'text-black' : 'text-white'} truncate`} // 文字の省略を防止
onClick={() => onCellTypeChange(type)}
>
{CELL_TYPES[type].label}
{CELL_DEFINITIONS[type].label}
</Button>
))}
</CardContent>
Expand Down
132 changes: 86 additions & 46 deletions frontend/src/components/editor/grid.tsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,16 @@
import { Button } from '@/components/ui/button';
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
import { Card, CardContent, CardHeader, CardTitle, } from '@/components/ui/card';
import { PlusCircle, MinusCircle, Download, Upload, Link } from 'lucide-react';
import { CellType, Panel, PanelPlacementModeType, PanelPlacementHistoryType } from '../types';
import { CELL_TYPES } from '../../constants/cell-types';
// import { exportStageToJson, importStageFromJson } from '../../utils/json';
import { GridCell, Panel, PanelPlacementModeType, PanelPlacementHistoryType , CellDefinitions} from '../types';
import { CELL_DEFINITIONS, CellSideInfo } from '../../constants/cell-types';
import { exportStageToYaml, importStageFromYaml } from '../../utils/yaml';
import { shareStageUrl } from '../../utils/url';

interface GridProps {
grid: CellType[][];
setGrid: React.Dispatch<React.SetStateAction<CellType[][]>>;
setGridHistory: React.Dispatch<React.SetStateAction<CellType[][][]>>;
selectedCellType: CellType;
grid: GridCell[][];
setGrid: React.Dispatch<React.SetStateAction<GridCell[][]>>;
setGridHistory: React.Dispatch<React.SetStateAction<GridCell[][][]>>;
selectedCellType: CellDefinitions;
panels: Panel[];
setPanels: React.Dispatch<React.SetStateAction<Panel[]>>;
panelPlacementMode: PanelPlacementModeType;
Expand All @@ -35,18 +34,31 @@ export const Grid: React.FC<GridProps> = ({
setGrid((prevGrid) => {
const newRows = prevGrid.length + rowDelta;
const newCols = prevGrid[0].length + colDelta;

if (newRows > 0 && newCols > 0) {
// 新しい空のセルを作成するヘルパー関数
const createEmptyCell = (): GridCell => ({
type: 'Normal',
side: 'front'
});

if (rowDelta > 0) {
return [...prevGrid, ...Array(rowDelta).fill(Array(newCols).fill('White'))];
// 行を追加
const newRow = Array(newCols).fill(null).map(() => createEmptyCell());
return [...prevGrid, ...Array(rowDelta).fill(null).map(() => newRow.map(cell => ({...cell})))];
} else if (rowDelta < 0) {
return prevGrid.slice(0, newRows).map((row) => row.slice(0, newCols));
// 行を削除
return prevGrid.slice(0, newRows).map(row => row.slice(0, newCols));
}

return prevGrid.map((row) => {

// 列の追加/削除
return prevGrid.map(row => {
if (colDelta > 0) {
return [...row, ...Array(colDelta).fill('White')];
// 列を追加
const newCells = Array(colDelta).fill(null).map(() => createEmptyCell());
return [...row, ...newCells];
} else if (colDelta < 0) {
// 列を削除
return row.slice(0, newCols);
}
return row;
Expand All @@ -57,14 +69,34 @@ export const Grid: React.FC<GridProps> = ({
};

const handleGridCellClick = (rowIndex: number, colIndex: number) => {
// 通常のセル選択モード
// セル選択モード
if (!panelPlacementMode.panel) {
const newGrid = [...grid];
newGrid[rowIndex][colIndex] = selectedCellType;


const cellDef = CELL_DEFINITIONS[selectedCellType];

// セル選択がFlipの場合:sideを反転
if (selectedCellType === 'Flip') {
const targetCell = newGrid[rowIndex][colIndex];
if (targetCell.type === 'Empty') return;

if (targetCell.side === 'front') {
newGrid[rowIndex][colIndex] = { ...targetCell, side: 'back' };
} else if (targetCell.side === 'back') {
newGrid[rowIndex][colIndex] = { ...targetCell, side: 'front' };
}
}

else {
// 基本はfront(表)で設置する。neutralのみの場合はneutral
const side = 'neutral' in cellDef ? 'neutral' : 'front';
newGrid[rowIndex][colIndex] = { type: selectedCellType, side };
}

setGrid(newGrid);
setGridHistory((prev) => [...prev, newGrid]);
return;

}

// パネル配置モード
Expand All @@ -80,21 +112,17 @@ export const Grid: React.FC<GridProps> = ({
for (let j = 0; j < panelCols; j++) {
if (placingPanel.cells[i][j] === 'Black') {
const targetCell = updatedGrid[rowIndex + i][colIndex + j];

// 矢印セルの向きを反転
if (targetCell.startsWith('Arrow')) {
if (targetCell === 'ArrowUp') updatedGrid[rowIndex + i][colIndex + j] = 'ArrowDown';
else if (targetCell === 'ArrowDown') updatedGrid[rowIndex + i][colIndex + j] = 'ArrowUp';
else if (targetCell === 'ArrowLeft') updatedGrid[rowIndex + i][colIndex + j] = 'ArrowRight';
else if (targetCell === 'ArrowRight') updatedGrid[rowIndex + i][colIndex + j] = 'ArrowLeft';
}

// 白黒反転
if (targetCell === 'White') {
updatedGrid[rowIndex + i][colIndex + j] = 'Black';
} else if (targetCell === 'Black') {
updatedGrid[rowIndex + i][colIndex + j] = 'White';

// Emptyには置かない
if (targetCell.type === 'Empty') continue;

// セルの状態を切り替える
if (targetCell.side === 'front') {
updatedGrid[rowIndex + i][colIndex + j] = { ...targetCell, side: 'back' };
} else if (targetCell.side === 'back') {
updatedGrid[rowIndex + i][colIndex + j] = { ...targetCell, side: 'front' };
}
// neutralの場合は変更しない
}
}
}
Expand All @@ -110,19 +138,27 @@ export const Grid: React.FC<GridProps> = ({
});
};

const renderGridCell = (cellType: CellType, rowIndex: number, colIndex: number) => {
const isEmpty = cellType === 'Empty';

const renderGridCell = (cell: GridCell, rowIndex: number, colIndex: number) => {
const cellDef = CELL_DEFINITIONS[cell.type];

// セルの状態に応じた情報を取得
const sideInfo: CellSideInfo | undefined = cellDef[cell.side];

if (!sideInfo) {
console.error(`No valid sideInfo found for cell type: ${cell.type}`);
return null; // またはデフォルトの要素を返す
}

return (
<div
key={`${rowIndex}-${colIndex}`}
className={`h-10 w-10 flex items-center justify-center ${isEmpty ? '' : 'border'}`}
className={`h-10 w-10 flex items-center justify-center ${cell.type === 'Empty' ? '' : 'border'}`}
onClick={() => handleGridCellClick(rowIndex, colIndex)}
>
{!isEmpty && (
{cell.type !== 'Empty' && (
<img
src={CELL_TYPES[cellType].imagePath}
alt={CELL_TYPES[cellType].label}
src={`/cells/${sideInfo.picture}`}
alt={`${cellDef.label} (${cell.side})`}
className="w-full h-full object-contain"
/>
)}
Expand All @@ -131,7 +167,7 @@ export const Grid: React.FC<GridProps> = ({
};

const canPlacePanelAtLocation = (
grid: CellType[][],
grid: GridCell[][],
rowIndex: number,
colIndex: number,
panel: Panel
Expand All @@ -147,15 +183,20 @@ export const Grid: React.FC<GridProps> = ({
// パネルを配置するセルがすべて適切であるかチェック
for (let i = 0; i < panelRows; i++) {
for (let j = 0; j < panelCols; j++) {
if (panel.cells[i][j] === 'Black') {
const panelCell = panel.cells[i][j];
if (panelCell !== 'Empty') {
const targetCell = grid[rowIndex + i][colIndex + j];
if (
targetCell !== 'White' &&
targetCell !== 'Black' &&
!targetCell.startsWith('Arrow')
) {

// Emptyには置けない
if (targetCell.type === 'Empty') {
return false;
}

// // neutralなセルには置けない(開始、ゴール、ダミーゴールなど)
// const cellDef = CELL_DEFINITIONS[targetCell.type];
// if ('neutral' in cellDef) {
// return false;
// }
}
}
}
Expand All @@ -169,7 +210,6 @@ export const Grid: React.FC<GridProps> = ({
input.click();
}
};


return (
<Card className="flex-grow bg-[#B3B9D1]">
Expand Down
8 changes: 4 additions & 4 deletions frontend/src/components/editor/panel-list.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Button } from '@/components/ui/button';
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
import { Trash2, Move } from 'lucide-react';
import { CellType, Panel, PanelPlacementModeType, PanelPlacementHistoryType } from '../types';
import { Panel, PanelPlacementModeType, PanelPlacementHistoryType, GridCell } from '../types';

interface PanelListProps {
panels: Panel[];
Expand All @@ -10,9 +10,9 @@ interface PanelListProps {
setPanelPlacementMode: React.Dispatch<React.SetStateAction<PanelPlacementModeType>>;
panelPlacementHistory: PanelPlacementHistoryType[];
setPanelPlacementHistory: React.Dispatch<React.SetStateAction<PanelPlacementHistoryType[]>>;
setGrid: React.Dispatch<React.SetStateAction<CellType[][]>>;
gridHistory: CellType[][][];
setGridHistory: React.Dispatch<React.SetStateAction<CellType[][][]>>;
setGrid: React.Dispatch<React.SetStateAction<GridCell[][]>>;
gridHistory: GridCell[][][];
setGridHistory: React.Dispatch<React.SetStateAction<GridCell[][][]>>;
}

export const PanelList: React.FC<PanelListProps> = ({
Expand Down
21 changes: 13 additions & 8 deletions frontend/src/components/types.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,23 @@
import { CELL_TYPES } from "../constants/cell-types";
import { CELL_TYPES, CELL_DEFINITIONS } from "../constants/cell-types";

export type CellType = keyof typeof CELL_TYPES;
export type CellType = keyof typeof CELL_TYPES
export type CellDefinitions = keyof typeof CELL_DEFINITIONS;

// グリッド上のセルを表す型
export type GridCell = {
type: CellDefinitions;
side: 'neutral' | 'front' | 'back';
};

// パネルの型を更新
export interface Panel {
id: string;
cells: CellType[][];
}

export interface PanelPlacementModeType {
export type PanelPlacementModeType = {
panel: Panel | null;
highlightedCell: { row: number; col: number } | null;
}
};

export interface PanelPlacementHistoryType {
panel: Panel | null;
highlightedCell: { row: number; col: number } | null;
}
export type PanelPlacementHistoryType = PanelPlacementModeType;
84 changes: 83 additions & 1 deletion frontend/src/constants/cell-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,86 @@ export const CELL_TYPES = {
} as const;

// キーから動的に型を生成
export type CellType = keyof typeof CELL_TYPES;
export type CellType = keyof typeof CELL_TYPES;
// export type CellType = 'Empty' | 'White' | 'Black' | 'Start' | 'Goal' | 'DummyGoal' | 'Crow' | 'ArrowUp' | 'ArrowDown' | 'ArrowLeft' | 'ArrowRight' | 'Normal';

// セルの状態を表す型
export type CellSideInfo = {
code: string;
picture: string;
};

// セルの定義を表す型
export type CellDefinition = {
label: string;
color: string;
neutral?: CellSideInfo;
front?: CellSideInfo;
back?: CellSideInfo;
};

// セルの種類を定義
export const CELL_DEFINITIONS: Record<string, CellDefinition> = {
Flip: {
label: '反転',
color: 'bg-black',
},
Empty: {
label: '空',
color: 'bg-white',
neutral: {
code: 'e',
picture: 'empty.png'
}
},
Normal: {
label: '通常床',
color: 'bg-[#DAE0EA]',
front: {
code: 'w',
picture: 'white.png'
},
back: {
code: 'b',
picture: 'black.png'
}
},
Start: {
label: 'スタート',
color: 'bg-green-500',
neutral: {
code: 's',
picture: 'start.png'
}
},
Goal: {
label: 'ゴール',
color: 'bg-blue-500',
neutral: {
code: 'g',
picture: 'goal.png'
}
},
DummyGoal: {
label: 'ダミーゴール',
color: 'bg-red-500',
neutral: {
code: 'd',
picture: 'dummy-goal.png'
}
},
Crow: {
label: 'カラス',
color: 'bg-yellow-500',
front: {
code: 'c',
picture: 'crow.png'
},
back: {
code: 'C',
picture: 'black.png'
}
},
} as const;

export type CellDefinitions = keyof typeof CELL_DEFINITIONS;
Loading

0 comments on commit 3d1e082

Please sign in to comment.