Skip to content

Commit 433a47f

Browse files
feat(core): collapsable plugin tabs (#77)
Adds collapsable plugin tabs, to the devtoolss panel, for increased devtool real-estate.
1 parent 17b78c2 commit 433a47f

File tree

5 files changed

+118
-27
lines changed

5 files changed

+118
-27
lines changed

packages/devtools/src/components/main-panel.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
import clsx from 'clsx'
2+
import { DrawClientProvider } from '../context/draw-context'
23
import { useDevtoolsSettings, useHeight } from '../context/use-devtools-context'
34
import { useStyles } from '../styles/use-styles'
45
import { TANSTACK_DEVTOOLS } from '../utils/storage'
56
import { usePiPWindow } from '../context/pip-context'
7+
68
import type { Accessor, JSX } from 'solid-js'
79

810
export const MainPanel = (props: {
@@ -30,7 +32,7 @@ export const MainPanel = (props: {
3032
styles().devtoolsPanelContainerResizing(props.isResizing),
3133
)}
3234
>
33-
{props.children}
35+
<DrawClientProvider>{props.children}</DrawClientProvider>
3436
</div>
3537
)
3638
}

packages/devtools/src/components/tabs.tsx

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import clsx from 'clsx'
22
import { For } from 'solid-js'
33
import { useStyles } from '../styles/use-styles'
44
import { useDevtoolsState } from '../context/use-devtools-context'
5+
import { useDrawContext } from '../context/draw-context'
56
import { tabs } from '../tabs'
67
import { usePiPWindow } from '../context/pip-context'
78

@@ -18,6 +19,8 @@ export const Tabs = (props: TabsProps) => {
1819
`width=${window.innerWidth},height=${state().height},top=${window.screen.height},left=${window.screenLeft}}`,
1920
)
2021
}
22+
const { hoverUtils } = useDrawContext()
23+
2124
return (
2225
<div class={styles().tabContainer}>
2326
<For each={tabs}>
@@ -26,6 +29,12 @@ export const Tabs = (props: TabsProps) => {
2629
type="button"
2730
onClick={() => setState({ activeTab: tab.id })}
2831
class={clsx(styles().tab, { active: state().activeTab === tab.id })}
32+
onMouseEnter={() => {
33+
if (tab.id === 'plugins') hoverUtils.enter()
34+
}}
35+
onMouseLeave={() => {
36+
if (tab.id === 'plugins') hoverUtils.leave()
37+
}}
2938
>
3039
{tab.icon}
3140
</button>
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import { createContext, createSignal, useContext } from 'solid-js'
2+
import type { ParentComponent } from 'solid-js'
3+
4+
const useDraw = () => {
5+
const [activeMenuHover, setActiveMenuHover] = createSignal<boolean>(false)
6+
let hoverTimeout: ReturnType<typeof setTimeout> | null = null
7+
8+
const hoverUtils = {
9+
enter: () => {
10+
if (hoverTimeout) {
11+
clearTimeout(hoverTimeout)
12+
hoverTimeout = null
13+
}
14+
setActiveMenuHover(true)
15+
},
16+
17+
leave: () => {
18+
hoverTimeout = setTimeout(() => {
19+
setActiveMenuHover(false)
20+
}, 400)
21+
},
22+
}
23+
24+
return { activeMenuHover, hoverUtils }
25+
}
26+
27+
type ContextType = ReturnType<typeof useDraw>
28+
29+
const DrawContext = createContext<ContextType | undefined>(undefined)
30+
31+
export const DrawClientProvider: ParentComponent = (props) => {
32+
const value = useDraw()
33+
34+
return (
35+
<DrawContext.Provider value={value}>{props.children}</DrawContext.Provider>
36+
)
37+
}
38+
39+
export function useDrawContext() {
40+
const context = useContext(DrawContext)
41+
42+
if (context === undefined) {
43+
throw new Error(`useDrawContext must be used within a DrawClientProvider`)
44+
}
45+
46+
return context
47+
}

packages/devtools/src/styles/use-styles.ts

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -231,12 +231,25 @@ const stylesFactory = () => {
231231
height: 100%;
232232
overflow: hidden;
233233
`,
234-
pluginsTabSidebar: css`
235-
width: ${size[48]};
234+
pluginsTabDraw: css`
235+
width: 0px;
236+
height: 100%;
236237
background-color: ${colors.darkGray[800]};
237-
border-right: 1px solid ${colors.gray[700]};
238238
box-shadow: 0 1px 0 ${colors.gray[700]};
239+
transition: width 0.3s ease;
240+
`,
241+
pluginsTabDrawExpanded: css`
242+
width: ${size[48]};
243+
border-right: 1px solid ${colors.gray[700]};
244+
`,
245+
pluginsTabSidebar: css`
246+
width: ${size[48]};
239247
overflow-y: auto;
248+
transition: transform 0.3s ease;
249+
transform: translateX(-100%);
250+
`,
251+
pluginsTabSidebarExpanded: css`
252+
transform: translateX(0);
240253
`,
241254
pluginName: css`
242255
font-size: ${fontSize.xs};

packages/devtools/src/tabs/plugins-tab.tsx

Lines changed: 43 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
import { For, createEffect } from 'solid-js'
22
import clsx from 'clsx'
3+
import { useDrawContext } from '../context/draw-context'
34
import { usePlugins } from '../context/use-devtools-context'
45
import { useStyles } from '../styles/use-styles'
56
import { PLUGIN_CONTAINER_ID, PLUGIN_TITLE_CONTAINER_ID } from '../constants'
67

78
export const PluginsTab = () => {
89
const { plugins, activePlugin, setActivePlugin } = usePlugins()
10+
const { activeMenuHover, hoverUtils } = useDrawContext()
911
let activePluginRef: HTMLDivElement | undefined
1012

1113
createEffect(() => {
@@ -17,32 +19,50 @@ export const PluginsTab = () => {
1719
}
1820
})
1921
const styles = useStyles()
22+
2023
return (
2124
<div class={styles().pluginsTabPanel}>
22-
<div class={styles().pluginsTabSidebar}>
23-
<For each={plugins()}>
24-
{(plugin) => {
25-
let pluginHeading: HTMLHeadingElement | undefined
26-
createEffect(() => {
27-
if (pluginHeading) {
28-
typeof plugin.name === 'string'
29-
? (pluginHeading.textContent = plugin.name)
30-
: plugin.name(pluginHeading)
31-
}
32-
})
33-
return (
34-
<div
35-
onClick={() => setActivePlugin(plugin.id!)}
36-
class={clsx(styles().pluginName, {
37-
active: activePlugin() === plugin.id,
38-
})}
39-
>
40-
<h3 id={PLUGIN_TITLE_CONTAINER_ID} ref={pluginHeading} />
41-
</div>
42-
)
43-
}}
44-
</For>
25+
<div
26+
class={clsx(styles().pluginsTabDraw, {
27+
[styles().pluginsTabDrawExpanded]: activeMenuHover(),
28+
})}
29+
onMouseEnter={() => {
30+
hoverUtils.enter()
31+
}}
32+
onMouseLeave={() => {
33+
hoverUtils.leave()
34+
}}
35+
>
36+
<div
37+
class={clsx(styles().pluginsTabSidebar, {
38+
[styles().pluginsTabSidebarExpanded]: activeMenuHover(),
39+
})}
40+
>
41+
<For each={plugins()}>
42+
{(plugin) => {
43+
let pluginHeading: HTMLHeadingElement | undefined
44+
createEffect(() => {
45+
if (pluginHeading) {
46+
typeof plugin.name === 'string'
47+
? (pluginHeading.textContent = plugin.name)
48+
: plugin.name(pluginHeading)
49+
}
50+
})
51+
return (
52+
<div
53+
onClick={() => setActivePlugin(plugin.id!)}
54+
class={clsx(styles().pluginName, {
55+
active: activePlugin() === plugin.id,
56+
})}
57+
>
58+
<h3 id={PLUGIN_TITLE_CONTAINER_ID} ref={pluginHeading} />
59+
</div>
60+
)
61+
}}
62+
</For>
63+
</div>
4564
</div>
65+
4666
<div
4767
id={PLUGIN_CONTAINER_ID}
4868
ref={activePluginRef}

0 commit comments

Comments
 (0)