From f60926b9a603fc288d8624dc0d3739b35dadaac2 Mon Sep 17 00:00:00 2001 From: atomisu0312 Date: Tue, 29 Jul 2025 21:43:08 +0900 Subject: [PATCH 1/3] =?UTF-8?q?=E3=83=84=E3=83=BC=E3=83=AB=E7=B3=BB?= =?UTF-8?q?=E3=81=AEState=E3=82=92Jotai=E3=81=AB=E7=BD=AE=E3=81=8D?= =?UTF-8?q?=E6=8F=9B=E3=81=88=20#264?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Jotaiライブラリのインストール - toolやtoolに関するアクションをpropから除外し、Jotaiライブラリが管理するように修正 - front/src/store/event/venueedit/tool.tsでStateやdispatchを管理し、useSubToolSelectionフック経由で各コンポーネントから状態を取得&操作できるように - isTextTool(selectedTool)のような冗長な関数呼び出しではなく、変数としてそのまま使えるように(isDrawingToolやisPixelToolも同様) --- front/package-lock.json | 21 +++++ front/package.json | 1 + .../event/venueedit/palette/ColorPalette.tsx | 14 +-- .../venueedit/palette/ColorSingleSelector.tsx | 16 +--- .../venueedit/palette/ToggleToEraseButton.tsx | 7 +- .../palette/ToggleToGenerateButton.tsx | 13 +-- .../event/venueedit/tools/ToolButtonGroup.tsx | 10 +- .../event/venueedit/VenueEditorTemplate.tsx | 17 +--- .../venueedit/VenueToolSelectionMenu.tsx | 20 ++-- .../event/venueedit/VenueToolSubMenu.tsx | 57 ++++-------- .../event/venueedit/tool/useToolSelect.ts | 31 +++---- .../hooks/event/venueedit/useCanvasDraw.ts | 26 ++---- .../event/venueedit/useSubToolSelection.ts | 91 ++++++++++--------- front/src/store/event/venueedit/tool.ts | 46 ++++++++++ 14 files changed, 184 insertions(+), 186 deletions(-) create mode 100644 front/src/store/event/venueedit/tool.ts diff --git a/front/package-lock.json b/front/package-lock.json index d9eb71f..024a18d 100644 --- a/front/package-lock.json +++ b/front/package-lock.json @@ -36,6 +36,7 @@ "date-fns-tz": "^3.2.0", "dotenv": "^16.5.0", "drizzle-orm": "^0.44.0", + "jotai": "^2.12.5", "lucide-react": "^0.511.0", "md5": "^2.3.0", "next": "15.3.2", @@ -6453,6 +6454,26 @@ "url": "https://github.com/sponsors/panva" } }, + "node_modules/jotai": { + "version": "2.12.5", + "resolved": "https://registry.npmjs.org/jotai/-/jotai-2.12.5.tgz", + "integrity": "sha512-G8m32HW3lSmcz/4mbqx0hgJIQ0ekndKWiYP7kWVKi0p6saLXdSoye+FZiOFyonnd7Q482LCzm8sMDl7Ar1NWDw==", + "engines": { + "node": ">=12.20.0" + }, + "peerDependencies": { + "@types/react": ">=17.0.0", + "react": ">=17.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "react": { + "optional": true + } + } + }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", diff --git a/front/package.json b/front/package.json index 9f21bd2..7b9b93a 100644 --- a/front/package.json +++ b/front/package.json @@ -38,6 +38,7 @@ "date-fns-tz": "^3.2.0", "dotenv": "^16.5.0", "drizzle-orm": "^0.44.0", + "jotai": "^2.12.5", "lucide-react": "^0.511.0", "md5": "^2.3.0", "next": "15.3.2", diff --git a/front/src/components/organisms/event/venueedit/palette/ColorPalette.tsx b/front/src/components/organisms/event/venueedit/palette/ColorPalette.tsx index 7732c3b..03dda2d 100644 --- a/front/src/components/organisms/event/venueedit/palette/ColorPalette.tsx +++ b/front/src/components/organisms/event/venueedit/palette/ColorPalette.tsx @@ -2,10 +2,9 @@ import ColorAddButton from '@/components/organisms/event/venueedit/palette/ColorAddButton' import ColorButton from '@/components/organisms/event/venueedit/palette/ColorButton' -import { VenueEditTool } from '@/types/tool' -import { isDrawingTool } from '@/lib/event/venueedit/subToolSelection' import { Color } from 'react-color' import ToggleToEraseButton from '@/components/organisms/event/venueedit/palette/ToggleToEraseButton' +import { useSubToolSelection } from '@/hooks/event/venueedit/useSubToolSelection' interface ColorPaletteProps { colors: Color[] @@ -13,9 +12,6 @@ interface ColorPaletteProps { onColorSelect: (color: Color) => void onAddColor?: (color: Color) => void onDeleteColor: (color: Color) => void - selectedTool: VenueEditTool - toggleToGenerateTool: () => void - toggleToEraseTool : () => void } export default function ColorPalette({ @@ -24,10 +20,8 @@ export default function ColorPalette({ onColorSelect, onAddColor, onDeleteColor, - selectedTool, - toggleToGenerateTool, - toggleToEraseTool }: Readonly) { + const { toggleToGenerateTool, isDrawingTool } = useSubToolSelection() return (
@@ -35,13 +29,13 @@ export default function ColorPalette({ {onColorSelect(color); toggleToGenerateTool()}} // 色の変更とツールを変更を同時に行う onDelete={() => onDeleteColor(color)} /> ))} {onAddColor && } - {toggleToEraseTool && } + {}
) } \ No newline at end of file diff --git a/front/src/components/organisms/event/venueedit/palette/ColorSingleSelector.tsx b/front/src/components/organisms/event/venueedit/palette/ColorSingleSelector.tsx index 7349df6..d407350 100644 --- a/front/src/components/organisms/event/venueedit/palette/ColorSingleSelector.tsx +++ b/front/src/components/organisms/event/venueedit/palette/ColorSingleSelector.tsx @@ -5,17 +5,13 @@ import { useState } from 'react' import ColorPickerDialog from '@/components/organisms/event/venueedit/palette/ColorPickerDialog' import ToggleToGenerateButton from '@/components/organisms/event/venueedit/palette/ToggleToGenerateButton' import ToggleToEraseButton from '@/components/organisms/event/venueedit/palette/ToggleToEraseButton' -import { VenueEditTool } from '@/types/tool' -import { isDrawingTool } from '@/lib/event/venueedit/subToolSelection' +import { useSubToolSelection } from '@/hooks/event/venueedit/useSubToolSelection' interface ColorSingleSelectorProps { selectedColor: Color selectedColorBackGround: Color setSelectedColor: (color: Color) => void setSelectedColorBackGround: (color: Color) => void - toggleToGenerateTool: () => void - toggleToEraseTool: () => void - selectedTool: VenueEditTool } export default function ColorSingleSelector({ @@ -23,10 +19,8 @@ export default function ColorSingleSelector({ selectedColorBackGround, setSelectedColor, setSelectedColorBackGround, - toggleToGenerateTool, - toggleToEraseTool, - selectedTool }: Readonly) { + const { isDrawingTool } = useSubToolSelection() const [isTextColorOpen, setIsTextColorOpen] = useState(false) const [isBackgroundColorOpen, setIsBackgroundColorOpen] = useState(false) @@ -42,14 +36,12 @@ export default function ColorSingleSelector({ <>
diff --git a/front/src/components/organisms/event/venueedit/palette/ToggleToEraseButton.tsx b/front/src/components/organisms/event/venueedit/palette/ToggleToEraseButton.tsx index 48cf5f7..7211e81 100644 --- a/front/src/components/organisms/event/venueedit/palette/ToggleToEraseButton.tsx +++ b/front/src/components/organisms/event/venueedit/palette/ToggleToEraseButton.tsx @@ -1,6 +1,7 @@ 'use client' import XButton from '@/components/molecules/button/XButton' +import { useSubToolSelection } from '@/hooks/event/venueedit/useSubToolSelection' /** * 削除系のツールに切り替えるボタンコンポーネント @@ -26,15 +27,12 @@ import XButton from '@/components/molecules/button/XButton' * /> * ``` * - * @param toggleToEraseTool - ボタンクリック時に実行されるコールバック関数 * @param disabled - ボタンを無効化するかどうか * @param isSelected - ボタンが選択されているかどうか * @param className - 追加のクラス名 * @returns バツ記号を表示するボタンコンポーネント */ interface ToggleToEraseButtonProps { - /** ボタンクリック時に実行されるコールバック関数 */ - toggleToEraseTool: () => void /** ボタンを無効化するかどうか */ disabled?: boolean /** ボタンが選択されているかどうか */ @@ -43,7 +41,8 @@ interface ToggleToEraseButtonProps { className?: string } -export default function ToggleToEraseButton({ toggleToEraseTool, disabled, isSelected, className }: Readonly) { +export default function ToggleToEraseButton({ disabled, isSelected, className }: Readonly) { + const { toggleToEraseTool } = useSubToolSelection() return ( console.log('text add selected')} * disabled={false} * isSelected={true} * className="w-8 h-8" * /> * ``` * - * @param onClick - ボタンクリック時に実行されるコールバック関数 * @param disabled - ボタンを無効化するかどうか * @param isSelected - ボタンが選択されているかどうか * @param className - ボタンに適用する追加のクラス名 * @returns テキスト追加選択ボタンコンポーネント */ interface ToggleToGenerateButtonProps { - /** ボタンクリック時に実行されるコールバック関数 */ - onClick: () => void /** ボタンを無効化するかどうか */ disabled?: boolean /** ボタンが選択されているかどうか */ @@ -42,28 +39,26 @@ interface ToggleToGenerateButtonProps { * 使用例: * ```tsx * console.log('text add selected')} * disabled={false} * isSelected={true} * className="w-8 h-8" * /> * ``` * - * @param onClick - ボタンクリック時に実行されるコールバック関数 * @param disabled - ボタンを無効化するかどうか * @param isSelected - ボタンが選択されているかどうか * @param className - ボタンに適用する追加のクラス名 * @returns テキスト追加選択ボタンコンポーネント */ -export default function ToggleToGenerateButton({ - onClick, +export default function ToggleToGenerateButton({ disabled, isSelected, className }: Readonly) { + const { toggleToGenerateTool } = useSubToolSelection() return ( diff --git a/front/src/components/organisms/event/venueedit/tools/ToolButtonGroup.tsx b/front/src/components/organisms/event/venueedit/tools/ToolButtonGroup.tsx index e36f4e1..aac6e23 100644 --- a/front/src/components/organisms/event/venueedit/tools/ToolButtonGroup.tsx +++ b/front/src/components/organisms/event/venueedit/tools/ToolButtonGroup.tsx @@ -1,20 +1,16 @@ import { EDITOR_TOOL_PAIR_TREE, EditorToolPairKey, VenueEditTool } from '@/types/tool' import MonoClomeButton from '@/components/atoms/button/MonoClomeButton' import { useCallback } from 'react' +import { useSubToolSelection } from '@/hooks/event/venueedit/useSubToolSelection' -interface ToolButtonGroupProps { - selectedTool: VenueEditTool - setDrawToolFromToolKey: (toolKey: keyof typeof EDITOR_TOOL_PAIR_TREE) => void -} /** * ツールボタングループ * - * @param selectedTool 現在選択されているツール - * @param setDrawToolFromToolKey ツール選択時のコールバック * @returns ツールボタングループ */ -export default function ToolButtonGroup({ selectedTool, setDrawToolFromToolKey }: Readonly) { +export default function ToolButtonGroup() { + const { selectedTool, setDrawToolFromToolKey } = useSubToolSelection() // 現在選択されているツールがどのツールタイプに属するかを求める関数 const isSelected = useCallback((toolKey: EditorToolPairKey) => { diff --git a/front/src/components/templates/event/venueedit/VenueEditorTemplate.tsx b/front/src/components/templates/event/venueedit/VenueEditorTemplate.tsx index 36be12d..6774005 100644 --- a/front/src/components/templates/event/venueedit/VenueEditorTemplate.tsx +++ b/front/src/components/templates/event/venueedit/VenueEditorTemplate.tsx @@ -8,7 +8,6 @@ import { useColorPalette } from '@/hooks/event/venueedit/submenu/useColorPalette import { useCanvasDraw } from '@/hooks/event/venueedit/useCanvasDraw' import TextDialog from '@/components/organisms/event/venueedit/text/TextDialog' import { EventVenueEditViewModel } from '@/types/event/viewmodel' -import { useSubToolSelection } from '@/hooks/event/venueedit/useSubToolSelection' interface Props { eventVenueEditViewModel: EventVenueEditViewModel @@ -20,13 +19,6 @@ interface Props { * @param eventVenueEditViewModel 会場編集ビューモデル */ export default function VenueEditorTemplate({ eventVenueEditViewModel }: Readonly) { - const { - selectedTool, - toggleToGenerateTool, - toggleToEraseTool, - setDrawToolFromToolKey - } = useSubToolSelection() - const { selectedColor, setSelectedColor, @@ -56,7 +48,6 @@ export default function VenueEditorTemplate({ eventVenueEditViewModel }: Readonl isPendingForSave } = useCanvasDraw({ selectedColor, - selectedTool, selectedColorBackGround, eventVenueEditViewModel, }) @@ -85,9 +76,6 @@ export default function VenueEditorTemplate({ eventVenueEditViewModel }: Readonl
- +
diff --git a/front/src/components/templates/event/venueedit/VenueToolSelectionMenu.tsx b/front/src/components/templates/event/venueedit/VenueToolSelectionMenu.tsx index 1772dfc..c012dcc 100644 --- a/front/src/components/templates/event/venueedit/VenueToolSelectionMenu.tsx +++ b/front/src/components/templates/event/venueedit/VenueToolSelectionMenu.tsx @@ -1,27 +1,19 @@ -import React from 'react' -import {EditorToolPairKey, VenueEditTool } from '@/types/tool' -import ToolButtonGroup from '@/components/organisms/event/venueedit/tools/ToolButtonGroup' - -interface VenueToolSelectionMenuProps { - selectedTool: VenueEditTool - setDrawToolFromToolKey: (toolKey: EditorToolPairKey) => void -} +import React from 'react'; +import ToolButtonGroup from '@/components/organisms/event/venueedit/tools/ToolButtonGroup'; /** * ツール選択メニュー - * - * @param selectedTool 現在選択されているツール - * @param setDrawToolFromToolKey ツール選択時のコールバック + * * @returns ツール選択メニュー */ -export default function VenueToolSelectionMenu({ selectedTool, setDrawToolFromToolKey }: Readonly) { +export default function VenueToolSelectionMenu() { return (
- +
- ) + ); } \ No newline at end of file diff --git a/front/src/components/templates/event/venueedit/VenueToolSubMenu.tsx b/front/src/components/templates/event/venueedit/VenueToolSubMenu.tsx index bdb6b6c..00d34e8 100644 --- a/front/src/components/templates/event/venueedit/VenueToolSubMenu.tsx +++ b/front/src/components/templates/event/venueedit/VenueToolSubMenu.tsx @@ -1,36 +1,21 @@ -import React, { useMemo } from 'react' -import ColorPalette from '@/components/organisms/event/venueedit/palette/ColorPalette' -import { Color } from 'react-color' -import { PIXEL_TOOLS, VenueEditTool, PixelTool, TEXT_TOOLS, TextTool } from '@/types/tool' -import ColorSingleSelector from '@/components/organisms/event/venueedit/palette/ColorSingleSelector' -import { getToolType } from '@/lib/event/venueedit/subToolSelection' -interface VenueToolSubMenuProps { - selectedTool: VenueEditTool - toggleToGenerateTool: () => void - toggleToEraseTool: () => void - selectedColor: Color - setSelectedColor: (color: Color) => void - selectedColorBackGround: Color - setSelectedColorBackGround: (color: Color) => void - colorPalette: Color[] - onAddColor: (color: Color) => void - onDeleteColor: (color: Color) => void -} +import ColorPalette from '@/components/organisms/event/venueedit/palette/ColorPalette'; +import { Color } from 'react-color'; +import ColorSingleSelector from '@/components/organisms/event/venueedit/palette/ColorSingleSelector'; +import { useSubToolSelection } from '@/hooks/event/venueedit/useSubToolSelection'; -function isPixelTool(tool: VenueEditTool): tool is PixelTool { - return (PIXEL_TOOLS as readonly string[]).includes(tool) -} - -function isTextTool(tool: VenueEditTool): tool is TextTool { - return (TEXT_TOOLS as readonly string[]).includes(tool) +interface VenueToolSubMenuProps { + selectedColor: Color; + setSelectedColor: (color: Color) => void; + selectedColorBackGround: Color; + setSelectedColorBackGround: (color: Color) => void; + colorPalette: Color[]; + onAddColor: (color: Color) => void; + onDeleteColor: (color: Color) => void; } -export default function VenueToolSubMenu({ - selectedTool, - toggleToGenerateTool, - toggleToEraseTool, - selectedColor, +export default function VenueToolSubMenu({ + selectedColor, setSelectedColor, selectedColorBackGround, setSelectedColorBackGround, @@ -38,7 +23,7 @@ export default function VenueToolSubMenu({ onAddColor, onDeleteColor }: Readonly) { - const toolType = useMemo(() => getToolType(selectedTool), [selectedTool]); + const { toolType, isPixelTool, isTextTool } = useSubToolSelection(); return (
@@ -47,31 +32,25 @@ export default function VenueToolSubMenu({
現在のツール: {toolType ?? 'なし'}
- {isPixelTool(selectedTool) && ( + {isPixelTool && ( )} - {isTextTool(selectedTool) && ( + {isTextTool && ( )}
- ) + ); } \ No newline at end of file diff --git a/front/src/hooks/event/venueedit/tool/useToolSelect.ts b/front/src/hooks/event/venueedit/tool/useToolSelect.ts index 4dcb57b..679d5dc 100644 --- a/front/src/hooks/event/venueedit/tool/useToolSelect.ts +++ b/front/src/hooks/event/venueedit/tool/useToolSelect.ts @@ -1,5 +1,4 @@ -import { useCallback } from 'react' -import { DRAWABLE_TOOLS, DrawableTool, VenueEditTool } from '@/types/tool' +import { VenueEditTool } from '@/types/tool' import { usePixelDraw } from '@/hooks/event/venueedit/tool/cell/usePixelDraw' import { usePixelErase } from '@/hooks/event/venueedit/tool/cell/usePixelErase' import { useCircleDraw } from '@/hooks/event/venueedit/tool/cell/useCircleDraw' @@ -8,9 +7,9 @@ import { Color } from 'react-color' import { useCircleErase } from '@/hooks/event/venueedit/tool/cell/useCircleErase' import { useTextAdd } from '@/hooks/event/venueedit/tool/select/useTextAdd' import { TextState } from '@/types/event/state' +import { useSubToolSelection } from '@/hooks/event/venueedit/useSubToolSelection' interface Props { - selectedTool: VenueEditTool selectedColor: Color selectedColorBackGround?: Color canvasRef: React.RefObject @@ -35,7 +34,6 @@ type ToolHandlers = { } export const useToolSelect = ({ - selectedTool, selectedColor, selectedColorBackGround, canvasRef, @@ -51,6 +49,7 @@ export const useToolSelect = ({ startDrawing, endDrawing, }: Props) => { + const { selectedTool } = useSubToolSelection() const pixelDraw = usePixelDraw({ canvasRef, @@ -147,17 +146,17 @@ export const useToolSelect = ({ const handleMouseUp = createHandler('handleMouseUp') const handleMouseLeave = createHandler('handleMouseLeave') - return { - handleMouseDown, - handleMouseMove, - handleMouseUp, - handleMouseLeave, - isTextDialogOpen: textAdd.isTextDialogOpen, - setIsTextDialogOpen: textAdd.setIsTextDialogOpen, - currentText: textAdd.currentText, - setCurrentText: textAdd.setCurrentText, - textPosition: textAdd.textPosition, - setTextPosition: textAdd.setTextPosition, - handleTextAdd: textAdd.handleTextAdd, + return { + handleMouseDown, + handleMouseMove, + handleMouseUp, + handleMouseLeave, + isTextDialogOpen: textAdd.isTextDialogOpen, + setIsTextDialogOpen: textAdd.setIsTextDialogOpen, + currentText: textAdd.currentText, + setCurrentText: textAdd.setCurrentText, + textPosition: textAdd.textPosition, + setTextPosition: textAdd.setTextPosition, + handleTextAdd: textAdd.handleTextAdd, } } \ No newline at end of file diff --git a/front/src/hooks/event/venueedit/useCanvasDraw.ts b/front/src/hooks/event/venueedit/useCanvasDraw.ts index 5a5e1eb..632c8e5 100644 --- a/front/src/hooks/event/venueedit/useCanvasDraw.ts +++ b/front/src/hooks/event/venueedit/useCanvasDraw.ts @@ -1,34 +1,29 @@ -import { VenueEditTool } from '@/types/tool' -import { Color } from 'react-color' -import { useZoom } from '@/hooks/event/venueedit/useZoom' -import { useToolSelect } from '@/hooks/event/venueedit/tool/useToolSelect' -import { useDrawingState } from '@/hooks/event/venueedit/useDrawingState' -import { useImageAction } from './image/useImageAction' -import { EventVenueEditViewModel } from '@/types/event/viewmodel' -import { useInitialState } from '@/hooks/event/venueedit/useInitialState' +import { Color } from 'react-color'; +import { useZoom } from '@/hooks/event/venueedit/useZoom'; +import { useToolSelect } from '@/hooks/event/venueedit/tool/useToolSelect'; +import { useDrawingState } from '@/hooks/event/venueedit/useDrawingState'; +import { useImageAction } from './image/useImageAction'; +import { EventVenueEditViewModel } from '@/types/event/viewmodel'; +import { useInitialState } from '@/hooks/event/venueedit/useInitialState'; /** * キャンバスの描画を管理するフックのProps * @param selectedColor 選択された色 - * @param selectedTool 選択されたツール */ interface Props { - selectedColor: Color - selectedTool: VenueEditTool - selectedColorBackGround?: Color - eventVenueEditViewModel: EventVenueEditViewModel + selectedColor: Color; + selectedColorBackGround?: Color; + eventVenueEditViewModel: EventVenueEditViewModel; } /** * キャンバスの描画を管理するフック * @param selectedColor 選択された色 - * @param selectedTool 選択されたツール * @returns キャンバスの参照、キャンバスのサイズ、セルの座標を取得する関数、描画関数 */ export const useCanvasDraw = ({ selectedColor, - selectedTool, selectedColorBackGround, eventVenueEditViewModel }: Props) => { @@ -79,7 +74,6 @@ export const useCanvasDraw = ({ textPosition, setTextPosition, handleTextAdd } = useToolSelect({ - selectedTool, selectedColor, selectedColorBackGround, canvasRef, diff --git a/front/src/hooks/event/venueedit/useSubToolSelection.ts b/front/src/hooks/event/venueedit/useSubToolSelection.ts index 00ddbfd..3b23218 100644 --- a/front/src/hooks/event/venueedit/useSubToolSelection.ts +++ b/front/src/hooks/event/venueedit/useSubToolSelection.ts @@ -1,54 +1,59 @@ -import { useCallback, useState } from "react" -import { EDITOR_TOOL_PAIR_TREE, VenueEditTool } from "@/types/tool" +import { useAtomValue, useSetAtom } from 'jotai'; +import { useCallback, useMemo } from 'react'; +import { selectedToolAtom, toolActionAtom } from '@/store/event/venueedit/tool'; +import { EditorToolPairKey, PIXEL_TOOLS, TEXT_TOOLS } from '@/types/tool'; +import { isDrawingTool as _isDrawingTool, getToolType as _getToolType } from '@/lib/event/venueedit/subToolSelection'; /** * サブツール選択を管理するフック - * + * * @returns サブツールの状態と操作関数 - * - selectedTool: 現在選択されているツール - * - toggleToGenerateTool: 生成系ツールに切り替える関数 - * - toggleToEraseTool: 消去系ツールに切り替える関数 - * - setDrawToolFromToolKey: ツールキーから描画系ツールに切り替える関数 */ export const useSubToolSelection = () => { - const [selectedTool, setSelectedTool] = useState("ピクセル塗りつぶし") - - // 生成系ツールに切り替える - // セッターには現在のツールを参照して変換する関数を受け取ることができる - // 依存配列に現在の配列をいれて書くよりも、効率が上がるためこちらの方が望ましいとのこと - const toggleToGenerateTool = useCallback(() => { - setSelectedTool(prevTool => { - const toolPair = Object.values(EDITOR_TOOL_PAIR_TREE).find( - pair => pair.generate === prevTool || pair.erase === prevTool - ); - - return toolPair ? toolPair.generate : prevTool; - }); - }, []) - - // 消去系ツールに切り替える関数 - const toggleToEraseTool = useCallback(() => { - setSelectedTool(prevTool => { - const toolPair = Object.values(EDITOR_TOOL_PAIR_TREE).find( - pair => pair.generate === prevTool || pair.erase === prevTool - ); - - return toolPair ? toolPair.erase : prevTool; - }); - }, []) - - const setDrawToolFromToolKey = useCallback((toolKey: keyof typeof EDITOR_TOOL_PAIR_TREE) => { - const toolPair = EDITOR_TOOL_PAIR_TREE[toolKey] - - if (toolPair) { - setSelectedTool(toolPair.generate) - } - }, []) + const selectedTool = useAtomValue(selectedToolAtom); + const dispatch = useSetAtom(toolActionAtom); + + const toggleToGenerateTool = useCallback( + () => dispatch({ type: 'TOGGLE_GENERATE' }), + [dispatch] + ); + + const toggleToEraseTool = useCallback( + () => dispatch({ type: 'TOGGLE_ERASE' }), + [dispatch] + ); + + const setDrawToolFromToolKey = useCallback( + (toolKey: EditorToolPairKey) => { + dispatch({ type: 'SET_DRAW_TOOL', toolKey }); + }, + [dispatch] + ); + + // isDrawingToolはselectedToolに依存し、その結果をメモ化する + const isDrawingTool = useMemo(() => _isDrawingTool(selectedTool), [selectedTool]); + + // getToolTypeは純粋関数であり、その参照を安定させるためにuseMemoでラップする + const getToolType = useMemo(() => _getToolType, []); + + // toolTypeはselectedToolとgetToolTypeに依存し、その結果をメモ化する + const toolType = useMemo(() => getToolType(selectedTool), [selectedTool, getToolType]); + + // isPixelToolはselectedToolに依存し、その結果をメモ化する + const isPixelTool = useMemo(() => (PIXEL_TOOLS as readonly string[]).includes(selectedTool), [selectedTool]); + + // isTextToolはselectedToolに依存し、その結果をメモ化する + const isTextTool = useMemo(() => (TEXT_TOOLS as readonly string[]).includes(selectedTool), [selectedTool]); return { selectedTool, toggleToGenerateTool, toggleToEraseTool, - setDrawToolFromToolKey - } -} \ No newline at end of file + setDrawToolFromToolKey, + getToolType, + toolType, + isDrawingTool, + isPixelTool, + isTextTool, + }; +}; \ No newline at end of file diff --git a/front/src/store/event/venueedit/tool.ts b/front/src/store/event/venueedit/tool.ts new file mode 100644 index 0000000..dd0098a --- /dev/null +++ b/front/src/store/event/venueedit/tool.ts @@ -0,0 +1,46 @@ +import { atom } from 'jotai' +import { EDITOR_TOOL_PAIR_TREE, VenueEditTool } from '@/types/tool' +import { EditorToolPairKey } from '@/types/tool' + +// アクションの型を定義 +export type ToolAction = + | { type: 'TOGGLE_GENERATE' } + | { type: 'TOGGLE_ERASE' } + | { type: 'SET_DRAW_TOOL'; toolKey: EditorToolPairKey }; + +// 状態を保持するプライベートなベースatom +const _selectedToolAtom = atom('ピクセル塗りつぶし'); + +// 読み取り専用のatom(コンポーネントはこれを参照) +export const selectedToolAtom = atom((get) => get(_selectedToolAtom)); + +// 更新ロジックをカプセル化した書き込み専用atom +export const toolActionAtom = atom( + null, // 読み取りはしないのでnull + (get, set, action: ToolAction) => { + const currentTool = get(_selectedToolAtom); + switch (action.type) { + case 'TOGGLE_GENERATE': { + const toolPair = Object.values(EDITOR_TOOL_PAIR_TREE).find( + pair => pair.generate === currentTool || pair.erase === currentTool + ); + if (toolPair) set(_selectedToolAtom, toolPair.generate); + break; + } + case 'TOGGLE_ERASE': { + const toolPair = Object.values(EDITOR_TOOL_PAIR_TREE).find( + pair => pair.generate === currentTool || pair.erase === currentTool + ); + if (toolPair) set(_selectedToolAtom, toolPair.erase); + break; + } + case 'SET_DRAW_TOOL': { + const toolPair = EDITOR_TOOL_PAIR_TREE[action.toolKey]; + if (toolPair) { + set(_selectedToolAtom, toolPair.generate); + } + break; + } + } + } +); \ No newline at end of file From 9dfe419f8076b85edb5b24c8378d61c16490472f Mon Sep 17 00:00:00 2001 From: atomisu0312 Date: Wed, 30 Jul 2025 00:00:03 +0900 Subject: [PATCH 2/3] =?UTF-8?q?=E3=83=84=E3=83=BC=E3=83=AB=E7=B3=BB?= =?UTF-8?q?=E3=81=AEState=E3=82=92Jotai=E3=81=AB=E7=BD=AE=E3=81=8D?= =?UTF-8?q?=E6=8F=9B=E3=81=88=20#264?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 余計な関数を除去 --- front/src/hooks/event/venueedit/useSubToolSelection.ts | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/front/src/hooks/event/venueedit/useSubToolSelection.ts b/front/src/hooks/event/venueedit/useSubToolSelection.ts index 3b23218..9132486 100644 --- a/front/src/hooks/event/venueedit/useSubToolSelection.ts +++ b/front/src/hooks/event/venueedit/useSubToolSelection.ts @@ -2,7 +2,7 @@ import { useAtomValue, useSetAtom } from 'jotai'; import { useCallback, useMemo } from 'react'; import { selectedToolAtom, toolActionAtom } from '@/store/event/venueedit/tool'; import { EditorToolPairKey, PIXEL_TOOLS, TEXT_TOOLS } from '@/types/tool'; -import { isDrawingTool as _isDrawingTool, getToolType as _getToolType } from '@/lib/event/venueedit/subToolSelection'; +import { isDrawingTool as _isDrawingTool, getToolType } from '@/lib/event/venueedit/subToolSelection'; /** * サブツール選択を管理するフック @@ -33,11 +33,8 @@ export const useSubToolSelection = () => { // isDrawingToolはselectedToolに依存し、その結果をメモ化する const isDrawingTool = useMemo(() => _isDrawingTool(selectedTool), [selectedTool]); - // getToolTypeは純粋関数であり、その参照を安定させるためにuseMemoでラップする - const getToolType = useMemo(() => _getToolType, []); - - // toolTypeはselectedToolとgetToolTypeに依存し、その結果をメモ化する - const toolType = useMemo(() => getToolType(selectedTool), [selectedTool, getToolType]); + // toolTypeはselectedToolに依存し、その結果をメモ化する + const toolType = useMemo(() => getToolType(selectedTool), [selectedTool]); // isPixelToolはselectedToolに依存し、その結果をメモ化する const isPixelTool = useMemo(() => (PIXEL_TOOLS as readonly string[]).includes(selectedTool), [selectedTool]); @@ -50,7 +47,6 @@ export const useSubToolSelection = () => { toggleToGenerateTool, toggleToEraseTool, setDrawToolFromToolKey, - getToolType, toolType, isDrawingTool, isPixelTool, From cb41e1e8c5ebf7e0160b3371901c96c3bd3d44d4 Mon Sep 17 00:00:00 2001 From: atomisu0312 Date: Wed, 30 Jul 2025 00:09:08 +0900 Subject: [PATCH 3/3] =?UTF-8?q?=E3=83=84=E3=83=BC=E3=83=AB=E7=B3=BB?= =?UTF-8?q?=E3=81=AEState=E3=82=92Jotai=E3=81=AB=E7=BD=AE=E3=81=8D?= =?UTF-8?q?=E6=8F=9B=E3=81=88=20#264=20=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - ハードコーディング部分の修正 --- front/src/lib/event/venueedit/constants.ts | 5 +++++ front/src/store/event/venueedit/tool.ts | 3 ++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/front/src/lib/event/venueedit/constants.ts b/front/src/lib/event/venueedit/constants.ts index 66de743..d1ad608 100644 --- a/front/src/lib/event/venueedit/constants.ts +++ b/front/src/lib/event/venueedit/constants.ts @@ -24,6 +24,11 @@ export const DEFAULT_COLORS: Color[] = [ '#808080', // グレー ] +/** + * デフォルトの選択ツール + */ +export const DEFAULT_SELECTED_TOOL = 'ピクセル塗りつぶし' + /** * デフォルトのピクセル数 */ diff --git a/front/src/store/event/venueedit/tool.ts b/front/src/store/event/venueedit/tool.ts index dd0098a..19f31d4 100644 --- a/front/src/store/event/venueedit/tool.ts +++ b/front/src/store/event/venueedit/tool.ts @@ -1,6 +1,7 @@ import { atom } from 'jotai' import { EDITOR_TOOL_PAIR_TREE, VenueEditTool } from '@/types/tool' import { EditorToolPairKey } from '@/types/tool' +import { DEFAULT_SELECTED_TOOL } from '@/lib/event/venueedit/constants' // アクションの型を定義 export type ToolAction = @@ -9,7 +10,7 @@ export type ToolAction = | { type: 'SET_DRAW_TOOL'; toolKey: EditorToolPairKey }; // 状態を保持するプライベートなベースatom -const _selectedToolAtom = atom('ピクセル塗りつぶし'); +const _selectedToolAtom = atom(DEFAULT_SELECTED_TOOL); // 読み取り専用のatom(コンポーネントはこれを参照) export const selectedToolAtom = atom((get) => get(_selectedToolAtom));