From de93f4562bfff034795ad11b7d1d8ba6fc2f82bb Mon Sep 17 00:00:00 2001 From: Ian Haken Date: Sat, 20 Jun 2026 10:07:18 -0700 Subject: [PATCH] Support docking build dialog. --- src/client/grid/building_dialog.module.css | 52 ++++++++ src/client/grid/building_dialog.tsx | 140 ++++++++++++++------- src/client/grid/hex_grid.tsx | 2 +- 3 files changed, 147 insertions(+), 47 deletions(-) diff --git a/src/client/grid/building_dialog.module.css b/src/client/grid/building_dialog.module.css index 19717ab5..7049f2f4 100644 --- a/src/client/grid/building_dialog.module.css +++ b/src/client/grid/building_dialog.module.css @@ -14,3 +14,55 @@ flex-direction: column; align-content: center; } + +.sidePanel { + position: fixed; + bottom: 0; + left: 0; + right: 0; + height: 300px; + background: white; + box-shadow: 0 -3px 12px rgba(0, 0, 0, 0.25); + display: flex; + flex-direction: column; + z-index: 1000; +} + +.sidePanelHeader { + display: flex; + justify-content: space-between; + align-items: center; + padding: 8px 14px; + border-bottom: 1px solid #ddd; + background: #f4f4f4; + font-size: 1.1em; + font-weight: bold; + flex-shrink: 0; +} + +.sidePanelContent { + flex: 1; + overflow-x: auto; + overflow-y: hidden; + padding: 10px 12px; + display: flex; + flex-direction: column; +} + +.sidePanelContent .buildingDialogContainer { + flex-wrap: nowrap; +} + +.sidePanelClose { + background: none; + border: none; + font-size: 1.2em; + cursor: pointer; + padding: 2px 6px; + line-height: 1; +} + +.sidePanelClose:hover { + background: #e0e0e0; + border-radius: 4px; +} diff --git a/src/client/grid/building_dialog.tsx b/src/client/grid/building_dialog.tsx index 1ee22484..f5d3ecc0 100644 --- a/src/client/grid/building_dialog.tsx +++ b/src/client/grid/building_dialog.tsx @@ -48,6 +48,10 @@ import { buildingDialogContainer, buildingOption, dialogContent, + sidePanel, + sidePanelClose, + sidePanelContent, + sidePanelHeader, } from "./building_dialog.module.css"; import { ClickTarget } from "./click_target"; import { HexGrid } from "./hex_grid"; @@ -58,6 +62,8 @@ interface BuildingProps { coordinates?: Coordinates; } +const SIDE_PANEL_KEY = "buildingDialogSidePanel"; + export function BuildingDialog({ coordinates, settings, @@ -73,6 +79,9 @@ export function BuildingDialog({ const availableCities = useInjectedState(AVAILABLE_CITIES); const grid = useInjected(GridHelper); const [showReasons, setShowReasons] = useState(false); + const [useSidePanel, setUseSidePanel] = useState( + () => localStorage.getItem(SIDE_PANEL_KEY) === "true", + ); const [direction, rotate] = useReducer( (prev: Direction, _: object) => rotateDirectionClockwise(prev), Direction.TOP, @@ -101,6 +110,14 @@ export function BuildingDialog({ [space, emitUrbanize], ); + const toggleMode = useCallback(() => { + setUseSidePanel((prev) => { + const next = !prev; + localStorage.setItem(SIDE_PANEL_KEY, String(next)); + return next; + }); + }, []); + const eligibleCities: number[] = []; availableCities.forEach((_, idx) => { if (canUrbanizeSpace(urbanizeAction, coordinates, idx)) { @@ -121,57 +138,88 @@ export function BuildingDialog({ } }, [coordinates, hasBuildingOptions, cancelBuild]); - return ( + const innerContent = ( <> - - Select a tile to place - -

- setShowReasons(!!data.checked)} +

+ setShowReasons(!!data.checked)} + /> + {!useSidePanel && ( + + )} +

+ {showReasons && ( + + )} +
+ {canUrbanize && + eligibleCities.map((cityIdx) => ( +
+ selectAvailableCity(cityIdx)} + /> +
+ ))} + {eligible.map((build, index) => ( +
+ build.reason == null && onSelect(build.action)} /> -

- {showReasons && ( - - )} -
- {canUrbanize && - eligibleCities.map((cityIdx) => ( -
- selectAvailableCity(cityIdx)} - /> -
- ))} - {eligible.map((build, index) => ( -
- build.reason == null && onSelect(build.action)} - /> -
${build.cost}
- {build.reason} -
- ))} +
${build.cost}
+ {build.reason}
- - + ))} +
); + + if (useSidePanel) { + if (!isOpen) return null; + return ( +
+
+ + Select a tile to place + +
+
{innerContent}
+
+ ); + } + + return ( + + Select a tile to place + {innerContent} + + ); } interface PlaceDialogProps { diff --git a/src/client/grid/hex_grid.tsx b/src/client/grid/hex_grid.tsx index 9f037c9e..9f34370a 100644 --- a/src/client/grid/hex_grid.tsx +++ b/src/client/grid/hex_grid.tsx @@ -239,7 +239,7 @@ export function HexGrid({ if (!deepEquals(newViewBox, internalViewBox)) { setViewBox(newViewBox); } - }, [ref, internalViewBox]); + }, [ref, spaces, internalViewBox]); const viewBox = useMemo( () => ({