@tanstack/react-devtools 0.6.6 → 0.6.8

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.
@@ -1,11 +1,13 @@
1
1
  import { jsxs, Fragment, jsx } from "react/jsx-runtime";
2
2
  import { useRef, useState, useEffect } from "react";
3
- import { TanStackDevtoolsCore, PLUGIN_CONTAINER_ID, PLUGIN_TITLE_CONTAINER_ID } from "@tanstack/devtools";
3
+ import { TanStackDevtoolsCore } from "@tanstack/devtools";
4
4
  import { createPortal } from "react-dom";
5
- const convertRender = (Component, setComponent, e, theme) => {
6
- setComponent(
7
- typeof Component === "function" ? Component(e, theme) : Component
8
- );
5
+ const convertRender = (Component, setComponents, e, theme) => {
6
+ const element = typeof Component === "function" ? Component(e, theme) : Component;
7
+ setComponents((prev) => ({
8
+ ...prev,
9
+ [e.getAttribute("id")]: element
10
+ }));
9
11
  };
10
12
  const TanStackDevtools = ({
11
13
  plugins,
@@ -13,14 +15,10 @@ const TanStackDevtools = ({
13
15
  eventBusConfig
14
16
  }) => {
15
17
  const devToolRef = useRef(null);
16
- const [pluginContainer, setPluginContainer] = useState(
17
- null
18
- );
19
- const [titleContainer, setTitleContainer] = useState(null);
20
- const [PluginComponent, setPluginComponent] = useState(
21
- null
22
- );
23
- const [TitleComponent, setTitleComponent] = useState(null);
18
+ const [pluginContainers, setPluginContainers] = useState({});
19
+ const [titleContainers, setTitleContainers] = useState({});
20
+ const [PluginComponents, setPluginComponents] = useState({});
21
+ const [TitleComponents, setTitleComponents] = useState({});
24
22
  const [devtools] = useState(
25
23
  () => new TanStackDevtoolsCore({
26
24
  config,
@@ -28,27 +26,32 @@ const TanStackDevtools = ({
28
26
  plugins: plugins?.map((plugin) => {
29
27
  return {
30
28
  ...plugin,
31
- name: typeof plugin.name === "string" ? plugin.name : (
32
- // The check above confirms that `plugin.name` is of Render type
33
- (e, theme) => {
34
- setTitleContainer(
35
- e.ownerDocument.getElementById(
36
- PLUGIN_TITLE_CONTAINER_ID
37
- ) || null
38
- );
39
- convertRender(
40
- plugin.name,
41
- setTitleComponent,
42
- e,
43
- theme
44
- );
29
+ name: typeof plugin.name === "string" ? plugin.name : (e, theme) => {
30
+ const id = e.getAttribute("id");
31
+ const target = e.ownerDocument.getElementById(id);
32
+ if (target) {
33
+ setTitleContainers((prev) => ({
34
+ ...prev,
35
+ [id]: e
36
+ }));
45
37
  }
46
- ),
47
- render: (e, theme) => {
48
- setPluginContainer(
49
- e.ownerDocument.getElementById(PLUGIN_CONTAINER_ID) || null
38
+ convertRender(
39
+ plugin.name,
40
+ setTitleComponents,
41
+ e,
42
+ theme
50
43
  );
51
- convertRender(plugin.render, setPluginComponent, e, theme);
44
+ },
45
+ render: (e, theme) => {
46
+ const id = e.getAttribute("id");
47
+ const target = e.ownerDocument.getElementById(id);
48
+ if (target) {
49
+ setPluginContainers((prev) => ({
50
+ ...prev,
51
+ [id]: e
52
+ }));
53
+ }
54
+ convertRender(plugin.render, setPluginComponents, e, theme);
52
55
  }
53
56
  };
54
57
  })
@@ -60,10 +63,16 @@ const TanStackDevtools = ({
60
63
  }
61
64
  return () => devtools.unmount();
62
65
  }, [devtools]);
66
+ const hasPlugins = Object.values(pluginContainers).length > 0 && Object.values(PluginComponents).length > 0;
67
+ const hasTitles = Object.values(titleContainers).length > 0 && Object.values(TitleComponents).length > 0;
63
68
  return /* @__PURE__ */ jsxs(Fragment, { children: [
64
69
  /* @__PURE__ */ jsx("div", { style: { position: "absolute" }, ref: devToolRef }),
65
- pluginContainer && PluginComponent ? createPortal(/* @__PURE__ */ jsx(Fragment, { children: PluginComponent }), pluginContainer) : null,
66
- titleContainer && TitleComponent ? createPortal(/* @__PURE__ */ jsx(Fragment, { children: TitleComponent }), titleContainer) : null
70
+ hasPlugins ? Object.entries(pluginContainers).map(
71
+ ([key, pluginContainer]) => createPortal(/* @__PURE__ */ jsx(Fragment, { children: PluginComponents[key] }), pluginContainer)
72
+ ) : null,
73
+ hasTitles ? Object.entries(titleContainers).map(
74
+ ([key, titleContainer]) => createPortal(/* @__PURE__ */ jsx(Fragment, { children: TitleComponents[key] }), titleContainer)
75
+ ) : null
67
76
  ] });
68
77
  };
69
78
  export {
@@ -1 +1 @@
1
- {"version":3,"file":"devtools.js","sources":["../../src/devtools.tsx"],"sourcesContent":["import React, { useEffect, useRef, useState } from 'react'\nimport {\n PLUGIN_CONTAINER_ID,\n PLUGIN_TITLE_CONTAINER_ID,\n TanStackDevtoolsCore,\n} from '@tanstack/devtools'\nimport { createPortal } from 'react-dom'\nimport type { JSX, ReactElement } from 'react'\nimport type {\n ClientEventBusConfig,\n TanStackDevtoolsConfig,\n TanStackDevtoolsPlugin,\n} from '@tanstack/devtools'\n\ntype PluginRender =\n | JSX.Element\n | ((el: HTMLElement, theme: 'dark' | 'light') => JSX.Element)\n\nexport type TanStackDevtoolsReactPlugin = Omit<\n TanStackDevtoolsPlugin,\n 'render' | 'name'\n> & {\n /**\n * The render function can be a React element or a function that returns a React element.\n * If it's a function, it will be called to render the plugin, otherwise it will be rendered directly.\n *\n * Example:\n * ```jsx\n * {\n * render: () => <CustomPluginComponent />,\n * }\n * ```\n * or\n * ```jsx\n * {\n * render: <CustomPluginComponent />,\n * }\n * ```\n */\n render: PluginRender\n /**\n * Name to be displayed in the devtools UI.\n * If a string, it will be used as the plugin name.\n * If a function, it will be called with the mount element.\n *\n * Example:\n * ```jsx\n * {\n * name: \"Your Plugin\",\n * render: () => <CustomPluginComponent />,\n * }\n * ```\n * or\n * ```jsx\n * {\n * name: <h1>Your Plugin title</h1>,\n * render: () => <CustomPluginComponent />,\n * }\n * ```\n */\n name: string | PluginRender\n}\n\nexport interface TanStackDevtoolsReactInit {\n /**\n * Array of plugins to be used in the devtools.\n * Each plugin should have a `render` function that returns a React element or a function\n *\n * Example:\n * ```jsx\n * <TanStackDevtools\n * plugins={[\n * {\n * id: \"your-plugin-id\",\n * name: \"Your Plugin\",\n * render: <CustomPluginComponent />,\n * }\n * ]}\n * />\n * ```\n */\n plugins?: Array<TanStackDevtoolsReactPlugin>\n /**\n * Configuration for the devtools shell. These configuration options are used to set the\n * initial state of the devtools when it is started for the first time. Afterwards,\n * the settings are persisted in local storage and changed through the settings panel.\n */\n config?: Partial<TanStackDevtoolsConfig>\n /**\n * Configuration for the TanStack Devtools client event bus.\n */\n eventBusConfig?: ClientEventBusConfig\n}\n\nconst convertRender = (\n Component: PluginRender,\n setComponent: React.Dispatch<React.SetStateAction<JSX.Element | null>>,\n e: HTMLElement,\n theme: 'dark' | 'light',\n) => {\n setComponent(\n typeof Component === 'function' ? Component(e, theme) : Component,\n )\n}\n\nexport const TanStackDevtools = ({\n plugins,\n config,\n eventBusConfig,\n}: TanStackDevtoolsReactInit): ReactElement | null => {\n const devToolRef = useRef<HTMLDivElement>(null)\n const [pluginContainer, setPluginContainer] = useState<HTMLElement | null>(\n null,\n )\n const [titleContainer, setTitleContainer] = useState<HTMLElement | null>(null)\n const [PluginComponent, setPluginComponent] = useState<JSX.Element | null>(\n null,\n )\n const [TitleComponent, setTitleComponent] = useState<JSX.Element | null>(null)\n const [devtools] = useState(\n () =>\n new TanStackDevtoolsCore({\n config,\n eventBusConfig,\n plugins: plugins?.map((plugin) => {\n return {\n ...plugin,\n name:\n typeof plugin.name === 'string'\n ? plugin.name\n : // The check above confirms that `plugin.name` is of Render type\n (e, theme) => {\n setTitleContainer(\n e.ownerDocument.getElementById(\n PLUGIN_TITLE_CONTAINER_ID,\n ) || null,\n )\n convertRender(\n plugin.name as PluginRender,\n setTitleComponent,\n e,\n theme,\n )\n },\n render: (e, theme) => {\n setPluginContainer(\n e.ownerDocument.getElementById(PLUGIN_CONTAINER_ID) || null,\n )\n convertRender(plugin.render, setPluginComponent, e, theme)\n },\n }\n }),\n }),\n )\n useEffect(() => {\n if (devToolRef.current) {\n devtools.mount(devToolRef.current)\n }\n\n return () => devtools.unmount()\n }, [devtools])\n\n return (\n <>\n <div style={{ position: 'absolute' }} ref={devToolRef} />\n {pluginContainer && PluginComponent\n ? createPortal(<>{PluginComponent}</>, pluginContainer)\n : null}\n {titleContainer && TitleComponent\n ? createPortal(<>{TitleComponent}</>, titleContainer)\n : null}\n </>\n )\n}\n"],"names":[],"mappings":";;;;AA8FA,MAAM,gBAAgB,CACpB,WACA,cACA,GACA,UACG;AACH;AAAA,IACE,OAAO,cAAc,aAAa,UAAU,GAAG,KAAK,IAAI;AAAA,EAAA;AAE5D;AAEO,MAAM,mBAAmB,CAAC;AAAA,EAC/B;AAAA,EACA;AAAA,EACA;AACF,MAAsD;AACpD,QAAM,aAAa,OAAuB,IAAI;AAC9C,QAAM,CAAC,iBAAiB,kBAAkB,IAAI;AAAA,IAC5C;AAAA,EAAA;AAEF,QAAM,CAAC,gBAAgB,iBAAiB,IAAI,SAA6B,IAAI;AAC7E,QAAM,CAAC,iBAAiB,kBAAkB,IAAI;AAAA,IAC5C;AAAA,EAAA;AAEF,QAAM,CAAC,gBAAgB,iBAAiB,IAAI,SAA6B,IAAI;AAC7E,QAAM,CAAC,QAAQ,IAAI;AAAA,IACjB,MACE,IAAI,qBAAqB;AAAA,MACvB;AAAA,MACA;AAAA,MACA,SAAS,SAAS,IAAI,CAAC,WAAW;AAChC,eAAO;AAAA,UACL,GAAG;AAAA,UACH,MACE,OAAO,OAAO,SAAS,WACnB,OAAO;AAAA;AAAA,YAEP,CAAC,GAAG,UAAU;AACZ;AAAA,gBACE,EAAE,cAAc;AAAA,kBACd;AAAA,gBAAA,KACG;AAAA,cAAA;AAEP;AAAA,gBACE,OAAO;AAAA,gBACP;AAAA,gBACA;AAAA,gBACA;AAAA,cAAA;AAAA,YAEJ;AAAA;AAAA,UACN,QAAQ,CAAC,GAAG,UAAU;AACpB;AAAA,cACE,EAAE,cAAc,eAAe,mBAAmB,KAAK;AAAA,YAAA;AAEzD,0BAAc,OAAO,QAAQ,oBAAoB,GAAG,KAAK;AAAA,UAC3D;AAAA,QAAA;AAAA,MAEJ,CAAC;AAAA,IAAA,CACF;AAAA,EAAA;AAEL,YAAU,MAAM;AACd,QAAI,WAAW,SAAS;AACtB,eAAS,MAAM,WAAW,OAAO;AAAA,IACnC;AAEA,WAAO,MAAM,SAAS,QAAA;AAAA,EACxB,GAAG,CAAC,QAAQ,CAAC;AAEb,SACE,qBAAA,UAAA,EACE,UAAA;AAAA,IAAA,oBAAC,SAAI,OAAO,EAAE,UAAU,WAAA,GAAc,KAAK,YAAY;AAAA,IACtD,mBAAmB,kBAChB,6CAAgB,UAAA,gBAAA,CAAgB,GAAK,eAAe,IACpD;AAAA,IACH,kBAAkB,iBACf,6CAAgB,UAAA,eAAA,CAAe,GAAK,cAAc,IAClD;AAAA,EAAA,GACN;AAEJ;"}
1
+ {"version":3,"file":"devtools.js","sources":["../../src/devtools.tsx"],"sourcesContent":["import React, { useEffect, useRef, useState } from 'react'\nimport { TanStackDevtoolsCore } from '@tanstack/devtools'\nimport { createPortal } from 'react-dom'\nimport type { JSX, ReactElement } from 'react'\nimport type {\n ClientEventBusConfig,\n TanStackDevtoolsConfig,\n TanStackDevtoolsPlugin,\n} from '@tanstack/devtools'\n\ntype PluginRender =\n | JSX.Element\n | ((el: HTMLElement, theme: 'dark' | 'light') => JSX.Element)\n\nexport type TanStackDevtoolsReactPlugin = Omit<\n TanStackDevtoolsPlugin,\n 'render' | 'name'\n> & {\n /**\n * The render function can be a React element or a function that returns a React element.\n * If it's a function, it will be called to render the plugin, otherwise it will be rendered directly.\n *\n * Example:\n * ```jsx\n * {\n * render: () => <CustomPluginComponent />,\n * }\n * ```\n * or\n * ```jsx\n * {\n * render: <CustomPluginComponent />,\n * }\n * ```\n */\n render: PluginRender\n /**\n * Name to be displayed in the devtools UI.\n * If a string, it will be used as the plugin name.\n * If a function, it will be called with the mount element.\n *\n * Example:\n * ```jsx\n * {\n * name: \"Your Plugin\",\n * render: () => <CustomPluginComponent />,\n * }\n * ```\n * or\n * ```jsx\n * {\n * name: <h1>Your Plugin title</h1>,\n * render: () => <CustomPluginComponent />,\n * }\n * ```\n */\n name: string | PluginRender\n}\n\nexport interface TanStackDevtoolsReactInit {\n /**\n * Array of plugins to be used in the devtools.\n * Each plugin should have a `render` function that returns a React element or a function\n *\n * Example:\n * ```jsx\n * <TanStackDevtools\n * plugins={[\n * {\n * id: \"your-plugin-id\",\n * name: \"Your Plugin\",\n * render: <CustomPluginComponent />,\n * }\n * ]}\n * />\n * ```\n */\n plugins?: Array<TanStackDevtoolsReactPlugin>\n /**\n * Configuration for the devtools shell. These configuration options are used to set the\n * initial state of the devtools when it is started for the first time. Afterwards,\n * the settings are persisted in local storage and changed through the settings panel.\n */\n config?: Partial<TanStackDevtoolsConfig>\n /**\n * Configuration for the TanStack Devtools client event bus.\n */\n eventBusConfig?: ClientEventBusConfig\n}\n\nconst convertRender = (\n Component: PluginRender,\n setComponents: React.Dispatch<\n React.SetStateAction<Record<string, JSX.Element>>\n >,\n e: HTMLElement,\n theme: 'dark' | 'light',\n) => {\n const element =\n typeof Component === 'function' ? Component(e, theme) : Component\n\n setComponents((prev) => ({\n ...prev,\n [e.getAttribute('id') as string]: element,\n }))\n}\n\nexport const TanStackDevtools = ({\n plugins,\n config,\n eventBusConfig,\n}: TanStackDevtoolsReactInit): ReactElement | null => {\n const devToolRef = useRef<HTMLDivElement>(null)\n\n const [pluginContainers, setPluginContainers] = useState<\n Record<string, HTMLElement>\n >({})\n const [titleContainers, setTitleContainers] = useState<\n Record<string, HTMLElement>\n >({})\n\n const [PluginComponents, setPluginComponents] = useState<\n Record<string, JSX.Element>\n >({})\n const [TitleComponents, setTitleComponents] = useState<\n Record<string, JSX.Element>\n >({})\n\n const [devtools] = useState(\n () =>\n new TanStackDevtoolsCore({\n config,\n eventBusConfig,\n plugins: plugins?.map((plugin) => {\n return {\n ...plugin,\n name:\n typeof plugin.name === 'string'\n ? plugin.name\n : (e, theme) => {\n const id = e.getAttribute('id')!\n const target = e.ownerDocument.getElementById(id)\n\n if (target) {\n setTitleContainers((prev) => ({\n ...prev,\n [id]: e,\n }))\n }\n\n convertRender(\n plugin.name as PluginRender,\n setTitleComponents,\n e,\n theme,\n )\n },\n render: (e, theme) => {\n const id = e.getAttribute('id')!\n const target = e.ownerDocument.getElementById(id)\n\n if (target) {\n setPluginContainers((prev) => ({\n ...prev,\n [id]: e,\n }))\n }\n\n convertRender(plugin.render, setPluginComponents, e, theme)\n },\n }\n }),\n }),\n )\n\n useEffect(() => {\n if (devToolRef.current) {\n devtools.mount(devToolRef.current)\n }\n\n return () => devtools.unmount()\n }, [devtools])\n\n const hasPlugins =\n Object.values(pluginContainers).length > 0 &&\n Object.values(PluginComponents).length > 0\n const hasTitles =\n Object.values(titleContainers).length > 0 &&\n Object.values(TitleComponents).length > 0\n\n return (\n <>\n <div style={{ position: 'absolute' }} ref={devToolRef} />\n\n {hasPlugins\n ? Object.entries(pluginContainers).map(([key, pluginContainer]) =>\n createPortal(<>{PluginComponents[key]}</>, pluginContainer),\n )\n : null}\n\n {hasTitles\n ? Object.entries(titleContainers).map(([key, titleContainer]) =>\n createPortal(<>{TitleComponents[key]}</>, titleContainer),\n )\n : null}\n </>\n )\n}\n"],"names":[],"mappings":";;;;AA0FA,MAAM,gBAAgB,CACpB,WACA,eAGA,GACA,UACG;AACH,QAAM,UACJ,OAAO,cAAc,aAAa,UAAU,GAAG,KAAK,IAAI;AAE1D,gBAAc,CAAC,UAAU;AAAA,IACvB,GAAG;AAAA,IACH,CAAC,EAAE,aAAa,IAAI,CAAW,GAAG;AAAA,EAAA,EAClC;AACJ;AAEO,MAAM,mBAAmB,CAAC;AAAA,EAC/B;AAAA,EACA;AAAA,EACA;AACF,MAAsD;AACpD,QAAM,aAAa,OAAuB,IAAI;AAE9C,QAAM,CAAC,kBAAkB,mBAAmB,IAAI,SAE9C,CAAA,CAAE;AACJ,QAAM,CAAC,iBAAiB,kBAAkB,IAAI,SAE5C,CAAA,CAAE;AAEJ,QAAM,CAAC,kBAAkB,mBAAmB,IAAI,SAE9C,CAAA,CAAE;AACJ,QAAM,CAAC,iBAAiB,kBAAkB,IAAI,SAE5C,CAAA,CAAE;AAEJ,QAAM,CAAC,QAAQ,IAAI;AAAA,IACjB,MACE,IAAI,qBAAqB;AAAA,MACvB;AAAA,MACA;AAAA,MACA,SAAS,SAAS,IAAI,CAAC,WAAW;AAChC,eAAO;AAAA,UACL,GAAG;AAAA,UACH,MACE,OAAO,OAAO,SAAS,WACnB,OAAO,OACP,CAAC,GAAG,UAAU;AACZ,kBAAM,KAAK,EAAE,aAAa,IAAI;AAC9B,kBAAM,SAAS,EAAE,cAAc,eAAe,EAAE;AAEhD,gBAAI,QAAQ;AACV,iCAAmB,CAAC,UAAU;AAAA,gBAC5B,GAAG;AAAA,gBACH,CAAC,EAAE,GAAG;AAAA,cAAA,EACN;AAAA,YACJ;AAEA;AAAA,cACE,OAAO;AAAA,cACP;AAAA,cACA;AAAA,cACA;AAAA,YAAA;AAAA,UAEJ;AAAA,UACN,QAAQ,CAAC,GAAG,UAAU;AACpB,kBAAM,KAAK,EAAE,aAAa,IAAI;AAC9B,kBAAM,SAAS,EAAE,cAAc,eAAe,EAAE;AAEhD,gBAAI,QAAQ;AACV,kCAAoB,CAAC,UAAU;AAAA,gBAC7B,GAAG;AAAA,gBACH,CAAC,EAAE,GAAG;AAAA,cAAA,EACN;AAAA,YACJ;AAEA,0BAAc,OAAO,QAAQ,qBAAqB,GAAG,KAAK;AAAA,UAC5D;AAAA,QAAA;AAAA,MAEJ,CAAC;AAAA,IAAA,CACF;AAAA,EAAA;AAGL,YAAU,MAAM;AACd,QAAI,WAAW,SAAS;AACtB,eAAS,MAAM,WAAW,OAAO;AAAA,IACnC;AAEA,WAAO,MAAM,SAAS,QAAA;AAAA,EACxB,GAAG,CAAC,QAAQ,CAAC;AAEb,QAAM,aACJ,OAAO,OAAO,gBAAgB,EAAE,SAAS,KACzC,OAAO,OAAO,gBAAgB,EAAE,SAAS;AAC3C,QAAM,YACJ,OAAO,OAAO,eAAe,EAAE,SAAS,KACxC,OAAO,OAAO,eAAe,EAAE,SAAS;AAE1C,SACE,qBAAA,UAAA,EACE,UAAA;AAAA,IAAA,oBAAC,SAAI,OAAO,EAAE,UAAU,WAAA,GAAc,KAAK,YAAY;AAAA,IAEtD,aACG,OAAO,QAAQ,gBAAgB,EAAE;AAAA,MAAI,CAAC,CAAC,KAAK,eAAe,MACzD,aAAa,oBAAA,UAAA,EAAG,UAAA,iBAAiB,GAAG,EAAA,CAAE,GAAK,eAAe;AAAA,IAAA,IAE5D;AAAA,IAEH,YACG,OAAO,QAAQ,eAAe,EAAE;AAAA,MAAI,CAAC,CAAC,KAAK,cAAc,MACvD,aAAa,oBAAA,UAAA,EAAG,UAAA,gBAAgB,GAAG,EAAA,CAAE,GAAK,cAAc;AAAA,IAAA,IAE1D;AAAA,EAAA,GACN;AAEJ;"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tanstack/react-devtools",
3
- "version": "0.6.6",
3
+ "version": "0.6.8",
4
4
  "description": "TanStack Devtools is a set of tools for building advanced devtools for your React application.",
5
5
  "author": "Tanner Linsley",
6
6
  "license": "MIT",
@@ -45,7 +45,7 @@
45
45
  "src"
46
46
  ],
47
47
  "dependencies": {
48
- "@tanstack/devtools": "0.6.10"
48
+ "@tanstack/devtools": "0.6.12"
49
49
  },
50
50
  "devDependencies": {
51
51
  "@eslint-react/eslint-plugin": "^1.48.5",
package/src/devtools.tsx CHANGED
@@ -1,9 +1,5 @@
1
1
  import React, { useEffect, useRef, useState } from 'react'
2
- import {
3
- PLUGIN_CONTAINER_ID,
4
- PLUGIN_TITLE_CONTAINER_ID,
5
- TanStackDevtoolsCore,
6
- } from '@tanstack/devtools'
2
+ import { TanStackDevtoolsCore } from '@tanstack/devtools'
7
3
  import { createPortal } from 'react-dom'
8
4
  import type { JSX, ReactElement } from 'react'
9
5
  import type {
@@ -94,13 +90,19 @@ export interface TanStackDevtoolsReactInit {
94
90
 
95
91
  const convertRender = (
96
92
  Component: PluginRender,
97
- setComponent: React.Dispatch<React.SetStateAction<JSX.Element | null>>,
93
+ setComponents: React.Dispatch<
94
+ React.SetStateAction<Record<string, JSX.Element>>
95
+ >,
98
96
  e: HTMLElement,
99
97
  theme: 'dark' | 'light',
100
98
  ) => {
101
- setComponent(
102
- typeof Component === 'function' ? Component(e, theme) : Component,
103
- )
99
+ const element =
100
+ typeof Component === 'function' ? Component(e, theme) : Component
101
+
102
+ setComponents((prev) => ({
103
+ ...prev,
104
+ [e.getAttribute('id') as string]: element,
105
+ }))
104
106
  }
105
107
 
106
108
  export const TanStackDevtools = ({
@@ -109,14 +111,21 @@ export const TanStackDevtools = ({
109
111
  eventBusConfig,
110
112
  }: TanStackDevtoolsReactInit): ReactElement | null => {
111
113
  const devToolRef = useRef<HTMLDivElement>(null)
112
- const [pluginContainer, setPluginContainer] = useState<HTMLElement | null>(
113
- null,
114
- )
115
- const [titleContainer, setTitleContainer] = useState<HTMLElement | null>(null)
116
- const [PluginComponent, setPluginComponent] = useState<JSX.Element | null>(
117
- null,
118
- )
119
- const [TitleComponent, setTitleComponent] = useState<JSX.Element | null>(null)
114
+
115
+ const [pluginContainers, setPluginContainers] = useState<
116
+ Record<string, HTMLElement>
117
+ >({})
118
+ const [titleContainers, setTitleContainers] = useState<
119
+ Record<string, HTMLElement>
120
+ >({})
121
+
122
+ const [PluginComponents, setPluginComponents] = useState<
123
+ Record<string, JSX.Element>
124
+ >({})
125
+ const [TitleComponents, setTitleComponents] = useState<
126
+ Record<string, JSX.Element>
127
+ >({})
128
+
120
129
  const [devtools] = useState(
121
130
  () =>
122
131
  new TanStackDevtoolsCore({
@@ -128,30 +137,42 @@ export const TanStackDevtools = ({
128
137
  name:
129
138
  typeof plugin.name === 'string'
130
139
  ? plugin.name
131
- : // The check above confirms that `plugin.name` is of Render type
132
- (e, theme) => {
133
- setTitleContainer(
134
- e.ownerDocument.getElementById(
135
- PLUGIN_TITLE_CONTAINER_ID,
136
- ) || null,
137
- )
140
+ : (e, theme) => {
141
+ const id = e.getAttribute('id')!
142
+ const target = e.ownerDocument.getElementById(id)
143
+
144
+ if (target) {
145
+ setTitleContainers((prev) => ({
146
+ ...prev,
147
+ [id]: e,
148
+ }))
149
+ }
150
+
138
151
  convertRender(
139
152
  plugin.name as PluginRender,
140
- setTitleComponent,
153
+ setTitleComponents,
141
154
  e,
142
155
  theme,
143
156
  )
144
157
  },
145
158
  render: (e, theme) => {
146
- setPluginContainer(
147
- e.ownerDocument.getElementById(PLUGIN_CONTAINER_ID) || null,
148
- )
149
- convertRender(plugin.render, setPluginComponent, e, theme)
159
+ const id = e.getAttribute('id')!
160
+ const target = e.ownerDocument.getElementById(id)
161
+
162
+ if (target) {
163
+ setPluginContainers((prev) => ({
164
+ ...prev,
165
+ [id]: e,
166
+ }))
167
+ }
168
+
169
+ convertRender(plugin.render, setPluginComponents, e, theme)
150
170
  },
151
171
  }
152
172
  }),
153
173
  }),
154
174
  )
175
+
155
176
  useEffect(() => {
156
177
  if (devToolRef.current) {
157
178
  devtools.mount(devToolRef.current)
@@ -160,14 +181,27 @@ export const TanStackDevtools = ({
160
181
  return () => devtools.unmount()
161
182
  }, [devtools])
162
183
 
184
+ const hasPlugins =
185
+ Object.values(pluginContainers).length > 0 &&
186
+ Object.values(PluginComponents).length > 0
187
+ const hasTitles =
188
+ Object.values(titleContainers).length > 0 &&
189
+ Object.values(TitleComponents).length > 0
190
+
163
191
  return (
164
192
  <>
165
193
  <div style={{ position: 'absolute' }} ref={devToolRef} />
166
- {pluginContainer && PluginComponent
167
- ? createPortal(<>{PluginComponent}</>, pluginContainer)
194
+
195
+ {hasPlugins
196
+ ? Object.entries(pluginContainers).map(([key, pluginContainer]) =>
197
+ createPortal(<>{PluginComponents[key]}</>, pluginContainer),
198
+ )
168
199
  : null}
169
- {titleContainer && TitleComponent
170
- ? createPortal(<>{TitleComponent}</>, titleContainer)
200
+
201
+ {hasTitles
202
+ ? Object.entries(titleContainers).map(([key, titleContainer]) =>
203
+ createPortal(<>{TitleComponents[key]}</>, titleContainer),
204
+ )
171
205
  : null}
172
206
  </>
173
207
  )