diff --git a/packages/maker/src/App.tsx b/packages/maker/src/App.tsx index f54d387..61fe8f8 100644 --- a/packages/maker/src/App.tsx +++ b/packages/maker/src/App.tsx @@ -8,6 +8,7 @@ import SplitPane from 'react-split-pane'; import './common/SplitPane.scss'; import { connect, MapStateToProps } from 'react-redux'; import { StoreType } from './State'; +import NotificationView from './notification/NotificationView'; interface StateProps { isOpened: boolean @@ -39,20 +40,23 @@ class App extends React.Component { public render() { return ( -
-
- + <> + +
+
+ +
+
+ + + + +
+
+ +
-
- - - - -
-
- -
-
+ ); } } diff --git a/packages/maker/src/State.ts b/packages/maker/src/State.ts index b36dc9b..b21d57e 100644 --- a/packages/maker/src/State.ts +++ b/packages/maker/src/State.ts @@ -2,6 +2,7 @@ import { LoadState, LoadReducer, INITIAL_STATE as LOAD_INITIAL_STATE } from "./l import { PartsState, INITIAL_STATE as PARTS_INITIAL_STATE, ObjectPartsReducer, MapPartsReducer } from "./parts/PartsState"; import { MapState, INITIAL_STATE as MAP_INITIAL_STATE, MapReducer } from "./map/MapStates"; import { InfoPanelState, INITIAL_STATE as INFOPANEL_INITIAL_STATE, InfoPanelReducer } from "./info/InfoPanelState"; +import { NotificationState, INITIAL_STATE as NOTIFICATION_INITIAL_STATE, NotificationReducer } from "./notification/NotificationStates"; import { WWAData } from "@wwawing/common-interface"; import { WWADataReducer } from "./wwadata/WWADataState"; import { createStore, applyMiddleware } from "redux"; @@ -21,7 +22,8 @@ export interface StoreType { objParts: PartsState, mapParts: PartsState, imageUrl: string|null, - info: InfoPanelState + info: InfoPanelState, + notification: NotificationState } /** @@ -35,7 +37,8 @@ const INITIAL_STATE: StoreType = { objParts: PARTS_INITIAL_STATE, mapParts: PARTS_INITIAL_STATE, imageUrl: null, - info: INFOPANEL_INITIAL_STATE + info: INFOPANEL_INITIAL_STATE, + notification: NOTIFICATION_INITIAL_STATE } const actionCreator = actionCreatorFactory(); @@ -86,7 +89,8 @@ const reducer = reducerWithInitialState(INITIAL_STATE) map: MapReducer(state.map, action), objParts: ObjectPartsReducer(state.objParts, action), mapParts: MapPartsReducer(state.mapParts, action), - info: InfoPanelReducer(state.info, action) + info: InfoPanelReducer(state.info, action), + modal: NotificationReducer(state.notification, action) })) export const Store = createStore( diff --git a/packages/maker/src/common/MapView/MapCanvas.tsx b/packages/maker/src/common/MapView/MapCanvas.tsx index 2546760..468b479 100644 --- a/packages/maker/src/common/MapView/MapCanvas.tsx +++ b/packages/maker/src/common/MapView/MapCanvas.tsx @@ -111,8 +111,12 @@ const MapCanvas: React.FC = props => { * 指定した座標から各パーツ種類のパーツ番号を取得します * @param chipX * @param chipY + * @returns その座標にあるパーツ番号 (範囲外の場合は 0) */ const getPartsNumberOnTarget = (chipX: number, chipY: number, type: PartsType) => { + if (chipX < 0 || chipX >= mapWidth || chipY < 0 || chipY >= mapWidth) { + return 0; + } switch (type) { case PartsType.MAP: return map[chipY][chipX]; diff --git a/packages/maker/src/info/InfoPanelState.ts b/packages/maker/src/info/InfoPanelState.ts index b0c2dfe..0a08037 100644 --- a/packages/maker/src/info/InfoPanelState.ts +++ b/packages/maker/src/info/InfoPanelState.ts @@ -1,6 +1,7 @@ import actionCreatorFactory from "typescript-fsa"; import { reducerWithInitialState } from "typescript-fsa-reducers"; import { PartsType } from "../classes/WWAData"; +import { showError } from "../notification/types/ErrorNotification"; /** * InfoPanel のモードです。 @@ -49,14 +50,13 @@ export const InfoPanelReducer = reducerWithInitialState(INITIAL_STATE) viewMode: payload.mode })) .case(showPartsEdit, (state, payload) => { - // TODO: Reducer の中にアラートを表示するのはちょっと良くないかもしれない。別の場所に移しておきたい。 if (payload.number === 0) { switch (payload.type) { case PartsType.OBJECT: - alert("パーツ番号0の物体は編集できません。\nこのパーツはマップの物体を消去するときに指定してください。"); + showError("パーツ番号0の物体は編集できません。\nこのパーツはマップの物体を消去するときに指定してください。"); break; case PartsType.MAP: - alert("パーツ番号0の背景は編集できません。\nこのパーツはマップの背景を消去するときに指定してください。"); + showError("パーツ番号0の背景は編集できません。\nこのパーツはマップの背景を消去するときに指定してください。"); } return state; } diff --git a/packages/maker/src/notification/NotificationItem.ts b/packages/maker/src/notification/NotificationItem.ts new file mode 100644 index 0000000..91496c2 --- /dev/null +++ b/packages/maker/src/notification/NotificationItem.ts @@ -0,0 +1,15 @@ +import React from "react"; +import { ErrorNotificationState } from "./types/ErrorNotification"; + +/** + * 通知で表示するアイテムです。 + */ +export type NotificationItem = ErrorNotificationState; + +/** + * types ディレクトリ以降の各通知の内容表示コンポーネントを定義づける型情報です。 + * @todo 各通知固有のパラメーターをもたせる場合、どうしたら良いか考える + */ +export type NotificationItemView = React.FC<{ + +}>; diff --git a/packages/maker/src/notification/NotificationStates.ts b/packages/maker/src/notification/NotificationStates.ts new file mode 100644 index 0000000..13835d7 --- /dev/null +++ b/packages/maker/src/notification/NotificationStates.ts @@ -0,0 +1,26 @@ +import { NotificationItem } from "./NotificationItem"; +import actionCreatorFactory from "typescript-fsa"; +import { reducerWithInitialState } from "typescript-fsa-reducers"; + +export type NotificationState = { + items: NotificationItem[] +}; + +export const INITIAL_STATE: NotificationState = { + items: [] +}; + +const actionCreator = actionCreatorFactory(); +/** + * 通知を発生させます。 + */ +export const notify = actionCreator<{ content: NotificationItem }>("SHOW_ERROR"); + +export const NotificationReducer = reducerWithInitialState(INITIAL_STATE) + .case(notify, (state, params) => { + const newState = Object.assign({}, state); + newState.items = state.items.slice(); + newState.items.push(params.content); + + return newState; + }) diff --git a/packages/maker/src/notification/NotificationView.module.scss b/packages/maker/src/notification/NotificationView.module.scss new file mode 100644 index 0000000..4ab97d6 --- /dev/null +++ b/packages/maker/src/notification/NotificationView.module.scss @@ -0,0 +1,5 @@ +.notificationView { + display: absolute; + right: 0; + bottom: 0; +} diff --git a/packages/maker/src/notification/NotificationView.tsx b/packages/maker/src/notification/NotificationView.tsx new file mode 100644 index 0000000..09c3860 --- /dev/null +++ b/packages/maker/src/notification/NotificationView.tsx @@ -0,0 +1,28 @@ +import React from "react"; + +import { NotificationItem } from "./NotificationItem"; +import styles from "./NotificationView.module.scss"; +import { useSelector } from "react-redux"; +import { NotificationItemView } from "./NotificationItem"; +import { ErrorNotification } from "./types/ErrorNotification"; + +const NotificationView: React.FC<{}> = () => { + const notificationItems = useSelector(state => state.notification.items); + + return ( +
+ {notificationItems.map(notificationItem => { + const NotificationItemComponent = NotificationItemViewTable[notificationItem.type]; + return ( + + ) + })} +
+ ); +}; + +const NotificationItemViewTable: { [type in NotificationItem["type"] ]: NotificationItemView } = { + "ERROR": ErrorNotification +} + +export default NotificationView; diff --git a/packages/maker/src/notification/types/ErrorNotification.tsx b/packages/maker/src/notification/types/ErrorNotification.tsx new file mode 100644 index 0000000..6e6745c --- /dev/null +++ b/packages/maker/src/notification/types/ErrorNotification.tsx @@ -0,0 +1,31 @@ +import React from "react"; +import { Message } from "semantic-ui-react"; +import { notify } from "../NotificationStates"; +import { NotificationItemView } from "../NotificationItem"; + +export type ErrorNotificationState = { + type: "ERROR", + message: string +}; + +/** + * エラーを発生させます。 + * @param message エラーメッセージ + */ +export const showError = (message: string) => { + notify({ + content: { + type: "ERROR", + message + } + }); +}; + +export const ErrorNotification: NotificationItemView = () => { + return ( + + エラー! + {/*

{message}

*/} +
+ ); +};