|  | 
|  | 1 | +import { | 
|  | 2 | +  createEffect, | 
|  | 3 | +  createMemo, | 
|  | 4 | +  createSignal, | 
|  | 5 | +  onCleanup, | 
|  | 6 | +  onMount, | 
|  | 7 | +  sharedConfig, | 
|  | 8 | +  splitProps, | 
|  | 9 | +  untrack, | 
|  | 10 | +} from 'solid-js' | 
|  | 11 | +import { onlineManager, useQueryClient } from '@tanstack/solid-query' | 
|  | 12 | +import { isServer } from 'solid-js/web' | 
|  | 13 | +import { TanstackQueryDevtools } from '@tanstack/query-devtools' | 
|  | 14 | +import type { | 
|  | 15 | +  DevToolsErrorType, | 
|  | 16 | +  DevtoolsButtonPosition, | 
|  | 17 | +  DevtoolsPosition, | 
|  | 18 | +} from '@tanstack/query-devtools' | 
|  | 19 | +import type { QueryClient } from '@tanstack/solid-query' | 
|  | 20 | +import type { Component, ComponentProps, JSX } from 'solid-js' | 
|  | 21 | + | 
|  | 22 | +export interface DevtoolsOptions { | 
|  | 23 | +  /** | 
|  | 24 | +   * Set this true if you want the dev tools to default to being open | 
|  | 25 | +   */ | 
|  | 26 | +  initialIsOpen?: boolean | 
|  | 27 | +  /** | 
|  | 28 | +   * The position of the React Query logo to open and close the devtools panel. | 
|  | 29 | +   * 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right' | 
|  | 30 | +   * Defaults to 'bottom-left'. | 
|  | 31 | +   */ | 
|  | 32 | +  buttonPosition?: DevtoolsButtonPosition | 
|  | 33 | +  /** | 
|  | 34 | +   * The position of the React Query devtools panel. | 
|  | 35 | +   * 'top' | 'bottom' | 'left' | 'right' | 
|  | 36 | +   * Defaults to 'bottom'. | 
|  | 37 | +   */ | 
|  | 38 | +  position?: DevtoolsPosition | 
|  | 39 | +  /** | 
|  | 40 | +   * Custom instance of QueryClient | 
|  | 41 | +   */ | 
|  | 42 | +  client?: QueryClient | 
|  | 43 | +  /** | 
|  | 44 | +   * Use this so you can define custom errors that can be shown in the devtools. | 
|  | 45 | +   */ | 
|  | 46 | +  errorTypes?: Array<DevToolsErrorType> | 
|  | 47 | +} | 
|  | 48 | + | 
|  | 49 | +export default function SolidQueryDevtools(props: DevtoolsOptions) { | 
|  | 50 | +  const queryClient = useQueryClient() | 
|  | 51 | +  const client = createMemo(() => props.client || queryClient) | 
|  | 52 | +  let ref!: HTMLDivElement | 
|  | 53 | +  const devtools = new TanstackQueryDevtools({ | 
|  | 54 | +    client: client(), | 
|  | 55 | +    queryFlavor: 'Solid Query', | 
|  | 56 | +    version: '5', | 
|  | 57 | +    onlineManager, | 
|  | 58 | +    buttonPosition: props.buttonPosition, | 
|  | 59 | +    position: props.position, | 
|  | 60 | +    initialIsOpen: props.initialIsOpen, | 
|  | 61 | +    errorTypes: props.errorTypes, | 
|  | 62 | +  }) | 
|  | 63 | + | 
|  | 64 | +  createEffect(() => { | 
|  | 65 | +    devtools.setClient(client()) | 
|  | 66 | +  }) | 
|  | 67 | + | 
|  | 68 | +  createEffect(() => { | 
|  | 69 | +    const buttonPos = props.buttonPosition | 
|  | 70 | +    if (buttonPos) { | 
|  | 71 | +      devtools.setButtonPosition(buttonPos) | 
|  | 72 | +    } | 
|  | 73 | +  }) | 
|  | 74 | + | 
|  | 75 | +  createEffect(() => { | 
|  | 76 | +    const pos = props.position | 
|  | 77 | +    if (pos) { | 
|  | 78 | +      devtools.setPosition(pos) | 
|  | 79 | +    } | 
|  | 80 | +  }) | 
|  | 81 | + | 
|  | 82 | +  createEffect(() => { | 
|  | 83 | +    devtools.setInitialIsOpen(props.initialIsOpen || false) | 
|  | 84 | +  }) | 
|  | 85 | + | 
|  | 86 | +  createEffect(() => { | 
|  | 87 | +    devtools.setErrorTypes(props.errorTypes || []) | 
|  | 88 | +  }) | 
|  | 89 | + | 
|  | 90 | +  onMount(() => { | 
|  | 91 | +    devtools.mount(ref) | 
|  | 92 | +    onCleanup(() => devtools.unmount()) | 
|  | 93 | +  }) | 
|  | 94 | + | 
|  | 95 | +  return <div class="tsqd-parent-container" ref={ref}></div> | 
|  | 96 | +} | 
|  | 97 | + | 
|  | 98 | +/*  | 
|  | 99 | +  This function has been taken from solid-start's codebase | 
|  | 100 | +  This allows the devtools to be loaded only on the client and bypasses any server side rendering | 
|  | 101 | +  https://github.com/solidjs/solid-start/blob/2967fc2db3f0df826f061020231dbdafdfa0746b/packages/start/islands/clientOnly.tsx | 
|  | 102 | +*/ | 
|  | 103 | +export function clientOnly<T extends Component<any>>( | 
|  | 104 | +  fn: () => Promise<{ | 
|  | 105 | +    default: T | 
|  | 106 | +  }>, | 
|  | 107 | +) { | 
|  | 108 | +  if (isServer) | 
|  | 109 | +    return (props: ComponentProps<T> & { fallback?: JSX.Element }) => | 
|  | 110 | +      props.fallback | 
|  | 111 | + | 
|  | 112 | +  const [comp, setComp] = createSignal<T>() | 
|  | 113 | +  fn().then((m) => setComp(() => m.default)) | 
|  | 114 | +  return (props: ComponentProps<T>) => { | 
|  | 115 | +    let Comp: T | undefined | 
|  | 116 | +    let m: boolean | 
|  | 117 | +    const [, rest] = splitProps(props, ['fallback']) | 
|  | 118 | +    if ((Comp = comp()) && !sharedConfig.context) return Comp(rest) | 
|  | 119 | +    const [mounted, setMounted] = createSignal(!sharedConfig.context) | 
|  | 120 | +    onMount(() => setMounted(true)) | 
|  | 121 | +    return createMemo( | 
|  | 122 | +      () => ( | 
|  | 123 | +        (Comp = comp()), | 
|  | 124 | +        (m = mounted()), | 
|  | 125 | +        untrack(() => (Comp && m ? Comp(rest) : props.fallback)) | 
|  | 126 | +      ), | 
|  | 127 | +    ) | 
|  | 128 | +  } | 
|  | 129 | +} | 
0 commit comments