Skip to content

Commit 0e08e83

Browse files
committed
Demure devtools container
- toggle rendering of Jotai and react query tools - menu on hover and is draggable so it doesn't get in the way
1 parent 545e114 commit 0e08e83

File tree

2 files changed

+170
-18
lines changed

2 files changed

+170
-18
lines changed

console/src/components/DevtoolsContainer.tsx

Lines changed: 170 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -7,29 +7,184 @@
77
// the Business Source License, use of this software will be governed
88
// by the Apache License, Version 2.0.
99

10-
import { VStack } from "@chakra-ui/react";
10+
import {
11+
Box,
12+
Center,
13+
HStack,
14+
Switch,
15+
Text,
16+
useTheme,
17+
VStack,
18+
} from "@chakra-ui/react";
1119
import { ReactQueryDevtools } from "@tanstack/react-query-devtools";
12-
import { DevTools as JotaiDevtools } from "jotai-devtools";
13-
import React, { Suspense } from "react";
20+
import React, { Suspense, useRef, useState } from "react";
1421

22+
import { useDrag } from "~/hooks/useDrag";
1523
import { getStore } from "~/jotai";
1624
import { DEVTOOL_BUTTONS_Z_INDEX } from "~/layouts/zIndex";
25+
import { MaterializeTheme } from "~/theme";
26+
27+
import { MaterializeLogo } from "./MaterializeLogo";
1728

1829
const SwitchStackModal = React.lazy(() => import("./SwitchStackModal"));
1930

