void;
+ onYearEntered: (value: string) => void;
+ onPhaseEntered: (value: Phase) => void;
+};
+
+const LocationInput = ({
+ board,
+ onTimelineEntered,
+ onYearEntered,
+ onPhaseEntered,
+}: LocationInputProps) => (
+
+
+
+
+
+);
+
+type BoardGhostProps = {
+ initialTimeline: number;
+ initialYear: number;
+ initialPhase: Phase;
+};
+
+const BoardGhost = ({ initialTimeline, initialYear, initialPhase }: BoardGhostProps) => {
+ const [timeline, setTimeline] = useState(initialTimeline);
+ const [year, setYear] = useState(initialYear);
+ const [phase, setPhase] = useState(initialPhase);
+
+ const board = {
+ timeline,
+ year,
+ phase,
+ childTimelines: [],
+ centres: {},
+ units: {},
+ };
+
+ const onTimelineEntered = (value: string) => {
+ if (!isInteger(value, false)) {
+ setTimeline(initialTimeline);
+ return;
+ }
+
+ const parsedValue = parseInt(value, 10);
+ setTimeline(parsedValue);
+ };
+
+ const onYearEntered = (value: string) => {
+ if (!isInteger(value, false)) {
+ setYear(initialYear);
+ return;
+ }
+
+ const parsedValue = parseInt(value, 10);
+ setYear(parsedValue);
+ };
+
+ return (
+
+
+
+
{getBoardName(board)}
+
+
+
+ );
+};
+
+export default BoardGhost;
diff --git a/client/src/components/world/boards/BoardLayer.tsx b/client/src/components/world/boards/BoardLayer.tsx
index 58381d1..35e8718 100644
--- a/client/src/components/world/boards/BoardLayer.tsx
+++ b/client/src/components/world/boards/BoardLayer.tsx
@@ -1,12 +1,16 @@
import { useContext } from 'react';
-import { getNextPhase } from '../../../types/enums/phase';
+import { getNextMajorPhase, getNextPhase } from '../../../types/enums/phase';
import { filterUnique } from '../../../utils/listUtils';
import Board from './Board';
import BoardSkip from './BoardSkip';
import WorldContext from '../../context/WorldContext';
+import BoardGhost from './BoardGhost';
+import OrderEntryContext from '../../context/OrderEntryContext';
+import InputMode from '../../../types/enums/inputMode';
const BoardLayer = () => {
const { world } = useContext(WorldContext);
+ const { currentMode, currentOrder } = useContext(OrderEntryContext);
if (!world) return null;
const timelines = filterUnique(world.boards.map(({ timeline }) => timeline));
@@ -17,6 +21,17 @@ const BoardLayer = () => {
Object.values(board.units).some((unit) => unit.mustRetreat),
);
+ const selectedLocation = currentOrder?.location;
+ const showGhostBoard =
+ (currentMode === InputMode.Support || currentMode === InputMode.Convoy) && selectedLocation;
+ const ghostBoard = showGhostBoard && (
+
+ );
+
return (
{timelines.map((timeline) => (
@@ -50,6 +65,7 @@ const BoardLayer = () => {
);
})}
+ {timeline === selectedLocation?.timeline && ghostBoard}
))}
diff --git a/client/src/types/enums/phase.ts b/client/src/types/enums/phase.ts
index 187fa75..79363d5 100644
--- a/client/src/types/enums/phase.ts
+++ b/client/src/types/enums/phase.ts
@@ -14,6 +14,16 @@ export const getNextPhase = (year: number, phase: Phase) => {
return { year: nextYear, phase: nextPhase };
};
+export const getNextMajorPhase = (year: number, phase: Phase) => {
+ const nextYear = phase === Phase.Winter || phase === Phase.Fall ? year + 1 : year;
+ const nextPhase = {
+ [Phase.Spring]: Phase.Fall,
+ [Phase.Fall]: Phase.Spring,
+ [Phase.Winter]: Phase.Spring,
+ }[phase];
+ return { year: nextYear, phase: nextPhase };
+};
+
export const getPhaseIndex = (phase: Phase) =>
({
[Phase.Spring]: 1,
diff --git a/client/src/utils/numberUtils.ts b/client/src/utils/numberUtils.ts
new file mode 100644
index 0000000..0a00563
--- /dev/null
+++ b/client/src/utils/numberUtils.ts
@@ -0,0 +1,13 @@
+/* eslint-disable import/prefer-default-export */
+
+export const isInteger = (value: string, allowNegative: boolean = true) => {
+ const parsedValue = parseInt(value, 10);
+
+ // None of these cover every case (e.g. whitespace), so use all of them
+ return (
+ parsedValue.toString() === value &&
+ !Number.isNaN(parsedValue) &&
+ Number.isInteger(parsedValue) &&
+ (allowNegative || parsedValue >= 0)
+ );
+};