@tanstack/devtools 0.5.1 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/esm/components/main-panel.js +8 -2
- package/dist/esm/components/main-panel.js.map +1 -1
- package/dist/esm/components/tabs.js +10 -0
- package/dist/esm/components/tabs.js.map +1 -1
- package/dist/esm/context/draw-context.d.ts +13 -0
- package/dist/esm/context/draw-context.js +55 -0
- package/dist/esm/context/draw-context.js.map +1 -0
- package/dist/esm/context/use-devtools-context.js +10 -1
- package/dist/esm/context/use-devtools-context.js.map +1 -1
- package/dist/esm/hooks/use-head-changes.d.ts +39 -0
- package/dist/esm/hooks/use-head-changes.js +65 -0
- package/dist/esm/hooks/use-head-changes.js.map +1 -0
- package/dist/esm/styles/tokens.js +4 -1
- package/dist/esm/styles/tokens.js.map +1 -1
- package/dist/esm/styles/use-styles.d.ts +19 -0
- package/dist/esm/styles/use-styles.js +143 -3
- package/dist/esm/styles/use-styles.js.map +1 -1
- package/dist/esm/tabs/index.d.ts +5 -0
- package/dist/esm/tabs/index.js +8 -2
- package/dist/esm/tabs/index.js.map +1 -1
- package/dist/esm/tabs/plugins-tab.js +31 -13
- package/dist/esm/tabs/plugins-tab.js.map +1 -1
- package/dist/esm/tabs/seo-tab.d.ts +1 -0
- package/dist/esm/tabs/seo-tab.js +291 -0
- package/dist/esm/tabs/seo-tab.js.map +1 -0
- package/package.json +1 -1
- package/src/components/main-panel.tsx +5 -1
- package/src/components/tabs.tsx +9 -0
- package/src/context/draw-context.tsx +67 -0
- package/src/context/use-devtools-context.ts +12 -2
- package/src/hooks/use-head-changes.ts +110 -0
- package/src/styles/use-styles.ts +148 -3
- package/src/tabs/index.tsx +25 -0
- package/src/tabs/plugins-tab.tsx +51 -23
- package/src/tabs/seo-tab.tsx +238 -0
package/src/components/tabs.tsx
CHANGED
|
@@ -2,6 +2,7 @@ import clsx from 'clsx'
|
|
|
2
2
|
import { For } from 'solid-js'
|
|
3
3
|
import { useStyles } from '../styles/use-styles'
|
|
4
4
|
import { useDevtoolsState } from '../context/use-devtools-context'
|
|
5
|
+
import { useDrawContext } from '../context/draw-context'
|
|
5
6
|
import { tabs } from '../tabs'
|
|
6
7
|
import { usePiPWindow } from '../context/pip-context'
|
|
7
8
|
|
|
@@ -18,6 +19,8 @@ export const Tabs = (props: TabsProps) => {
|
|
|
18
19
|
`width=${window.innerWidth},height=${state().height},top=${window.screen.height},left=${window.screenLeft}}`,
|
|
19
20
|
)
|
|
20
21
|
}
|
|
22
|
+
const { hoverUtils } = useDrawContext()
|
|
23
|
+
|
|
21
24
|
return (
|
|
22
25
|
<div class={styles().tabContainer}>
|
|
23
26
|
<For each={tabs}>
|
|
@@ -26,6 +29,12 @@ export const Tabs = (props: TabsProps) => {
|
|
|
26
29
|
type="button"
|
|
27
30
|
onClick={() => setState({ activeTab: tab.id })}
|
|
28
31
|
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
|
+
}}
|
|
29
38
|
>
|
|
30
39
|
{tab.icon}
|
|
31
40
|
</button>
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import {
|
|
2
|
+
createContext,
|
|
3
|
+
createMemo,
|
|
4
|
+
createSignal,
|
|
5
|
+
onCleanup,
|
|
6
|
+
useContext,
|
|
7
|
+
} from 'solid-js'
|
|
8
|
+
import type { ParentComponent } from 'solid-js'
|
|
9
|
+
|
|
10
|
+
const useDraw = (props: { animationMs: number }) => {
|
|
11
|
+
const [activeHover, setActiveHover] = createSignal<boolean>(false)
|
|
12
|
+
const [forceExpand, setForceExpand] = createSignal<boolean>(false)
|
|
13
|
+
|
|
14
|
+
const expanded = createMemo(() => activeHover() || forceExpand())
|
|
15
|
+
|
|
16
|
+
let hoverTimeout: ReturnType<typeof setTimeout> | null = null
|
|
17
|
+
onCleanup(() => {
|
|
18
|
+
if (hoverTimeout) clearTimeout(hoverTimeout)
|
|
19
|
+
})
|
|
20
|
+
|
|
21
|
+
const hoverUtils = {
|
|
22
|
+
enter: () => {
|
|
23
|
+
if (hoverTimeout) {
|
|
24
|
+
clearTimeout(hoverTimeout)
|
|
25
|
+
hoverTimeout = null
|
|
26
|
+
}
|
|
27
|
+
setActiveHover(true)
|
|
28
|
+
},
|
|
29
|
+
|
|
30
|
+
leave: () => {
|
|
31
|
+
hoverTimeout = setTimeout(() => {
|
|
32
|
+
setActiveHover(false)
|
|
33
|
+
}, props.animationMs)
|
|
34
|
+
},
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
return {
|
|
38
|
+
expanded,
|
|
39
|
+
setForceExpand,
|
|
40
|
+
hoverUtils,
|
|
41
|
+
animationMs: props.animationMs,
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
type ContextType = ReturnType<typeof useDraw>
|
|
46
|
+
|
|
47
|
+
const DrawContext = createContext<ContextType | undefined>(undefined)
|
|
48
|
+
|
|
49
|
+
export const DrawClientProvider: ParentComponent<{
|
|
50
|
+
animationMs: number
|
|
51
|
+
}> = (props) => {
|
|
52
|
+
const value = useDraw({ animationMs: props.animationMs })
|
|
53
|
+
|
|
54
|
+
return (
|
|
55
|
+
<DrawContext.Provider value={value}>{props.children}</DrawContext.Provider>
|
|
56
|
+
)
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export function useDrawContext() {
|
|
60
|
+
const context = useContext(DrawContext)
|
|
61
|
+
|
|
62
|
+
if (context === undefined) {
|
|
63
|
+
throw new Error(`useDrawContext must be used within a DrawClientProvider`)
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
return context
|
|
67
|
+
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import { createMemo, useContext } from 'solid-js'
|
|
1
|
+
import { createEffect, createMemo, useContext } from 'solid-js'
|
|
2
2
|
import { DevtoolsContext } from './devtools-context.jsx'
|
|
3
|
-
|
|
3
|
+
import { useDrawContext } from './draw-context.jsx'
|
|
4
|
+
|
|
4
5
|
import type { DevtoolsStore } from './devtools-store.js'
|
|
5
6
|
|
|
6
7
|
/**
|
|
@@ -19,10 +20,19 @@ const useDevtoolsContext = () => {
|
|
|
19
20
|
|
|
20
21
|
export const usePlugins = () => {
|
|
21
22
|
const { store, setStore } = useDevtoolsContext()
|
|
23
|
+
const { setForceExpand } = useDrawContext()
|
|
22
24
|
|
|
23
25
|
const plugins = createMemo(() => store.plugins)
|
|
24
26
|
const activePlugin = createMemo(() => store.state.activePlugin)
|
|
25
27
|
|
|
28
|
+
createEffect(() => {
|
|
29
|
+
if (activePlugin() == null) {
|
|
30
|
+
setForceExpand(false)
|
|
31
|
+
} else {
|
|
32
|
+
setForceExpand(true)
|
|
33
|
+
}
|
|
34
|
+
})
|
|
35
|
+
|
|
26
36
|
const setActivePlugin = (pluginId: string) => {
|
|
27
37
|
setStore((prev) => ({
|
|
28
38
|
...prev,
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import { onCleanup, onMount } from 'solid-js'
|
|
2
|
+
|
|
3
|
+
type HeadChange =
|
|
4
|
+
| { kind: 'added'; node: Node }
|
|
5
|
+
| { kind: 'removed'; node: Node }
|
|
6
|
+
| {
|
|
7
|
+
kind: 'attr'
|
|
8
|
+
target: Element
|
|
9
|
+
name: string | null
|
|
10
|
+
oldValue: string | null
|
|
11
|
+
}
|
|
12
|
+
| { kind: 'title'; title: string }
|
|
13
|
+
|
|
14
|
+
type UseHeadChangesOptions = {
|
|
15
|
+
/**
|
|
16
|
+
* Observe attribute changes on elements inside <head>
|
|
17
|
+
* Default: true
|
|
18
|
+
*/
|
|
19
|
+
attributes?: boolean
|
|
20
|
+
/**
|
|
21
|
+
* Observe added/removed nodes in <head>
|
|
22
|
+
* Default: true
|
|
23
|
+
*/
|
|
24
|
+
childList?: boolean
|
|
25
|
+
/**
|
|
26
|
+
* Observe descendants of <head>
|
|
27
|
+
* Default: true
|
|
28
|
+
*/
|
|
29
|
+
subtree?: boolean
|
|
30
|
+
/**
|
|
31
|
+
* Also observe <title> changes explicitly
|
|
32
|
+
* Default: true
|
|
33
|
+
*/
|
|
34
|
+
observeTitle?: boolean
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export function useHeadChanges(
|
|
38
|
+
onChange: (change: HeadChange, raw?: MutationRecord) => void,
|
|
39
|
+
opts: UseHeadChangesOptions = {},
|
|
40
|
+
) {
|
|
41
|
+
const {
|
|
42
|
+
attributes = true,
|
|
43
|
+
childList = true,
|
|
44
|
+
subtree = true,
|
|
45
|
+
observeTitle = true,
|
|
46
|
+
} = opts
|
|
47
|
+
|
|
48
|
+
onMount(() => {
|
|
49
|
+
const headObserver = new MutationObserver((mutations) => {
|
|
50
|
+
for (const m of mutations) {
|
|
51
|
+
if (m.type === 'childList') {
|
|
52
|
+
m.addedNodes.forEach((node) => onChange({ kind: 'added', node }, m))
|
|
53
|
+
m.removedNodes.forEach((node) =>
|
|
54
|
+
onChange({ kind: 'removed', node }, m),
|
|
55
|
+
)
|
|
56
|
+
} else if (m.type === 'attributes') {
|
|
57
|
+
const el = m.target as Element
|
|
58
|
+
onChange(
|
|
59
|
+
{
|
|
60
|
+
kind: 'attr',
|
|
61
|
+
target: el,
|
|
62
|
+
name: m.attributeName,
|
|
63
|
+
oldValue: m.oldValue ?? null,
|
|
64
|
+
},
|
|
65
|
+
m,
|
|
66
|
+
)
|
|
67
|
+
} else {
|
|
68
|
+
// If someone mutates a Text node inside <title>, surface it as a title change.
|
|
69
|
+
const isInTitle =
|
|
70
|
+
m.target.parentNode &&
|
|
71
|
+
(m.target.parentNode as Element).tagName.toLowerCase() === 'title'
|
|
72
|
+
if (isInTitle) onChange({ kind: 'title', title: document.title }, m)
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
})
|
|
76
|
+
|
|
77
|
+
headObserver.observe(document.head, {
|
|
78
|
+
childList,
|
|
79
|
+
attributes,
|
|
80
|
+
subtree,
|
|
81
|
+
attributeOldValue: attributes,
|
|
82
|
+
characterData: true, // helps catch <title> text node edits
|
|
83
|
+
characterDataOldValue: false,
|
|
84
|
+
})
|
|
85
|
+
|
|
86
|
+
// Extra explicit observer for <title>, since `document.title = "..."`
|
|
87
|
+
// may not always bubble as a head mutation in all setups.
|
|
88
|
+
let titleObserver: MutationObserver | undefined
|
|
89
|
+
if (observeTitle) {
|
|
90
|
+
const titleEl =
|
|
91
|
+
document.head.querySelector('title') ||
|
|
92
|
+
// create a <title> if missing so future changes are observable
|
|
93
|
+
document.head.appendChild(document.createElement('title'))
|
|
94
|
+
|
|
95
|
+
titleObserver = new MutationObserver(() => {
|
|
96
|
+
onChange({ kind: 'title', title: document.title })
|
|
97
|
+
})
|
|
98
|
+
titleObserver.observe(titleEl, {
|
|
99
|
+
childList: true,
|
|
100
|
+
characterData: true,
|
|
101
|
+
subtree: true,
|
|
102
|
+
})
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
onCleanup(() => {
|
|
106
|
+
headObserver.disconnect()
|
|
107
|
+
titleObserver?.disconnect()
|
|
108
|
+
})
|
|
109
|
+
})
|
|
110
|
+
}
|
package/src/styles/use-styles.ts
CHANGED
|
@@ -4,12 +4,133 @@ import { tokens } from './tokens'
|
|
|
4
4
|
import type { TanStackDevtoolsConfig } from '../context/devtools-context'
|
|
5
5
|
import type { Accessor } from 'solid-js'
|
|
6
6
|
|
|
7
|
+
const mSecondsToCssSeconds = (mSeconds: number) =>
|
|
8
|
+
`${(mSeconds / 1000).toFixed(2)}s`
|
|
9
|
+
|
|
7
10
|
const stylesFactory = () => {
|
|
8
11
|
const { colors, font, size, alpha, border } = tokens
|
|
9
12
|
const { fontFamily, size: fontSize } = font
|
|
10
13
|
const css = goober.css
|
|
11
14
|
|
|
12
15
|
return {
|
|
16
|
+
seoTabContainer: css`
|
|
17
|
+
padding: 0;
|
|
18
|
+
margin: 0 auto;
|
|
19
|
+
background: ${colors.darkGray[700]};
|
|
20
|
+
border-radius: 12px;
|
|
21
|
+
box-shadow: 0 2px 16px rgba(0, 0, 0, 0.08);
|
|
22
|
+
overflow-y: auto;
|
|
23
|
+
height: 100%;
|
|
24
|
+
display: flex;
|
|
25
|
+
flex-direction: column;
|
|
26
|
+
gap: 0;
|
|
27
|
+
width: 100%;
|
|
28
|
+
overflow-y: auto;
|
|
29
|
+
`,
|
|
30
|
+
seoTabTitle: css`
|
|
31
|
+
font-size: 1.25rem;
|
|
32
|
+
font-weight: 600;
|
|
33
|
+
color: ${colors.purple[400]};
|
|
34
|
+
margin: 0;
|
|
35
|
+
padding: 1rem 1.5rem 0.5rem 1.5rem;
|
|
36
|
+
text-align: left;
|
|
37
|
+
border-bottom: 1px solid ${colors.gray[700]};
|
|
38
|
+
`,
|
|
39
|
+
seoTabSection: css`
|
|
40
|
+
padding: 1.5rem;
|
|
41
|
+
background: ${colors.darkGray[800]};
|
|
42
|
+
border: 1px solid ${colors.gray[700]};
|
|
43
|
+
display: flex;
|
|
44
|
+
flex-direction: column;
|
|
45
|
+
gap: 0.5rem;
|
|
46
|
+
margin: 1.5rem;
|
|
47
|
+
margin-bottom: 2rem;
|
|
48
|
+
border-radius: 0.75rem;
|
|
49
|
+
`,
|
|
50
|
+
seoPreviewSection: css`
|
|
51
|
+
display: flex;
|
|
52
|
+
flex-direction: row;
|
|
53
|
+
gap: 16px;
|
|
54
|
+
margin-bottom: 0;
|
|
55
|
+
justify-content: flex-start;
|
|
56
|
+
align-items: flex-start;
|
|
57
|
+
overflow-x: auto;
|
|
58
|
+
flex-wrap: wrap;
|
|
59
|
+
padding-bottom: 0.5rem;
|
|
60
|
+
`,
|
|
61
|
+
seoPreviewCard: css`
|
|
62
|
+
border: 1px solid ${colors.gray[700]};
|
|
63
|
+
border-radius: 8px;
|
|
64
|
+
padding: 12px 10px;
|
|
65
|
+
background: ${colors.darkGray[900]};
|
|
66
|
+
margin-bottom: 0;
|
|
67
|
+
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.04);
|
|
68
|
+
display: flex;
|
|
69
|
+
flex-direction: column;
|
|
70
|
+
align-items: flex-start;
|
|
71
|
+
min-width: 200px;
|
|
72
|
+
max-width: 240px;
|
|
73
|
+
font-size: 0.95rem;
|
|
74
|
+
gap: 4px;
|
|
75
|
+
`,
|
|
76
|
+
seoPreviewHeader: css`
|
|
77
|
+
font-size: 1rem;
|
|
78
|
+
font-weight: 500;
|
|
79
|
+
margin-bottom: 6px;
|
|
80
|
+
color: ${colors.purple[400]};
|
|
81
|
+
`,
|
|
82
|
+
seoPreviewImage: css`
|
|
83
|
+
max-width: 100%;
|
|
84
|
+
border-radius: 6px;
|
|
85
|
+
margin-bottom: 6px;
|
|
86
|
+
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.06);
|
|
87
|
+
height: 160px;
|
|
88
|
+
`,
|
|
89
|
+
seoPreviewTitle: css`
|
|
90
|
+
font-size: 1rem;
|
|
91
|
+
font-weight: 600;
|
|
92
|
+
margin-bottom: 2px;
|
|
93
|
+
color: ${colors.gray[100]};
|
|
94
|
+
`,
|
|
95
|
+
seoPreviewDesc: css`
|
|
96
|
+
color: ${colors.gray[300]};
|
|
97
|
+
margin-bottom: 2px;
|
|
98
|
+
font-size: 0.95rem;
|
|
99
|
+
`,
|
|
100
|
+
seoPreviewUrl: css`
|
|
101
|
+
color: ${colors.gray[500]};
|
|
102
|
+
font-size: 0.9rem;
|
|
103
|
+
margin-bottom: 2px;
|
|
104
|
+
word-break: break-all;
|
|
105
|
+
`,
|
|
106
|
+
seoMissingTagsSection: css`
|
|
107
|
+
margin-top: 4px;
|
|
108
|
+
font-size: 0.95rem;
|
|
109
|
+
color: ${colors.red[400]};
|
|
110
|
+
`,
|
|
111
|
+
seoMissingTagsList: css`
|
|
112
|
+
margin: 4px 0 0 0;
|
|
113
|
+
padding: 0;
|
|
114
|
+
list-style: none;
|
|
115
|
+
display: flex;
|
|
116
|
+
flex-wrap: wrap;
|
|
117
|
+
gap: 6px;
|
|
118
|
+
max-width: 240px;
|
|
119
|
+
`,
|
|
120
|
+
seoMissingTag: css`
|
|
121
|
+
background: ${colors.red[500]}22;
|
|
122
|
+
color: ${colors.red[500]};
|
|
123
|
+
border-radius: 4px;
|
|
124
|
+
padding: 1px 6px;
|
|
125
|
+
font-size: 0.9rem;
|
|
126
|
+
font-weight: 500;
|
|
127
|
+
`,
|
|
128
|
+
seoAllTagsFound: css`
|
|
129
|
+
color: ${colors.green[500]};
|
|
130
|
+
font-weight: 500;
|
|
131
|
+
margin-left: 6px;
|
|
132
|
+
font-size: 0.95rem;
|
|
133
|
+
`,
|
|
13
134
|
devtoolsPanelContainer: (
|
|
14
135
|
panelLocation: TanStackDevtoolsConfig['panelLocation'],
|
|
15
136
|
isDetached: boolean,
|
|
@@ -231,13 +352,37 @@ const stylesFactory = () => {
|
|
|
231
352
|
height: 100%;
|
|
232
353
|
overflow: hidden;
|
|
233
354
|
`,
|
|
234
|
-
|
|
235
|
-
|
|
355
|
+
|
|
356
|
+
pluginsTabDraw: css`
|
|
357
|
+
width: 0px;
|
|
358
|
+
height: 100%;
|
|
236
359
|
background-color: ${colors.darkGray[800]};
|
|
237
|
-
border-right: 1px solid ${colors.gray[700]};
|
|
238
360
|
box-shadow: 0 1px 0 ${colors.gray[700]};
|
|
361
|
+
`,
|
|
362
|
+
pluginsTabDrawExpanded: css`
|
|
363
|
+
width: ${size[48]};
|
|
364
|
+
border-right: 1px solid ${colors.gray[700]};
|
|
365
|
+
`,
|
|
366
|
+
pluginsTabDrawTransition: (mSeconds: number) => {
|
|
367
|
+
return css`
|
|
368
|
+
transition: width ${mSecondsToCssSeconds(mSeconds)} ease;
|
|
369
|
+
`
|
|
370
|
+
},
|
|
371
|
+
|
|
372
|
+
pluginsTabSidebar: css`
|
|
373
|
+
width: ${size[48]};
|
|
239
374
|
overflow-y: auto;
|
|
375
|
+
transform: translateX(-100%);
|
|
376
|
+
`,
|
|
377
|
+
pluginsTabSidebarExpanded: css`
|
|
378
|
+
transform: translateX(0);
|
|
240
379
|
`,
|
|
380
|
+
pluginsTabSidebarTransition: (mSeconds: number) => {
|
|
381
|
+
return css`
|
|
382
|
+
transition: transform ${mSecondsToCssSeconds(mSeconds)} ease;
|
|
383
|
+
`
|
|
384
|
+
},
|
|
385
|
+
|
|
241
386
|
pluginName: css`
|
|
242
387
|
font-size: ${fontSize.xs};
|
|
243
388
|
font-family: ${fontFamily.sans};
|
package/src/tabs/index.tsx
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { SettingsTab } from './settings-tab'
|
|
2
2
|
import { PluginsTab } from './plugins-tab'
|
|
3
|
+
import { SeoTab } from './seo-tab'
|
|
3
4
|
|
|
4
5
|
export const tabs = [
|
|
5
6
|
{
|
|
@@ -24,6 +25,30 @@ export const tabs = [
|
|
|
24
25
|
</svg>
|
|
25
26
|
),
|
|
26
27
|
},
|
|
28
|
+
{
|
|
29
|
+
name: 'SEO',
|
|
30
|
+
id: 'seo',
|
|
31
|
+
component: () => <SeoTab />,
|
|
32
|
+
icon: (
|
|
33
|
+
<svg
|
|
34
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
35
|
+
width="24"
|
|
36
|
+
height="24"
|
|
37
|
+
viewBox="0 0 24 24"
|
|
38
|
+
fill="none"
|
|
39
|
+
stroke="currentColor"
|
|
40
|
+
stroke-width="2"
|
|
41
|
+
stroke-linecap="round"
|
|
42
|
+
stroke-linejoin="round"
|
|
43
|
+
class="lucide lucide-file-search2-icon lucide-file-search-2"
|
|
44
|
+
>
|
|
45
|
+
<path d="M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z" />
|
|
46
|
+
<path d="M14 2v4a2 2 0 0 0 2 2h4" />
|
|
47
|
+
<circle cx="11.5" cy="14.5" r="2.5" />
|
|
48
|
+
<path d="M13.3 16.3 15 18" />
|
|
49
|
+
</svg>
|
|
50
|
+
),
|
|
51
|
+
},
|
|
27
52
|
{
|
|
28
53
|
name: 'Settings',
|
|
29
54
|
id: 'settings',
|
package/src/tabs/plugins-tab.tsx
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
import { For, createEffect } from 'solid-js'
|
|
2
2
|
import clsx from 'clsx'
|
|
3
|
+
import { useDrawContext } from '../context/draw-context'
|
|
3
4
|
import { usePlugins } from '../context/use-devtools-context'
|
|
4
5
|
import { useStyles } from '../styles/use-styles'
|
|
5
6
|
import { PLUGIN_CONTAINER_ID, PLUGIN_TITLE_CONTAINER_ID } from '../constants'
|
|
6
7
|
|
|
7
8
|
export const PluginsTab = () => {
|
|
8
9
|
const { plugins, activePlugin, setActivePlugin } = usePlugins()
|
|
10
|
+
const { expanded, hoverUtils, animationMs } = useDrawContext()
|
|
9
11
|
let activePluginRef: HTMLDivElement | undefined
|
|
10
12
|
|
|
11
13
|
createEffect(() => {
|
|
@@ -17,32 +19,58 @@ export const PluginsTab = () => {
|
|
|
17
19
|
}
|
|
18
20
|
})
|
|
19
21
|
const styles = useStyles()
|
|
22
|
+
|
|
20
23
|
return (
|
|
21
24
|
<div class={styles().pluginsTabPanel}>
|
|
22
|
-
<div
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
25
|
+
<div
|
|
26
|
+
class={clsx(
|
|
27
|
+
styles().pluginsTabDraw,
|
|
28
|
+
{
|
|
29
|
+
[styles().pluginsTabDrawExpanded]: expanded(),
|
|
30
|
+
},
|
|
31
|
+
styles().pluginsTabDrawTransition(animationMs),
|
|
32
|
+
)}
|
|
33
|
+
onMouseEnter={() => {
|
|
34
|
+
hoverUtils.enter()
|
|
35
|
+
}}
|
|
36
|
+
onMouseLeave={() => {
|
|
37
|
+
hoverUtils.leave()
|
|
38
|
+
}}
|
|
39
|
+
>
|
|
40
|
+
<div
|
|
41
|
+
class={clsx(
|
|
42
|
+
styles().pluginsTabSidebar,
|
|
43
|
+
{
|
|
44
|
+
[styles().pluginsTabSidebarExpanded]: expanded(),
|
|
45
|
+
},
|
|
46
|
+
styles().pluginsTabSidebarTransition(animationMs),
|
|
47
|
+
)}
|
|
48
|
+
>
|
|
49
|
+
<For each={plugins()}>
|
|
50
|
+
{(plugin) => {
|
|
51
|
+
let pluginHeading: HTMLHeadingElement | undefined
|
|
52
|
+
createEffect(() => {
|
|
53
|
+
if (pluginHeading) {
|
|
54
|
+
typeof plugin.name === 'string'
|
|
55
|
+
? (pluginHeading.textContent = plugin.name)
|
|
56
|
+
: plugin.name(pluginHeading)
|
|
57
|
+
}
|
|
58
|
+
})
|
|
59
|
+
return (
|
|
60
|
+
<div
|
|
61
|
+
onClick={() => setActivePlugin(plugin.id!)}
|
|
62
|
+
class={clsx(styles().pluginName, {
|
|
63
|
+
active: activePlugin() === plugin.id,
|
|
64
|
+
})}
|
|
65
|
+
>
|
|
66
|
+
<h3 id={PLUGIN_TITLE_CONTAINER_ID} ref={pluginHeading} />
|
|
67
|
+
</div>
|
|
68
|
+
)
|
|
69
|
+
}}
|
|
70
|
+
</For>
|
|
71
|
+
</div>
|
|
45
72
|
</div>
|
|
73
|
+
|
|
46
74
|
<div
|
|
47
75
|
id={PLUGIN_CONTAINER_ID}
|
|
48
76
|
ref={activePluginRef}
|