31+
const LazyJotaiDevtools = React.lazy(() =>
32+
import("jotai-devtools").then(({ DevTools }) => ({
33+
default: () => <DevTools position="bottom-right" store={getStore()} />,
34+
})),
35+
);
36+
37+
const DEVTOOLS_POSITION_KEY = "devtools-position";
38+
const BUTTON_SIZE = 36;
39+
40+
const clamp = (value: number, min: number, max: number) =>
41+
Math.max(min, Math.min(max, value));
42+
43+
const getStoredPosition = (): { x: number; y: number } => {
44+
try {
45+
const stored = localStorage.getItem(DEVTOOLS_POSITION_KEY);
46+
if (stored) return JSON.parse(stored);
47+
} catch {
48+
// ignore
49+
}
50+
return { x: 20, y: window.innerHeight - 140 };
51+
};
52+
2053
export const DevtoolsContainer = () => {
54+
const { colors } = useTheme<MaterializeTheme>();
55+
const [isExpanded, setIsExpanded] = useState(false);
56+
const [isDragging, setIsDragging] = useState(false);
57+
const [showRQDevtools, setShowRQDevtools] = useState(false);
58+
const [showJotaiDevtools, setShowJotaiDevtools] = useState(false);
59+
const [position, setPosition] = useState(getStoredPosition);
60+
61+
const buttonRef = useRef<HTMLDivElement>(null);
62+
63+
useDrag({
64+
ref: buttonRef,
65+
onStart: () => setIsDragging(true),
66+
onDrag: (_, { pointDelta }) => {
67+
if (!pointDelta) return;
68+
setPosition((prev) => ({
69+
x: clamp(prev.x + pointDelta.x, 0, window.innerWidth - BUTTON_SIZE),
70+
y: clamp(prev.y + pointDelta.y, 0, window.innerHeight - BUTTON_SIZE),
71+
}));
72+
},
73+
onStop: () => {
74+
setIsDragging(false);
75+
setPosition((pos) => {
76+
localStorage.setItem(DEVTOOLS_POSITION_KEY, JSON.stringify(pos));
77+
return pos;
78+
});
79+
},
80+
});
81+
2182
return (
22-
<VStack
23-
position="fixed"
24-
bottom="4"
25-
right="4"
26-
zIndex={DEVTOOL_BUTTONS_Z_INDEX}
27-
>
28-
<ReactQueryDevtools buttonPosition="relative" />
29-
<JotaiDevtools position="bottom-right" store={getStore()} />
30-
<Suspense fallback={null}>
31-
<SwitchStackModal />
32-
</Suspense>
33-
</VStack>
83+
<>
84+
{(showRQDevtools || showJotaiDevtools) && (
85+
<VStack
86+
position="fixed"
87+
bottom="20"
88+
right="4"
89+
zIndex={DEVTOOL_BUTTONS_Z_INDEX + 2}
90+
spacing="2"
91+
align="flex-end"
92+
sx={{
93+
".jotai-devtools-trigger-button": {
94+
position: "relative !important",
95+
bottom: "unset !important",
96+
right: "unset !important",
97+
zIndex: "unset !important",
98+
},
99+
}}
100+
>
101+
{showRQDevtools && <ReactQueryDevtools buttonPosition="relative" />}
102+
{showJotaiDevtools && (
103+
<Suspense fallback={null}>
104+
<LazyJotaiDevtools />
105+
</Suspense>
106+
)}
107+
</VStack>
108+
)}
109+
110+
<Box
111+
position="fixed"
112+
left={`${position.x}px`}
113+
top={`${position.y}px`}
114+
zIndex={DEVTOOL_BUTTONS_Z_INDEX}
115+
onMouseEnter={() => {
116+
if (!isDragging) setIsExpanded(true);
117+
}}
118+
onMouseLeave={() => setIsExpanded(false)}
119+
>
120+
<Center
121+
ref={buttonRef}
122+
width={`${BUTTON_SIZE}px`}
123+
height={`${BUTTON_SIZE}px`}
124+
borderRadius="full"
125+
bg={colors.background.primary}
126+
border="1px"
127+
borderColor={colors.border.secondary}
128+
boxShadow="md"
129+
cursor="grab"
130+
opacity={0.7}
131+
_hover={{ opacity: 1 }}
132+
_active={{ cursor: "grabbing" }}
133+
transition="opacity 0.2s"
134+
>
135+
<MaterializeLogo markOnly height="4" width="4" />
136+
</Center>
137+
138+
{isExpanded && !isDragging && (
139+
<VStack
140+
position="absolute"
141+
bottom="100%"
142+
left="0"
143+
mb="1"
144+
p="3"
145+
bg={colors.background.primary}
146+
border="1px"
147+
borderColor={colors.border.primary}
148+
borderRadius="md"
149+
boxShadow="lg"
150+
spacing="3"
151+
align="stretch"
152+
minWidth="180px"
153+
>
154+
<Text
155+
fontSize="xs"
156+
fontWeight="600"
157+
color={colors.foreground.secondary}
158+
>
159+
Dev Tools
160+
</Text>
161+
162+
<HStack justify="space-between">
163+
<Text fontSize="xs">React Query</Text>
164+
<Switch
165+
size="sm"
166+
isChecked={showRQDevtools}
167+
onChange={() => setShowRQDevtools((v) => !v)}
168+
/>
169+
</HStack>
170+
171+
<HStack justify="space-between">
172+
<Text fontSize="xs">Jotai</Text>
173+
<Switch
174+
size="sm"
175+
isChecked={showJotaiDevtools}
176+
onChange={() => setShowJotaiDevtools((v) => !v)}
177+
/>
178+
</HStack>
179+
180+
<Box borderTop="1px" borderColor={colors.border.primary} pt="2">
181+
<Suspense fallback={null}>
182+
<SwitchStackModal />
183+
</Suspense>
184+
</Box>
185+
</VStack>
186+
)}
187+
</Box>
188+
</>
34189
);
35190
};

console/src/theme/index.tsx

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -190,9 +190,6 @@ const collapseOverflowOverrides: SystemStyleInterpolation = {
190190
};
191191

192192
const devtoolOverrides: SystemStyleInterpolation = {
193-
".jotai-devtools-trigger-button": {
194-
position: "initial !important",
195-
},
196193
// React query devtools button
197194
".tsqd-open-btn-container": {
198195
height: "64px",

0 commit comments

Comments
 (0)