Skip to content
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions .changeset/rare-moles-rest.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
'@tanstack/react-devtools': minor
'@tanstack/devtools': minor
---

added optional trigger component in config

removed trigger image setting completely
5 changes: 5 additions & 0 deletions examples/solid/basic/src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,11 @@ function App() {
<Router />

<TanStackDevtools
config={{
customTrigger: () => <h1>
hello world
</h1>
}}
plugins={[
{
name: 'TanStack Query',
Expand Down
37 changes: 27 additions & 10 deletions packages/devtools/src/components/trigger.tsx
Original file line number Diff line number Diff line change
@@ -1,38 +1,55 @@
import { Show, createMemo } from 'solid-js'
import { Show, createEffect, createMemo, createSignal } from 'solid-js'
import clsx from 'clsx'
import { useDevtoolsSettings } from '../context/use-devtools-context'
import { useStyles } from '../styles/use-styles'
import TanStackLogo from './tanstack-logo.png'
import type { Accessor } from 'solid-js'

export const Trigger = ({
isOpen,
setIsOpen,
image = TanStackLogo,
}: {
export const Trigger = (props: {
isOpen: Accessor<boolean>
setIsOpen: (isOpen: boolean) => void
image: string
}) => {
const { settings } = useDevtoolsSettings()
const [containerRef, setContainerRef] = createSignal<HTMLElement>()
const styles = useStyles()
const buttonStyle = createMemo(() => {
return clsx(
styles().mainCloseBtn,
styles().mainCloseBtnPosition(settings().position),
styles().mainCloseBtnAnimation(isOpen(), settings().hideUntilHover),
styles().mainCloseBtnAnimation(props.isOpen(), settings().hideUntilHover),
)
})

createEffect(() => {
const triggerComponent = settings().customTrigger
const el = containerRef()
if (triggerComponent && el) {
triggerComponent(el, {
theme: settings().theme,

})
}
})

return (
<Show when={!settings().triggerHidden}>
<button
type="button"
aria-label="Open TanStack Devtools"
class={buttonStyle()}
onClick={() => setIsOpen(!isOpen())}
onClick={() => props.setIsOpen(!props.isOpen())}
>
<img src={image || TanStackLogo} alt="TanStack Devtools" />
<Show
when={settings().customTrigger}
fallback={
<img src={TanStackLogo} alt="TanStack Devtools" />
}
>
<div ref={setContainerRef} />
</Show>

</button>

</Show>
)
}
18 changes: 12 additions & 6 deletions packages/devtools/src/context/devtools-store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@ type TriggerPosition =
| 'middle-left'
| 'middle-right'

type TriggerProps = {
theme: 'light' | 'dark'
}

export type DevtoolsStore = {
settings: {
/**
Expand Down Expand Up @@ -61,15 +65,17 @@ export type DevtoolsStore = {
* @default "dark"
*/
theme: 'light' | 'dark'
/**
* The image used for the dev tools trigger
* @default TanStackLogo
*/
triggerImage: string

/**
* Whether the trigger should be completely hidden or not (you can still open with the hotkey)
*/
triggerHidden?: boolean
/**
* An optional custom function to render the dev tools trigger component.
* If provided, it replaces the default trigger button.
* @default undefined
*/
customTrigger?: (el: HTMLElement, props: TriggerProps) => void
}
state: {
activeTab: TabName
Expand All @@ -95,8 +101,8 @@ export const initialState: DevtoolsStore = {
window.matchMedia('(prefers-color-scheme: dark)').matches
? 'dark'
: 'light',
triggerImage: '',
triggerHidden: false,
customTrigger: undefined,
},
state: {
activeTab: 'plugins',
Expand Down
5 changes: 2 additions & 3 deletions packages/devtools/src/devtools.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -100,10 +100,10 @@ export default function DevTools() {
run()

if (typeof window !== 'undefined') {
;(pip().pipWindow ?? window).addEventListener('resize', run)
; (pip().pipWindow ?? window).addEventListener('resize', run)

return () => {
;(pip().pipWindow ?? window).removeEventListener('resize', run)
; (pip().pipWindow ?? window).removeEventListener('resize', run)
if (rootEl()?.parentElement && typeof previousValue === 'string') {
setRootEl((prev) => {
// prev!.parentElement!.style.paddingBottom = previousValue
Expand Down Expand Up @@ -178,7 +178,6 @@ export default function DevTools() {
<Trigger
isOpen={isOpen}
setIsOpen={toggleOpen}
image={settings().triggerImage}
/>
<MainPanel isResizing={isResizing} isOpen={isOpen}>
<ContentPanel
Expand Down
8 changes: 1 addition & 7 deletions packages/devtools/src/tabs/settings-tab.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -79,13 +79,7 @@ export const SettingsTab = () => {
}
checked={settings().triggerHidden}
/>
<Input
label="Trigger Image"
description="Specify the URL of the image to use for the trigger"
value={settings().triggerImage}
placeholder="Default TanStack Logo"
onChange={(value) => setSettings({ triggerImage: value })}
/>

<Select
label="Theme"
description="Choose the theme for the devtools panel"
Expand Down
116 changes: 86 additions & 30 deletions packages/react-devtools/src/devtools.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,15 @@ type PluginRender =
| JSX.Element
| ((el: HTMLElement, theme: 'dark' | 'light') => JSX.Element)

type TriggerProps = {
theme: 'dark' | 'light'

}

type TriggerRender =
| JSX.Element
| ((el: HTMLElement, props: TriggerProps) => JSX.Element)

export type TanStackDevtoolsReactPlugin = Omit<
TanStackDevtoolsPlugin,
'render' | 'name'
Expand Down Expand Up @@ -57,6 +66,24 @@ export type TanStackDevtoolsReactPlugin = Omit<
name: string | PluginRender
}

type TanStackDevtoolsReactConfig = Omit<
Partial<TanStackDevtoolsConfig>,
'customTrigger'
> & {
/**
* Optional custom trigger component for the devtools.
* It can be a React element or a function that renders one.
*
* Example:
* ```jsx
* {
* customTrigger: <CustomTriggerComponent />,
* }
* ```
*/
customTrigger?: TriggerRender
}

export interface TanStackDevtoolsReactInit {
/**
* Array of plugins to be used in the devtools.
Expand All @@ -81,7 +108,7 @@ export interface TanStackDevtoolsReactInit {
* initial state of the devtools when it is started for the first time. Afterwards,
* the settings are persisted in local storage and changed through the settings panel.
*/
config?: Partial<TanStackDevtoolsConfig>
config?: TanStackDevtoolsReactConfig
/**
* Configuration for the TanStack Devtools client event bus.
*/
Expand All @@ -105,6 +132,17 @@ const convertRender = (
}))
}

const convertTrigger = (
Component: TriggerRender,
setComponent: React.Dispatch<React.SetStateAction<JSX.Element | null>>,
e: HTMLElement,
props: TriggerProps,
) => {
const element =
typeof Component === 'function' ? Component(e, props) : Component
setComponent(element)
}

export const TanStackDevtools = ({
plugins,
config,
Expand All @@ -118,13 +156,19 @@ export const TanStackDevtools = ({
const [titleContainers, setTitleContainers] = useState<
Record<string, HTMLElement>
>({})
const [triggerContainer, setTriggerContainer] = useState<HTMLElement | null>(
null,
)

const [PluginComponents, setPluginComponents] = useState<
Record<string, JSX.Element>
>({})
const [TitleComponents, setTitleComponents] = useState<
Record<string, JSX.Element>
>({})
const [TriggerComponent, setTriggerComponent] = useState<JSX.Element | null>(
null,
)

const pluginsMap: Array<TanStackDevtoolsPlugin> = useMemo(
() =>
Expand All @@ -135,23 +179,23 @@ export const TanStackDevtools = ({
typeof plugin.name === 'string'
? plugin.name
: (e, theme) => {
const id = e.getAttribute('id')!
const target = e.ownerDocument.getElementById(id)

if (target) {
setTitleContainers((prev) => ({
...prev,
[id]: e,
}))
}

convertRender(
plugin.name as PluginRender,
setTitleComponents,
e,
theme,
)
},
const id = e.getAttribute('id')!
const target = e.ownerDocument.getElementById(id)

if (target) {
setTitleContainers((prev) => ({
...prev,
[id]: e,
}))
}

convertRender(
plugin.name as PluginRender,
setTitleComponents,
e,
theme,
)
},
render: (e, theme) => {
const id = e.getAttribute('id')!
const target = e.ownerDocument.getElementById(id)
Expand All @@ -170,14 +214,22 @@ export const TanStackDevtools = ({
[plugins],
)

const [devtools] = useState(
() =>
new TanStackDevtoolsCore({
config,
eventBusConfig,
plugins: pluginsMap,
}),
)
const [devtools] = useState(() => {
const { customTrigger, ...coreConfig } = config || {}
return new TanStackDevtoolsCore({
config: {
...coreConfig,
customTrigger: customTrigger
? (el, props) => {
setTriggerContainer(el)
convertTrigger(customTrigger, setTriggerComponent, el, props)
}
: undefined,
},
eventBusConfig,
plugins: pluginsMap,
})
})

useEffect(() => {
devtools.setConfig({
Expand Down Expand Up @@ -206,14 +258,18 @@ export const TanStackDevtools = ({

{hasPlugins
? Object.entries(pluginContainers).map(([key, pluginContainer]) =>
createPortal(<>{PluginComponents[key]}</>, pluginContainer),
)
createPortal(<>{PluginComponents[key]}</>, pluginContainer),
)
: null}

{hasTitles
? Object.entries(titleContainers).map(([key, titleContainer]) =>
createPortal(<>{TitleComponents[key]}</>, titleContainer),
)
createPortal(<>{TitleComponents[key]}</>, titleContainer),
)
: null}

{triggerContainer && TriggerComponent
? createPortal(<>{TriggerComponent}</>, triggerContainer)
: null}
</>
)
Expand Down
Loading
Loading