@tanstack/devtools 0.0.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/LICENSE +21 -0
- package/dist/cjs/components/content-panel.cjs +28 -0
- package/dist/cjs/components/content-panel.cjs.map +1 -0
- package/dist/cjs/components/content-panel.d.cts +6 -0
- package/dist/cjs/components/logo.cjs +95 -0
- package/dist/cjs/components/logo.cjs.map +1 -0
- package/dist/cjs/components/logo.d.cts +1 -0
- package/dist/cjs/components/main-panel.cjs +31 -0
- package/dist/cjs/components/main-panel.cjs.map +1 -0
- package/dist/cjs/components/main-panel.d.cts +7 -0
- package/dist/cjs/components/tab-content.cjs +28 -0
- package/dist/cjs/components/tab-content.cjs.map +1 -0
- package/dist/cjs/components/tab-content.d.cts +2 -0
- package/dist/cjs/components/tabs.cjs +47 -0
- package/dist/cjs/components/tabs.cjs.map +1 -0
- package/dist/cjs/components/tabs.d.cts +5 -0
- package/dist/cjs/components/trigger.cjs +31 -0
- package/dist/cjs/components/trigger.cjs.map +1 -0
- package/dist/cjs/components/trigger.d.cts +5 -0
- package/dist/cjs/context/devtools-context.cjs +62 -0
- package/dist/cjs/context/devtools-context.cjs.map +1 -0
- package/dist/cjs/context/devtools-context.d.cts +20 -0
- package/dist/cjs/context/devtools-store.cjs +21 -0
- package/dist/cjs/context/devtools-store.cjs.map +1 -0
- package/dist/cjs/context/devtools-store.d.cts +53 -0
- package/dist/cjs/context/use-devtools-context.cjs +78 -0
- package/dist/cjs/context/use-devtools-context.cjs.map +1 -0
- package/dist/cjs/context/use-devtools-context.d.cts +36 -0
- package/dist/cjs/core.cjs +81 -0
- package/dist/cjs/core.cjs.map +1 -0
- package/dist/cjs/core.d.cts +13 -0
- package/dist/cjs/devtools.cjs +146 -0
- package/dist/cjs/devtools.cjs.map +1 -0
- package/dist/cjs/devtools.d.cts +1 -0
- package/dist/cjs/hooks/use-disable-tabbing.cjs +23 -0
- package/dist/cjs/hooks/use-disable-tabbing.cjs.map +1 -0
- package/dist/cjs/hooks/use-disable-tabbing.d.cts +6 -0
- package/dist/cjs/index.cjs +5 -0
- package/dist/cjs/index.cjs.map +1 -0
- package/dist/cjs/index.d.cts +3 -0
- package/dist/cjs/styles/tokens.cjs +51 -0
- package/dist/cjs/styles/tokens.cjs.map +1 -0
- package/dist/cjs/styles/tokens.d.cts +298 -0
- package/dist/cjs/styles/use-styles.cjs +256 -0
- package/dist/cjs/styles/use-styles.cjs.map +1 -0
- package/dist/cjs/styles/use-styles.d.cts +21 -0
- package/dist/cjs/tabs/index.cjs +19 -0
- package/dist/cjs/tabs/index.cjs.map +1 -0
- package/dist/cjs/tabs/index.d.cts +12 -0
- package/dist/cjs/tabs/plugins-tab.cjs +73 -0
- package/dist/cjs/tabs/plugins-tab.cjs.map +1 -0
- package/dist/cjs/tabs/plugins-tab.d.cts +1 -0
- package/dist/cjs/tabs/settings-tab.cjs +9 -0
- package/dist/cjs/tabs/settings-tab.cjs.map +1 -0
- package/dist/cjs/tabs/settings-tab.d.cts +1 -0
- package/dist/cjs/utils/sanitize.cjs +12 -0
- package/dist/cjs/utils/sanitize.cjs.map +1 -0
- package/dist/cjs/utils/sanitize.d.cts +1 -0
- package/dist/cjs/utils/storage.cjs +19 -0
- package/dist/cjs/utils/storage.cjs.map +1 -0
- package/dist/cjs/utils/storage.d.cts +5 -0
- package/dist/esm/components/content-panel.d.ts +6 -0
- package/dist/esm/components/content-panel.js +28 -0
- package/dist/esm/components/content-panel.js.map +1 -0
- package/dist/esm/components/logo.d.ts +1 -0
- package/dist/esm/components/logo.js +95 -0
- package/dist/esm/components/logo.js.map +1 -0
- package/dist/esm/components/main-panel.d.ts +7 -0
- package/dist/esm/components/main-panel.js +31 -0
- package/dist/esm/components/main-panel.js.map +1 -0
- package/dist/esm/components/tab-content.d.ts +2 -0
- package/dist/esm/components/tab-content.js +28 -0
- package/dist/esm/components/tab-content.js.map +1 -0
- package/dist/esm/components/tabs.d.ts +5 -0
- package/dist/esm/components/tabs.js +47 -0
- package/dist/esm/components/tabs.js.map +1 -0
- package/dist/esm/components/trigger.d.ts +5 -0
- package/dist/esm/components/trigger.js +31 -0
- package/dist/esm/components/trigger.js.map +1 -0
- package/dist/esm/context/devtools-context.d.ts +20 -0
- package/dist/esm/context/devtools-context.js +62 -0
- package/dist/esm/context/devtools-context.js.map +1 -0
- package/dist/esm/context/devtools-store.d.ts +53 -0
- package/dist/esm/context/devtools-store.js +21 -0
- package/dist/esm/context/devtools-store.js.map +1 -0
- package/dist/esm/context/use-devtools-context.d.ts +36 -0
- package/dist/esm/context/use-devtools-context.js +78 -0
- package/dist/esm/context/use-devtools-context.js.map +1 -0
- package/dist/esm/core.d.ts +13 -0
- package/dist/esm/core.js +80 -0
- package/dist/esm/core.js.map +1 -0
- package/dist/esm/devtools.d.ts +1 -0
- package/dist/esm/devtools.js +147 -0
- package/dist/esm/devtools.js.map +1 -0
- package/dist/esm/hooks/use-disable-tabbing.d.ts +6 -0
- package/dist/esm/hooks/use-disable-tabbing.js +23 -0
- package/dist/esm/hooks/use-disable-tabbing.js.map +1 -0
- package/dist/esm/index.d.ts +3 -0
- package/dist/esm/index.js +5 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/styles/tokens.d.ts +298 -0
- package/dist/esm/styles/tokens.js +51 -0
- package/dist/esm/styles/tokens.js.map +1 -0
- package/dist/esm/styles/use-styles.d.ts +21 -0
- package/dist/esm/styles/use-styles.js +239 -0
- package/dist/esm/styles/use-styles.js.map +1 -0
- package/dist/esm/tabs/index.d.ts +12 -0
- package/dist/esm/tabs/index.js +19 -0
- package/dist/esm/tabs/index.js.map +1 -0
- package/dist/esm/tabs/plugins-tab.d.ts +1 -0
- package/dist/esm/tabs/plugins-tab.js +73 -0
- package/dist/esm/tabs/plugins-tab.js.map +1 -0
- package/dist/esm/tabs/settings-tab.d.ts +1 -0
- package/dist/esm/tabs/settings-tab.js +9 -0
- package/dist/esm/tabs/settings-tab.js.map +1 -0
- package/dist/esm/utils/sanitize.d.ts +1 -0
- package/dist/esm/utils/sanitize.js +12 -0
- package/dist/esm/utils/sanitize.js.map +1 -0
- package/dist/esm/utils/storage.d.ts +5 -0
- package/dist/esm/utils/storage.js +19 -0
- package/dist/esm/utils/storage.js.map +1 -0
- package/package.json +69 -0
- package/src/components/content-panel.tsx +21 -0
- package/src/components/logo.tsx +820 -0
- package/src/components/main-panel.tsx +31 -0
- package/src/components/tab-content.tsx +20 -0
- package/src/components/tabs.tsx +50 -0
- package/src/components/trigger.tsx +34 -0
- package/src/context/devtools-context.tsx +93 -0
- package/src/context/devtools-store.ts +79 -0
- package/src/context/use-devtools-context.ts +95 -0
- package/src/core.tsx +71 -0
- package/src/devtools.tsx +146 -0
- package/src/hooks/use-disable-tabbing.ts +28 -0
- package/src/index.ts +3 -0
- package/src/styles/tokens.ts +305 -0
- package/src/styles/use-styles.ts +252 -0
- package/src/tabs/index.tsx +62 -0
- package/src/tabs/plugins-tab.tsx +47 -0
- package/src/tabs/settings-tab.tsx +3 -0
- package/src/utils/sanitize.ts +8 -0
- package/src/utils/storage.ts +12 -0
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import clsx from 'clsx'
|
|
2
|
+
import { useHeight } from '../context/use-devtools-context'
|
|
3
|
+
import { useStyles } from '../styles/use-styles'
|
|
4
|
+
import { TANSTACK_DEVTOOLS } from '../utils/storage'
|
|
5
|
+
import type { JSX } from 'solid-js/jsx-runtime'
|
|
6
|
+
import type { Accessor } from 'solid-js'
|
|
7
|
+
|
|
8
|
+
export const MainPanel = (props: {
|
|
9
|
+
isOpen: Accessor<boolean>
|
|
10
|
+
children: JSX.Element
|
|
11
|
+
isResizing: Accessor<boolean>
|
|
12
|
+
}) => {
|
|
13
|
+
const styles = useStyles()
|
|
14
|
+
const { height } = useHeight()
|
|
15
|
+
return (
|
|
16
|
+
<div
|
|
17
|
+
id={TANSTACK_DEVTOOLS}
|
|
18
|
+
style={{
|
|
19
|
+
height: height() + 'px',
|
|
20
|
+
}}
|
|
21
|
+
class={clsx(
|
|
22
|
+
styles().devtoolsPanelContainer,
|
|
23
|
+
styles().devtoolsPanelContainerAnimation(props.isOpen(), height()),
|
|
24
|
+
styles().devtoolsPanelContainerVisibility(props.isOpen()),
|
|
25
|
+
styles().devtoolsPanelContainerResizing(props.isResizing),
|
|
26
|
+
)}
|
|
27
|
+
>
|
|
28
|
+
{props.children}
|
|
29
|
+
</div>
|
|
30
|
+
)
|
|
31
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { createEffect, createSignal } from 'solid-js'
|
|
2
|
+
import { useDevtoolsState } from '../context/use-devtools-context'
|
|
3
|
+
import { tabs } from '../tabs'
|
|
4
|
+
import { useStyles } from '../styles/use-styles'
|
|
5
|
+
import type { JSX } from 'solid-js'
|
|
6
|
+
|
|
7
|
+
export const TabContent = () => {
|
|
8
|
+
const { state } = useDevtoolsState()
|
|
9
|
+
const styles = useStyles()
|
|
10
|
+
const [component, setComponent] = createSignal<JSX.Element | null>(
|
|
11
|
+
tabs.find((t) => t.id === state().activeTab)?.component() || null,
|
|
12
|
+
)
|
|
13
|
+
createEffect(() => {
|
|
14
|
+
setComponent(
|
|
15
|
+
tabs.find((t) => t.id === state().activeTab)?.component() || null,
|
|
16
|
+
)
|
|
17
|
+
})
|
|
18
|
+
|
|
19
|
+
return <div class={styles().tabContent}>{component()}</div>
|
|
20
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import clsx from 'clsx'
|
|
2
|
+
import { For } from 'solid-js'
|
|
3
|
+
import { useStyles } from '../styles/use-styles'
|
|
4
|
+
import { useDevtoolsState } from '../context/use-devtools-context'
|
|
5
|
+
import { tabs } from '../tabs'
|
|
6
|
+
|
|
7
|
+
interface TabsProps {
|
|
8
|
+
toggleOpen: () => void
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export const Tabs = (props: TabsProps) => {
|
|
12
|
+
const styles = useStyles()
|
|
13
|
+
const { state, setState } = useDevtoolsState()
|
|
14
|
+
|
|
15
|
+
return (
|
|
16
|
+
<div class={styles().tabContainer}>
|
|
17
|
+
<For each={tabs}>
|
|
18
|
+
{(tab) => (
|
|
19
|
+
<button
|
|
20
|
+
type="button"
|
|
21
|
+
onClick={() => setState({ activeTab: tab.id })}
|
|
22
|
+
class={clsx(styles().tab, { active: state().activeTab === tab.id })}
|
|
23
|
+
>
|
|
24
|
+
{tab.icon}
|
|
25
|
+
</button>
|
|
26
|
+
)}
|
|
27
|
+
</For>
|
|
28
|
+
<button
|
|
29
|
+
type="button"
|
|
30
|
+
class={clsx(styles().tab, 'close')}
|
|
31
|
+
onClick={() => props.toggleOpen()}
|
|
32
|
+
>
|
|
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
|
+
>
|
|
44
|
+
<path d="M18 6 6 18" />
|
|
45
|
+
<path d="m6 6 12 12" />
|
|
46
|
+
</svg>
|
|
47
|
+
</button>
|
|
48
|
+
</div>
|
|
49
|
+
)
|
|
50
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { createMemo } from 'solid-js'
|
|
2
|
+
import clsx from 'clsx'
|
|
3
|
+
import { useDevtoolsSettings } from '../context/use-devtools-context'
|
|
4
|
+
import { useStyles } from '../styles/use-styles'
|
|
5
|
+
import { TanStackLogo } from './logo'
|
|
6
|
+
import type { Accessor } from 'solid-js'
|
|
7
|
+
|
|
8
|
+
export const Trigger = ({
|
|
9
|
+
isOpen,
|
|
10
|
+
setIsOpen,
|
|
11
|
+
}: {
|
|
12
|
+
isOpen: Accessor<boolean>
|
|
13
|
+
setIsOpen: (isOpen: boolean) => void
|
|
14
|
+
}) => {
|
|
15
|
+
const { settings } = useDevtoolsSettings()
|
|
16
|
+
const styles = useStyles()
|
|
17
|
+
const buttonStyle = createMemo(() => {
|
|
18
|
+
return clsx(
|
|
19
|
+
styles().mainCloseBtn,
|
|
20
|
+
styles().mainCloseBtnPosition(settings().position),
|
|
21
|
+
styles().mainCloseBtnAnimation(isOpen()),
|
|
22
|
+
)
|
|
23
|
+
})
|
|
24
|
+
return (
|
|
25
|
+
<button
|
|
26
|
+
type="button"
|
|
27
|
+
aria-label="Open TanStack Devtools"
|
|
28
|
+
class={buttonStyle()}
|
|
29
|
+
onClick={() => setIsOpen(!isOpen())}
|
|
30
|
+
>
|
|
31
|
+
<TanStackLogo />
|
|
32
|
+
</button>
|
|
33
|
+
)
|
|
34
|
+
}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { createContext } from 'solid-js'
|
|
2
|
+
import { createStore } from 'solid-js/store'
|
|
3
|
+
import { tryParseJson } from '../utils/sanitize'
|
|
4
|
+
import {
|
|
5
|
+
TANSTACK_DEVTOOLS_SETTINGS,
|
|
6
|
+
TANSTACK_DEVTOOLS_STATE,
|
|
7
|
+
getStorageItem,
|
|
8
|
+
setStorageItem,
|
|
9
|
+
} from '../utils/storage'
|
|
10
|
+
import { initialState } from './devtools-store'
|
|
11
|
+
import type { DevtoolsStore } from './devtools-store'
|
|
12
|
+
import type { Setter } from 'solid-js'
|
|
13
|
+
import type { JSX } from 'solid-js/jsx-runtime'
|
|
14
|
+
|
|
15
|
+
export interface DevtoolsPlugin {
|
|
16
|
+
name: string | ((el: HTMLHeadingElement) => void)
|
|
17
|
+
id: string
|
|
18
|
+
render: (el: HTMLDivElement) => void
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export const DevtoolsContext = createContext<{
|
|
22
|
+
store: DevtoolsStore
|
|
23
|
+
setStore: Setter<DevtoolsStore>
|
|
24
|
+
}>()
|
|
25
|
+
|
|
26
|
+
interface ContextProps {
|
|
27
|
+
children: JSX.Element
|
|
28
|
+
plugins?: Array<DevtoolsPlugin>
|
|
29
|
+
config?: DevtoolsSettings
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const getSettings = () => {
|
|
33
|
+
const settingsString = getStorageItem(TANSTACK_DEVTOOLS_SETTINGS)
|
|
34
|
+
const settings = tryParseJson<DevtoolsStore['settings']>(settingsString)
|
|
35
|
+
return {
|
|
36
|
+
...settings,
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const getExistingStateFromStorage = (
|
|
41
|
+
config?: DevtoolsSettings,
|
|
42
|
+
plugins?: Array<DevtoolsPlugin>,
|
|
43
|
+
) => {
|
|
44
|
+
const existingState = getStorageItem(TANSTACK_DEVTOOLS_STATE)
|
|
45
|
+
const settings = getSettings()
|
|
46
|
+
|
|
47
|
+
const state: DevtoolsStore = {
|
|
48
|
+
...initialState,
|
|
49
|
+
plugins: plugins || [],
|
|
50
|
+
state: {
|
|
51
|
+
...initialState.state,
|
|
52
|
+
...(existingState ? JSON.parse(existingState) : {}),
|
|
53
|
+
},
|
|
54
|
+
settings: {
|
|
55
|
+
...initialState.settings,
|
|
56
|
+
...config,
|
|
57
|
+
...settings,
|
|
58
|
+
},
|
|
59
|
+
}
|
|
60
|
+
return state
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export type DevtoolsSettings = DevtoolsStore['settings']
|
|
64
|
+
|
|
65
|
+
export const DevtoolsProvider = (props: ContextProps) => {
|
|
66
|
+
const [store, setStore] = createStore(
|
|
67
|
+
getExistingStateFromStorage(props.config, props.plugins),
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
const value = {
|
|
71
|
+
store,
|
|
72
|
+
setStore: (
|
|
73
|
+
updater: (prev: DevtoolsStore) => DevtoolsStore | Partial<DevtoolsStore>,
|
|
74
|
+
) => {
|
|
75
|
+
const newState = updater(store)
|
|
76
|
+
const { settings, state: internalState } = newState
|
|
77
|
+
// Store user settings for dev tools into local storage
|
|
78
|
+
setStorageItem(TANSTACK_DEVTOOLS_SETTINGS, JSON.stringify(settings))
|
|
79
|
+
// Store general state into local storage
|
|
80
|
+
setStorageItem(TANSTACK_DEVTOOLS_STATE, JSON.stringify(internalState))
|
|
81
|
+
setStore((prev) => ({
|
|
82
|
+
...prev,
|
|
83
|
+
...newState,
|
|
84
|
+
}))
|
|
85
|
+
},
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
return (
|
|
89
|
+
<DevtoolsContext.Provider value={value}>
|
|
90
|
+
{props.children}
|
|
91
|
+
</DevtoolsContext.Provider>
|
|
92
|
+
)
|
|
93
|
+
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import type { TabName } from '../tabs'
|
|
2
|
+
import type { DevtoolsPlugin } from './devtools-context'
|
|
3
|
+
|
|
4
|
+
type ModifierKey = 'Alt' | 'Control' | 'Meta' | 'Shift'
|
|
5
|
+
type KeyboardKey = ModifierKey | (string & {})
|
|
6
|
+
|
|
7
|
+
type TriggerPosition =
|
|
8
|
+
| 'top-left'
|
|
9
|
+
| 'top-right'
|
|
10
|
+
| 'bottom-left'
|
|
11
|
+
| 'bottom-right'
|
|
12
|
+
| 'middle-left'
|
|
13
|
+
| 'middle-right'
|
|
14
|
+
|
|
15
|
+
export type DevtoolsStore = {
|
|
16
|
+
settings: {
|
|
17
|
+
/**
|
|
18
|
+
* Whether the dev tools should be open by default
|
|
19
|
+
* @default false
|
|
20
|
+
*/
|
|
21
|
+
defaultOpen: boolean
|
|
22
|
+
/**
|
|
23
|
+
* Whether the dev tools trigger should be hidden until the user hovers over it
|
|
24
|
+
* @default false
|
|
25
|
+
*/
|
|
26
|
+
hideUntilHover: boolean
|
|
27
|
+
/**
|
|
28
|
+
* The position of the trigger button
|
|
29
|
+
* @default "bottom-right"
|
|
30
|
+
*/
|
|
31
|
+
position: TriggerPosition
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* The location of the panel once it is open
|
|
35
|
+
* @default "bottom"
|
|
36
|
+
*/
|
|
37
|
+
panelLocation: 'top' | 'bottom'
|
|
38
|
+
/**
|
|
39
|
+
* The hotkey to open the dev tools
|
|
40
|
+
* @default "shift+a"
|
|
41
|
+
*/
|
|
42
|
+
openHotkey: Array<KeyboardKey>
|
|
43
|
+
/**
|
|
44
|
+
* Whether to require the URL flag to open the dev tools
|
|
45
|
+
* @default false
|
|
46
|
+
*/
|
|
47
|
+
requireUrlFlag: boolean
|
|
48
|
+
/**
|
|
49
|
+
* The URL flag to open the dev tools, used in conjunction with requireUrlFlag (if set to true)
|
|
50
|
+
* @default "tanstack-devtools"
|
|
51
|
+
*/
|
|
52
|
+
urlFlag: string
|
|
53
|
+
}
|
|
54
|
+
state: {
|
|
55
|
+
activeTab: TabName
|
|
56
|
+
height: number
|
|
57
|
+
activePlugin?: DevtoolsPlugin | undefined
|
|
58
|
+
persistOpen: boolean
|
|
59
|
+
}
|
|
60
|
+
plugins?: Array<DevtoolsPlugin>
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export const initialState: DevtoolsStore = {
|
|
64
|
+
settings: {
|
|
65
|
+
defaultOpen: false,
|
|
66
|
+
hideUntilHover: false,
|
|
67
|
+
position: 'bottom-right',
|
|
68
|
+
panelLocation: 'bottom',
|
|
69
|
+
openHotkey: ['Shift', 'A'],
|
|
70
|
+
requireUrlFlag: false,
|
|
71
|
+
urlFlag: 'tanstack-devtools',
|
|
72
|
+
},
|
|
73
|
+
state: {
|
|
74
|
+
activeTab: 'plugins',
|
|
75
|
+
height: 400,
|
|
76
|
+
activePlugin: undefined,
|
|
77
|
+
persistOpen: false,
|
|
78
|
+
},
|
|
79
|
+
}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { createMemo, useContext } from 'solid-js'
|
|
2
|
+
import { DevtoolsContext } from './devtools-context.jsx'
|
|
3
|
+
import type { DevtoolsPlugin } from './devtools-context.jsx'
|
|
4
|
+
/* import type { DevtoolsPlugin } from './devtools-context' */
|
|
5
|
+
import type { DevtoolsStore } from './devtools-store.js'
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Returns an object containing the current state and setState function of the ShellContext.
|
|
9
|
+
* Throws an error if used outside of a ShellContextProvider.
|
|
10
|
+
*/
|
|
11
|
+
const useDevtoolsContext = () => {
|
|
12
|
+
const context = useContext(DevtoolsContext)
|
|
13
|
+
if (context === undefined) {
|
|
14
|
+
throw new Error(
|
|
15
|
+
'useDevtoolsShellContext must be used within a ShellContextProvider',
|
|
16
|
+
)
|
|
17
|
+
}
|
|
18
|
+
return context
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export const usePlugins = () => {
|
|
22
|
+
const { store, setStore } = useDevtoolsContext()
|
|
23
|
+
|
|
24
|
+
const plugins = createMemo(() => store.plugins)
|
|
25
|
+
const activePlugin = createMemo(() => store.state.activePlugin)
|
|
26
|
+
|
|
27
|
+
const setActivePlugin = (plugin: DevtoolsPlugin) => {
|
|
28
|
+
setStore((prev) => ({
|
|
29
|
+
...prev,
|
|
30
|
+
state: {
|
|
31
|
+
...prev.state,
|
|
32
|
+
activePlugin: plugin,
|
|
33
|
+
},
|
|
34
|
+
}))
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
return { plugins, setActivePlugin, activePlugin }
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export const useDevtoolsState = () => {
|
|
41
|
+
const { store, setStore } = useDevtoolsContext()
|
|
42
|
+
const state = createMemo(() => store.state)
|
|
43
|
+
const setState = (newState: Partial<DevtoolsStore['state']>) => {
|
|
44
|
+
setStore((prev) => ({
|
|
45
|
+
...prev,
|
|
46
|
+
state: {
|
|
47
|
+
...prev.state,
|
|
48
|
+
...newState,
|
|
49
|
+
},
|
|
50
|
+
}))
|
|
51
|
+
}
|
|
52
|
+
return { state, setState }
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export const useDevtoolsSettings = () => {
|
|
56
|
+
const { store, setStore } = useDevtoolsContext()
|
|
57
|
+
|
|
58
|
+
const settings = createMemo(() => store.settings)
|
|
59
|
+
|
|
60
|
+
const setSettings = (newSettings: Partial<DevtoolsStore['settings']>) => {
|
|
61
|
+
setStore((prev) => ({
|
|
62
|
+
...prev,
|
|
63
|
+
settings: {
|
|
64
|
+
...prev.settings,
|
|
65
|
+
...newSettings,
|
|
66
|
+
},
|
|
67
|
+
}))
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
return { setSettings, settings }
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export const usePersistOpen = () => {
|
|
74
|
+
const { state, setState } = useDevtoolsState()
|
|
75
|
+
|
|
76
|
+
const persistOpen = createMemo(() => state().persistOpen)
|
|
77
|
+
|
|
78
|
+
const setPersistOpen = (value: boolean) => {
|
|
79
|
+
setState({ persistOpen: value })
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
return { persistOpen, setPersistOpen }
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export const useHeight = () => {
|
|
86
|
+
const { state, setState } = useDevtoolsState()
|
|
87
|
+
|
|
88
|
+
const height = createMemo(() => state().height)
|
|
89
|
+
|
|
90
|
+
const setHeight = (newHeight: number) => {
|
|
91
|
+
setState({ height: newHeight })
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
return { height, setHeight }
|
|
95
|
+
}
|
package/src/core.tsx
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { lazy } from 'solid-js'
|
|
2
|
+
import { Portal, render } from 'solid-js/web'
|
|
3
|
+
import { DevtoolsProvider } from './context/devtools-context'
|
|
4
|
+
import { initialState } from './context/devtools-store'
|
|
5
|
+
import type {
|
|
6
|
+
DevtoolsPlugin,
|
|
7
|
+
DevtoolsSettings,
|
|
8
|
+
} from './context/devtools-context'
|
|
9
|
+
|
|
10
|
+
export interface DevtoolsOptions {
|
|
11
|
+
options?: Partial<DevtoolsSettings>
|
|
12
|
+
plugins?: Array<DevtoolsPlugin>
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
class TanStackDevtoolsCore {
|
|
16
|
+
#options: DevtoolsSettings = {
|
|
17
|
+
...initialState.settings,
|
|
18
|
+
}
|
|
19
|
+
#plugins: Array<DevtoolsPlugin> = []
|
|
20
|
+
#isMounted = false
|
|
21
|
+
#dispose?: () => void
|
|
22
|
+
#Component: any
|
|
23
|
+
|
|
24
|
+
constructor(config: DevtoolsOptions) {
|
|
25
|
+
this.#plugins = config.plugins || []
|
|
26
|
+
this.#options = {
|
|
27
|
+
...this.#options,
|
|
28
|
+
...config.options,
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
mount<T extends HTMLElement>(el: T) {
|
|
33
|
+
if (this.#isMounted) {
|
|
34
|
+
throw new Error('Devtools is already mounted')
|
|
35
|
+
}
|
|
36
|
+
const mountTo = el
|
|
37
|
+
const dispose = render(() => {
|
|
38
|
+
this.#Component = lazy(() => import('./devtools'))
|
|
39
|
+
|
|
40
|
+
const Devtools = this.#Component
|
|
41
|
+
|
|
42
|
+
return (
|
|
43
|
+
<DevtoolsProvider plugins={this.#plugins} config={this.#options}>
|
|
44
|
+
<Portal mount={mountTo}>
|
|
45
|
+
<Devtools />
|
|
46
|
+
</Portal>
|
|
47
|
+
</DevtoolsProvider>
|
|
48
|
+
)
|
|
49
|
+
}, mountTo)
|
|
50
|
+
|
|
51
|
+
this.#isMounted = true
|
|
52
|
+
this.#dispose = dispose
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
unmount() {
|
|
56
|
+
if (!this.#isMounted) {
|
|
57
|
+
throw new Error('Devtools is not mounted')
|
|
58
|
+
}
|
|
59
|
+
this.#dispose?.()
|
|
60
|
+
this.#isMounted = false
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
setOptions(options: Partial<DevtoolsOptions>) {
|
|
64
|
+
this.#options = {
|
|
65
|
+
...this.#options,
|
|
66
|
+
...options,
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export { TanStackDevtoolsCore as TanStackRouterDevtoolsCore }
|
package/src/devtools.tsx
ADDED
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
import { createEffect, createSignal } from 'solid-js'
|
|
2
|
+
import { createShortcut } from '@solid-primitives/keyboard'
|
|
3
|
+
import {
|
|
4
|
+
useDevtoolsSettings,
|
|
5
|
+
useHeight,
|
|
6
|
+
usePersistOpen,
|
|
7
|
+
} from './context/use-devtools-context'
|
|
8
|
+
import { useDisableTabbing } from './hooks/use-disable-tabbing'
|
|
9
|
+
import { TANSTACK_DEVTOOLS } from './utils/storage'
|
|
10
|
+
import { Trigger } from './components/trigger'
|
|
11
|
+
import { MainPanel } from './components/main-panel'
|
|
12
|
+
import { ContentPanel } from './components/content-panel'
|
|
13
|
+
import { Tabs } from './components/tabs'
|
|
14
|
+
import { TabContent } from './components/tab-content'
|
|
15
|
+
|
|
16
|
+
export default function DevTools() {
|
|
17
|
+
const { settings } = useDevtoolsSettings()
|
|
18
|
+
const { setHeight } = useHeight()
|
|
19
|
+
const { persistOpen, setPersistOpen } = usePersistOpen()
|
|
20
|
+
const [rootEl, setRootEl] = createSignal<HTMLDivElement>()
|
|
21
|
+
const [isOpen, setIsOpen] = createSignal(
|
|
22
|
+
settings().defaultOpen || persistOpen(),
|
|
23
|
+
)
|
|
24
|
+
let panelRef: HTMLDivElement | undefined = undefined
|
|
25
|
+
const [isResizing, setIsResizing] = createSignal(false)
|
|
26
|
+
const toggleOpen = () => {
|
|
27
|
+
const open = isOpen()
|
|
28
|
+
setIsOpen(!open)
|
|
29
|
+
setPersistOpen(!open)
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// Used to resize the panel
|
|
33
|
+
const handleDragStart = (
|
|
34
|
+
panelElement: HTMLDivElement | undefined,
|
|
35
|
+
startEvent: MouseEvent,
|
|
36
|
+
) => {
|
|
37
|
+
if (startEvent.button !== 0) return // Only allow left click for drag
|
|
38
|
+
|
|
39
|
+
setIsResizing(true)
|
|
40
|
+
|
|
41
|
+
const dragInfo = {
|
|
42
|
+
originalHeight: panelElement?.getBoundingClientRect().height ?? 0,
|
|
43
|
+
pageY: startEvent.pageY,
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const run = (moveEvent: MouseEvent) => {
|
|
47
|
+
const delta = dragInfo.pageY - moveEvent.pageY
|
|
48
|
+
const newHeight = dragInfo.originalHeight + delta
|
|
49
|
+
|
|
50
|
+
setHeight(newHeight)
|
|
51
|
+
|
|
52
|
+
if (newHeight < 70) {
|
|
53
|
+
setIsOpen(false)
|
|
54
|
+
} else {
|
|
55
|
+
setIsOpen(true)
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const unsub = () => {
|
|
60
|
+
setIsResizing(false)
|
|
61
|
+
document.removeEventListener('mousemove', run)
|
|
62
|
+
document.removeEventListener('mouseUp', unsub)
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
document.addEventListener('mousemove', run)
|
|
66
|
+
document.addEventListener('mouseup', unsub)
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// Handle resizing and padding adjustments
|
|
70
|
+
createEffect(() => {
|
|
71
|
+
if (isOpen()) {
|
|
72
|
+
const previousValue = rootEl()?.parentElement?.style.paddingBottom
|
|
73
|
+
|
|
74
|
+
const run = () => {
|
|
75
|
+
const containerHeight = panelRef!.getBoundingClientRect().height
|
|
76
|
+
if (rootEl()?.parentElement) {
|
|
77
|
+
setRootEl((prev) => {
|
|
78
|
+
if (prev?.parentElement) {
|
|
79
|
+
prev.parentElement.style.paddingBottom = `${containerHeight}px`
|
|
80
|
+
}
|
|
81
|
+
return prev
|
|
82
|
+
})
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
run()
|
|
87
|
+
|
|
88
|
+
if (typeof window !== 'undefined') {
|
|
89
|
+
window.addEventListener('resize', run)
|
|
90
|
+
|
|
91
|
+
return () => {
|
|
92
|
+
window.removeEventListener('resize', run)
|
|
93
|
+
if (rootEl()?.parentElement && typeof previousValue === 'string') {
|
|
94
|
+
setRootEl((prev) => {
|
|
95
|
+
prev!.parentElement!.style.paddingBottom = previousValue
|
|
96
|
+
return prev
|
|
97
|
+
})
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
} else {
|
|
102
|
+
// Reset padding when devtools are closed
|
|
103
|
+
if (rootEl()?.parentElement) {
|
|
104
|
+
setRootEl((prev) => {
|
|
105
|
+
if (prev?.parentElement) {
|
|
106
|
+
prev.parentElement.removeAttribute('style')
|
|
107
|
+
}
|
|
108
|
+
return prev
|
|
109
|
+
})
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
return
|
|
113
|
+
})
|
|
114
|
+
createEffect(() => {
|
|
115
|
+
window.addEventListener('keydown', (e) => {
|
|
116
|
+
if (e.key === 'Escape' && isOpen()) {
|
|
117
|
+
toggleOpen()
|
|
118
|
+
}
|
|
119
|
+
})
|
|
120
|
+
})
|
|
121
|
+
useDisableTabbing(isOpen)
|
|
122
|
+
createEffect(() => {
|
|
123
|
+
if (rootEl()) {
|
|
124
|
+
const el = rootEl()
|
|
125
|
+
const fontSize = getComputedStyle(el!).fontSize
|
|
126
|
+
el?.style.setProperty('--tsrd-font-size', fontSize)
|
|
127
|
+
}
|
|
128
|
+
})
|
|
129
|
+
createShortcut(settings().openHotkey, () => {
|
|
130
|
+
toggleOpen()
|
|
131
|
+
})
|
|
132
|
+
return (
|
|
133
|
+
<div ref={setRootEl} data-testid={TANSTACK_DEVTOOLS}>
|
|
134
|
+
<Trigger isOpen={isOpen} setIsOpen={toggleOpen} />
|
|
135
|
+
<MainPanel isResizing={isResizing} isOpen={isOpen}>
|
|
136
|
+
<ContentPanel
|
|
137
|
+
ref={(ref) => (panelRef = ref)}
|
|
138
|
+
handleDragStart={(e) => handleDragStart(panelRef, e)}
|
|
139
|
+
>
|
|
140
|
+
<Tabs toggleOpen={toggleOpen} />
|
|
141
|
+
<TabContent />
|
|
142
|
+
</ContentPanel>
|
|
143
|
+
</MainPanel>
|
|
144
|
+
</div>
|
|
145
|
+
)
|
|
146
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { createEffect } from 'solid-js'
|
|
2
|
+
import { TANSTACK_DEVTOOLS } from '../utils/storage'
|
|
3
|
+
|
|
4
|
+
const recursivelyChangeTabIndex = (
|
|
5
|
+
node: Element | HTMLElement,
|
|
6
|
+
remove = true,
|
|
7
|
+
) => {
|
|
8
|
+
if (remove) {
|
|
9
|
+
node.setAttribute('tabIndex', '-1')
|
|
10
|
+
} else {
|
|
11
|
+
node.removeAttribute('tabIndex')
|
|
12
|
+
}
|
|
13
|
+
for (const child of node.children) {
|
|
14
|
+
recursivelyChangeTabIndex(child, remove)
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* @param isOpen A function that returns whether the devtools are open or not.
|
|
19
|
+
* This is used to disable tabbing over the main devtools container while
|
|
20
|
+
* the devtools are closed.
|
|
21
|
+
*/
|
|
22
|
+
export const useDisableTabbing = (isOpen: () => boolean) => {
|
|
23
|
+
createEffect(() => {
|
|
24
|
+
const el = document.getElementById(TANSTACK_DEVTOOLS)
|
|
25
|
+
if (!el) return
|
|
26
|
+
recursivelyChangeTabIndex(el, !isOpen())
|
|
27
|
+
})
|
|
28
|
+
}
|
package/src/index.ts
ADDED