Skip to content

Commit 2c7b992

Browse files
committed
resizable sidebar
1 parent bbb5040 commit 2c7b992

File tree

9 files changed

+134
-28
lines changed

9 files changed

+134
-28
lines changed

src/main/client/src/Router.jsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,10 +38,10 @@ export const Router = createBrowserRouter(
3838
element={<WithConnection />}>
3939
<Route
4040
element={
41-
<div>
41+
<>
4242
<Outlet />
4343
<Toaster position="top-right" />
44-
</div>}>
44+
</>}>
4545
<Route
4646
path={base + "/lobby"}
4747
element={<Lobby />} />
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
import {
2+
useRef,
3+
useEffect,
4+
useState,
5+
} from "react"
6+
import {
7+
useLayoutStore,
8+
} from "../layout.js"
9+
10+
export const SideBar = ({children}) => {
11+
let vw = useLayoutStore(state => state.vw)
12+
let dragging = useLayoutStore(state => state.dragging)
13+
let setDragging = useLayoutStore(state => state.setDragging)
14+
let sidebarWidth = useLayoutStore(state => state.sidebarWidth)
15+
let setSidebarWidth = useLayoutStore(state => state.setSidebarWidth)
16+
let [ghostWidth, setGhostWidth] = useState(sidebarWidth)
17+
let draggingRef = useRef()
18+
let ghostWidthRef = useRef()
19+
draggingRef.current = dragging
20+
ghostWidthRef.current = ghostWidth
21+
useEffect(() => {
22+
let mousemove = (e) => {
23+
if (!draggingRef.current) {
24+
return
25+
}
26+
setGhostWidth(Math.trunc(vw - e.clientX))
27+
}
28+
let mouseup = () => {
29+
if (!draggingRef.current) {
30+
return
31+
}
32+
setSidebarWidth(ghostWidthRef.current)
33+
setDragging(false)
34+
}
35+
window.document.addEventListener("mousemove", mousemove)
36+
window.document.addEventListener("mouseup", mouseup)
37+
return () => {
38+
window.document.removeEventListener("mousemove", mousemove)
39+
window.document.removeEventListener("mouseup", mouseup)
40+
}
41+
}, [vw, draggingRef, setDragging, setSidebarWidth])
42+
return (
43+
<div
44+
style={{width: sidebarWidth + "px"}}
45+
className="fixed top-0 right-0 h-full bg-slate-800">
46+
<div
47+
onMouseDown={(e) => {
48+
e.preventDefault()
49+
setDragging(true)
50+
}}
51+
style={{right: sidebarWidth + "px"}}
52+
className="fixed top-0 w-[3px] h-full bg-slate-700 z-10 cursor-col-resize" />
53+
{dragging && (
54+
<div
55+
style={{right: ghostWidth + "px"}}
56+
className="fixed top-0 w-[3px] h-full bg-slate-600 z-20" />
57+
)}
58+
{children}
59+
</div>
60+
)
61+
}

src/main/client/src/feature/game/Game.jsx

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,9 @@ import {
2424
useAuthStore,
2525
useGameStore,
2626
} from "../../store.js"
27+
import {
28+
useLayoutStore,
29+
} from "../../layout.js"
2730
import {
2831
paintShadow,
2932
paintGrid,
@@ -59,6 +62,8 @@ export const Game = () => {
5962
let [forbidden_x, forbidden_y] = useGameStore(state => state.forbidden)
6063
let canvasRef = useRef()
6164
let countingGroup = !gameHasEnded() && counting ? getCountingGroup(board, cursor_x, cursor_y) : undefined
65+
let sidebarWidth = useLayoutStore(state => state.sidebarWidth)
66+
let vw = useLayoutStore(state => state.vw)
6267

6368
let context = useMemo(() => {
6469
let dim = board.length
@@ -247,7 +252,8 @@ export const Game = () => {
247252
}
248253

249254
return (
250-
<div className="w-[calc(100vw-24rem)]">
255+
<div
256+
style={{width: (vw - sidebarWidth) + "px"}}>
251257
<div className="grid justify-center mt-8">
252258
<canvas ref={canvasRef}
253259
onMouseLeave={() => {

src/main/client/src/feature/game/GamePanel.jsx

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,14 +33,17 @@ import {
3333
import {
3434
GameChat,
3535
} from "./GameChat.jsx"
36+
import {
37+
SideBar,
38+
} from "../../component/SideBar.jsx"
3639

3740
export const GamePanel = ({zoom, setZoom}) => {
3841
return (
39-
<div className="fixed top-0 right-0 z-1 h-full bg-slate-800 border-l-2 border-slate-700">
40-
<div className="w-[24rem] pr-3 pt-4 pl-2 h-full flex flex-col gap-y-1">
42+
<SideBar>
43+
<div className="pr-3 pt-4 pl-2 h-full flex flex-col gap-y-1">
4144
<Panel zoom={zoom} setZoom={setZoom} />
4245
</div>
43-
</div>
46+
</SideBar>
4447
)
4548
}
4649

src/main/client/src/feature/lobby/ActiveGames.jsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ export function ActiveGames() {
5151
}, [setInit, auth, initialized, stompClient, navigate])
5252
return (
5353
<>
54-
<div className="float-left ml-4 grid grid-cols-[min-content_min-content_min-content]">
54+
<div className="ml-4 grid grid-cols-[min-content_min-content_min-content]">
5555
{activeGames.map((game) => (
5656
<ActiveGame
5757
game={game}

src/main/client/src/feature/lobby/Lobby.jsx

Lines changed: 15 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ export function Lobby() {
7777
navigate(base + "/game/" + response.id)
7878
}), [auth, navigate])
7979
return (
80-
<div>
80+
<>
8181
<div className={twJoin(
8282
"mt-2 inline-flex py-2 pr-4 gap-x-1 border-r-2 border-y-2",
8383
isNewGameOpen && "rounded-r-full border-slate-600",
@@ -100,16 +100,17 @@ export function Lobby() {
100100
</button>
101101
)}
102102
</div>
103-
<div className="clear-both" />
104-
<DetailNavigation detail={detail} setDetail={setDetail} />
105-
{detail === "open" && (
106-
<OpenGames />
107-
)}
108-
{detail === "active" && (
109-
<ActiveGames />
110-
)}
103+
<div className="mt-2 grid grid-cols-[max-content_auto]">
104+
<DetailNavigation detail={detail} setDetail={setDetail} />
105+
{detail === "open" && (
106+
<OpenGames />
107+
)}
108+
{detail === "active" && (
109+
<ActiveGames />
110+
)}
111+
</div>
111112
<LobbyPanel />
112-
</div>
113+
</>
113114
)
114115
}
115116

@@ -145,11 +146,10 @@ function NewGameDialog({onNewGame, onStartEdit, setNewGameOpen}) {
145146
}
146147
function DetailNavigation({detail, setDetail}) {
147148
return (
148-
<div className="mt-2">
149-
<div className={twJoin(
150-
"float-left py-3 pl-2 pr-3 border-r-2 border-y-2 border-slate-600",
151-
"rounded-r-xl flex flex-col gap-y-2",
152-
)}>
149+
<div className={twJoin(
150+
"w-fit py-3 pl-2 pr-3 border-r-2 border-y-2 border-slate-600",
151+
"rounded-r-xl flex flex-col gap-y-2",
152+
)}>
153153
{detailData.map(([id, label]) => (
154154
<button
155155
key={id}
@@ -161,7 +161,6 @@ function DetailNavigation({detail, setDetail}) {
161161
id !== detail && "border-transparent hover:bg-stone-800",
162162
)}>{label}</button>
163163
))}
164-
</div>
165164
</div>
166165
)
167166
}

src/main/client/src/feature/lobby/LobbyPanel.jsx

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,17 @@ import {
1111
import {
1212
useAuthStore,
1313
} from "../../store.js"
14+
import {
15+
SideBar,
16+
} from "../../component/SideBar.jsx"
1417

1518
export const LobbyPanel = () => {
1619
return (
17-
<div className="fixed top-0 right-0 w-[24rem] h-full bg-slate-800 border-l-2 border-slate-700">
20+
<SideBar>
1821
<div className="pt-2">
1922
<Panel />
2023
</div>
21-
</div>
24+
</SideBar>
2225
)
2326
}
2427

src/main/client/src/feature/lobby/OpenGames.jsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -77,8 +77,8 @@ export function OpenGames() {
7777
navigate(base + "/game/" + d.game.id)
7878
}), [auth, navigate])
7979
return (
80-
<>
81-
<div className="float-left ml-4 grid grid-cols-[min-content_min-content]">
80+
<div>
81+
<div className="ml-4 grid grid-cols-[max-content_max-content]">
8282
{openGames.map((game) => (
8383
<OpenGame
8484
game={game}
@@ -94,7 +94,7 @@ export function OpenGames() {
9494
onAccept={onAccept}
9595
/>
9696
)}
97-
</>
97+
</div>
9898
)
9999
}
100100

src/main/client/src/layout.js

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import {
2+
create,
3+
} from "zustand"
4+
import {
5+
produce,
6+
} from "immer"
7+
import {
8+
persist,
9+
} from "zustand/middleware"
10+
11+
export const useLayoutStore = create(
12+
persist(
13+
(set) => ({
14+
dragging: false,
15+
vw: Math.max(document.documentElement.clientWidth || 0, window.innerWidth || 0),
16+
sidebarWidth: 24 * getPixelRem(),
17+
setDragging: (dragging) => {
18+
set(produce(state => {
19+
state.dragging = dragging
20+
}))
21+
},
22+
setSidebarWidth: (width) => {
23+
set(produce(state => {
24+
state.sidebarWidth = width
25+
}))
26+
},
27+
}),
28+
{name: "layout-storage"},
29+
),
30+
)
31+
32+
function getPixelRem() {
33+
return parseFloat(window.getComputedStyle(window.document.documentElement).fontSize)
34+
}

0 commit comments

Comments
 (0)