mirror of
https://github.com/reduxjs/redux-devtools.git
synced 2025-07-27 08:30:02 +03:00
feat(rtk-query): complete initial setup of rtk-query
This commit is contained in:
parent
1449d5a9c2
commit
e9f397bd1e
|
@ -38,6 +38,8 @@
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@redux-devtools/chart-monitor": "^1.9.0",
|
"@redux-devtools/chart-monitor": "^1.9.0",
|
||||||
|
"@reduxjs/toolkit": "^1.6.0",
|
||||||
|
"@redux-devtools/rtk-query-inspector-monitor": "^1.0.0",
|
||||||
"@redux-devtools/core": "^3.9.0",
|
"@redux-devtools/core": "^3.9.0",
|
||||||
"@redux-devtools/inspector-monitor": "^1.0.0",
|
"@redux-devtools/inspector-monitor": "^1.0.0",
|
||||||
"@redux-devtools/inspector-monitor-test-tab": "^0.7.2",
|
"@redux-devtools/inspector-monitor-test-tab": "^0.7.2",
|
||||||
|
|
|
@ -0,0 +1,25 @@
|
||||||
|
import React, { Component } from 'react';
|
||||||
|
import { connect, ResolveThunks } from 'react-redux';
|
||||||
|
import RtkQueryInspectorMonitor from '@redux-devtools/rtk-query-inspector-monitor';
|
||||||
|
import { selectMonitorWithState } from '../../actions';
|
||||||
|
|
||||||
|
type DispatchProps = ResolveThunks<typeof actionCreators>;
|
||||||
|
type Props = DispatchProps;
|
||||||
|
|
||||||
|
class RtkQueryInspectorMonitorWrapper extends Component<Props> {
|
||||||
|
static update = RtkQueryInspectorMonitor.update;
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||||
|
// @ts-ignore
|
||||||
|
<RtkQueryInspectorMonitor defaultIsVisible invertTheme {...this.props} />
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const actionCreators = {
|
||||||
|
selectMonitorWithState: selectMonitorWithState,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default connect(null, actionCreators)(RtkQueryInspectorMonitorWrapper);
|
|
@ -1,5 +1,6 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import LogMonitor from '@redux-devtools/log-monitor';
|
import LogMonitor from '@redux-devtools/log-monitor';
|
||||||
|
import RtkQueryInspectorMonitorWrapper from '../containers/monitors/RtkQueryMonitorWrapper';
|
||||||
import ChartMonitorWrapper from '../containers/monitors/ChartMonitorWrapper';
|
import ChartMonitorWrapper from '../containers/monitors/ChartMonitorWrapper';
|
||||||
import InspectorWrapper from '../containers/monitors/InspectorWrapper';
|
import InspectorWrapper from '../containers/monitors/InspectorWrapper';
|
||||||
|
|
||||||
|
@ -7,6 +8,7 @@ export const monitors = [
|
||||||
{ value: 'InspectorMonitor', name: 'Inspector' },
|
{ value: 'InspectorMonitor', name: 'Inspector' },
|
||||||
{ value: 'LogMonitor', name: 'Log monitor' },
|
{ value: 'LogMonitor', name: 'Log monitor' },
|
||||||
{ value: 'ChartMonitor', name: 'Chart' },
|
{ value: 'ChartMonitor', name: 'Chart' },
|
||||||
|
{ value: 'RtkQueryMonitor', name: 'RTK Query' },
|
||||||
];
|
];
|
||||||
|
|
||||||
export default function getMonitor({ monitor }: { monitor: string }) {
|
export default function getMonitor({ monitor }: { monitor: string }) {
|
||||||
|
@ -17,6 +19,8 @@ export default function getMonitor({ monitor }: { monitor: string }) {
|
||||||
);
|
);
|
||||||
case 'ChartMonitor':
|
case 'ChartMonitor':
|
||||||
return <ChartMonitorWrapper />;
|
return <ChartMonitorWrapper />;
|
||||||
|
case 'RtkQueryMonitor':
|
||||||
|
return <RtkQueryInspectorMonitorWrapper />;
|
||||||
default:
|
default:
|
||||||
return <InspectorWrapper />;
|
return <InspectorWrapper />;
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
{
|
||||||
|
"presets": [
|
||||||
|
"@babel/preset-env",
|
||||||
|
"@babel/preset-react",
|
||||||
|
"@babel/preset-typescript"
|
||||||
|
],
|
||||||
|
"plugins": ["@babel/plugin-proposal-class-properties"]
|
||||||
|
}
|
|
@ -0,0 +1,13 @@
|
||||||
|
module.exports = {
|
||||||
|
extends: '../../.eslintrc',
|
||||||
|
overrides: [
|
||||||
|
{
|
||||||
|
files: ['*.ts', '*.tsx'],
|
||||||
|
extends: '../../eslintrc.ts.react.base.json',
|
||||||
|
parserOptions: {
|
||||||
|
tsconfigRootDir: __dirname,
|
||||||
|
project: ['./tsconfig.json'],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
1
packages/redux-devtools-rtk-query-inspector-monitor/.gitignore
vendored
Normal file
1
packages/redux-devtools-rtk-query-inspector-monitor/.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
demo/src/generated-module/
|
|
@ -0,0 +1,21 @@
|
||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2021 Fabrizio Vitale
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
|
@ -0,0 +1,71 @@
|
||||||
|
# Redux DevTools Chart Monitor
|
||||||
|
|
||||||
|
A chart monitor for [Redux DevTools](https://github.com/gaearon/redux-devtools).
|
||||||
|
|
||||||
|
Created by [@romseguy](https://github.com/romseguy) and merged from [`reduxjs/redux-devtools-chart-monitor`](https://github.com/reduxjs/redux-devtools-chart-monitor).
|
||||||
|
|
||||||
|
It shows a real-time view of the store aka the current state of the app.
|
||||||
|
|
||||||
|
:rocket: Now with immutable-js support.
|
||||||
|
|
||||||
|
[Demo](http://romseguy.github.io/redux-store-visualizer/) [(Source)](https://github.com/romseguy/redux-store-visualizer)
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
### Installation
|
||||||
|
|
||||||
|
```
|
||||||
|
yarn add @redux-devtools/chart-monitor
|
||||||
|
```
|
||||||
|
|
||||||
|
### Usage
|
||||||
|
|
||||||
|
You can use `ChartMonitor` as the only monitor in your app:
|
||||||
|
|
||||||
|
##### `containers/DevTools.js`
|
||||||
|
|
||||||
|
```js
|
||||||
|
import React from 'react';
|
||||||
|
import { createDevTools } from '@redux-devtools/core';
|
||||||
|
import ChartMonitor from '@redux-devtools/chart-monitor';
|
||||||
|
|
||||||
|
export default createDevTools(<ChartMonitor />);
|
||||||
|
```
|
||||||
|
|
||||||
|
Then you can render `<DevTools>` to any place inside app or even into a separate popup window.
|
||||||
|
|
||||||
|
Alternatively, you can use it together with [`DockMonitor`](https://github.com/reduxjs/redux-devtools/tree/master/packages/redux-devtools-dock-monitor) to make it dockable.
|
||||||
|
Consult the [`DockMonitor` README](https://github.com/reduxjs/redux-devtools/tree/master/packages/redux-devtools-dock-monitor) for details of this approach.
|
||||||
|
|
||||||
|
[Read how to start using Redux DevTools.](https://github.com/reduxjs/redux-devtools)
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
### Props
|
||||||
|
|
||||||
|
#### ChartMonitor props
|
||||||
|
|
||||||
|
You can read the React component [propTypes](https://github.com/reduxjs/redux-devtools/blob/master/packages/redux-devtools-chart-monitor/src/Chart.js#L11) in addition to the details below:
|
||||||
|
|
||||||
|
| Name | Description |
|
||||||
|
| ------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||||
|
| `defaultIsVisible` | By default, set to `true`. |
|
||||||
|
| `transitionDuration` | By default, set to `750`, in milliseconds. |
|
||||||
|
| `heightBetweenNodesCoeff` | By default, set to `1`. |
|
||||||
|
| `widthBetweenNodesCoeff` | By default, set to `1.3`. |
|
||||||
|
| `isSorted` | By default, set to `false`. |
|
||||||
|
| `style` | {<br> width: '100%', height: '100%', // i.e fullscreen for [`DockMonitor`](https://github.com/reduxjs/redux-devtools/tree/master/packages/redux-devtools-dock-monitor)<br> text: {<br> colors: {<br> 'default': `theme.base0D`,<br> hover: `theme.base06`<br> }<br> },<br> node: {<br> colors: {<br> 'default': `theme.base0B`,<br> collapsed: `theme.base0B`,<br> parent: `theme.base0E`<br> },<br> radius: 7<br> }<br>} |
|
||||||
|
| `onClickText` | Function called with a reference to the clicked node as first argument when clicking on the text next to a node. |
|
||||||
|
| `tooltipOptions` | {<br> disabled: false,<br> indentationSize: 2,<br> style: {<br> 'background-color': `theme.base06`,<br> 'opacity': '0.7',<br> 'border-radius': '5px',<br> 'padding': '5px'<br> }<br>}<br>[More info](https://github.com/reduxjs/redux-devtools/tree/master/packages/d3tooltip#api). |
|
||||||
|
|
||||||
|
#### Redux DevTools props
|
||||||
|
|
||||||
|
| Name | Description |
|
||||||
|
| ------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||||
|
| `theme` | Either a string referring to one of the themes provided by [redux-devtools-themes](https://github.com/gaearon/redux-devtools-themes) (feel free to contribute!) or a custom object of the same format. Optional. By default, set to [`'nicinabox'`](https://github.com/gaearon/redux-devtools-themes/blob/master/src/nicinabox.js). |
|
||||||
|
| `invertTheme` | Boolean value that will invert the colors of the selected theme. Optional. By default, set to `false` |
|
||||||
|
| `select` | A function that selects the slice of the state for DevTools to show. For example, `state => state.thePart.iCare.about`. Optional. By default, set to `state => state`. |
|
||||||
|
|
||||||
|
### License
|
||||||
|
|
||||||
|
MIT
|
3
packages/redux-devtools-rtk-query-inspector-monitor/demo/.gitignore
vendored
Normal file
3
packages/redux-devtools-rtk-query-inspector-monitor/demo/.gitignore
vendored
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
.snowpack
|
||||||
|
build
|
||||||
|
node_modules
|
|
@ -0,0 +1,25 @@
|
||||||
|
# New Project
|
||||||
|
|
||||||
|
> ✨ Bootstrapped with Create Snowpack App (CSA).
|
||||||
|
|
||||||
|
## Available Scripts
|
||||||
|
|
||||||
|
### npm start
|
||||||
|
|
||||||
|
Runs the app in the development mode.
|
||||||
|
Open http://localhost:8080 to view it in the browser.
|
||||||
|
|
||||||
|
The page will reload if you make edits.
|
||||||
|
You will also see any lint errors in the console.
|
||||||
|
|
||||||
|
### npm run build
|
||||||
|
|
||||||
|
Builds a static copy of your site to the `build/` folder.
|
||||||
|
Your app is ready to be deployed!
|
||||||
|
|
||||||
|
**For the best production performance:** Add a build bundler plugin like "@snowpack/plugin-webpack" to your `snowpack.config.mjs` config file.
|
||||||
|
|
||||||
|
### npm test
|
||||||
|
|
||||||
|
Launches the application test runner.
|
||||||
|
Run with the `--watch` flag (`npm test -- --watch`) to run in interactive watch mode.
|
|
@ -0,0 +1,39 @@
|
||||||
|
{
|
||||||
|
"name": "rtk-query-imspector-monitor-demo",
|
||||||
|
"private": true,
|
||||||
|
"scripts": {
|
||||||
|
"start": "cross-env SKIP_PREFLIGHT_CHECK=true react-scripts start",
|
||||||
|
"build": "react-scripts build"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@redux-devtools/core": "^3.9.0",
|
||||||
|
"@redux-devtools/dock-monitor": "^1.4.0",
|
||||||
|
"@reduxjs/toolkit": "^1.6.0",
|
||||||
|
"cross-env": "^7.0.3",
|
||||||
|
"devui": "^1.0.0-8",
|
||||||
|
"react": "^17.0.2",
|
||||||
|
"react-base16-styling": "^0.8.0",
|
||||||
|
"react-dom": "^17.0.2",
|
||||||
|
"react-redux": "^7.2.1",
|
||||||
|
"react-scripts": "4.0.2",
|
||||||
|
"redux": "^4.0.5",
|
||||||
|
"redux-devtools-themes": "^1.0.0"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/react": "17.0.0",
|
||||||
|
"@types/react-dom": "17.0.0",
|
||||||
|
"@types/react-redux": "7.1.9",
|
||||||
|
"typescript": "4.1.3"
|
||||||
|
},
|
||||||
|
"eslintConfig": {
|
||||||
|
"extends": [
|
||||||
|
"react-app"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"browserslist": [
|
||||||
|
">0.2%",
|
||||||
|
"not dead",
|
||||||
|
"not ie <= 11",
|
||||||
|
"not op_mini all"
|
||||||
|
]
|
||||||
|
}
|
Binary file not shown.
After Width: | Height: | Size: 3.1 KiB |
|
@ -0,0 +1,25 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<link rel="icon" href="/favicon.ico" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
|
<meta name="description" content="Web site created using create-snowpack-app" />
|
||||||
|
<title>Snowpack App</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="root"></div>
|
||||||
|
<noscript>You need to enable JavaScript to run this app.</noscript>
|
||||||
|
<script type="module" src="/dist/index.js"></script>
|
||||||
|
<!--
|
||||||
|
This HTML file is a template.
|
||||||
|
If you open it directly in the browser, you will see an empty page.
|
||||||
|
|
||||||
|
You can add webfonts, meta tags, or analytics to this file.
|
||||||
|
The build step will place the bundled scripts into the <body> tag.
|
||||||
|
|
||||||
|
To begin the development, run `npm start` or `yarn start`.
|
||||||
|
To create a production bundle, use `npm run build` or `yarn build`.
|
||||||
|
-->
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -0,0 +1,46 @@
|
||||||
|
.App {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
.App code {
|
||||||
|
background: #fff3;
|
||||||
|
padding: 4px 8px;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
.App p {
|
||||||
|
margin: 0.4rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.App-logo {
|
||||||
|
height: 40vmin;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (prefers-reduced-motion: no-preference) {
|
||||||
|
.App-logo {
|
||||||
|
animation: App-logo-spin infinite 20s linear;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.App-header {
|
||||||
|
background-color: #282c34;
|
||||||
|
min-height: 100vh;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
font-size: calc(10px + 2vmin);
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.App-link {
|
||||||
|
color: #61dafb;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes App-logo-spin {
|
||||||
|
from {
|
||||||
|
transform: rotate(0deg);
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
transform: rotate(360deg);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,32 @@
|
||||||
|
import * as React from 'react';
|
||||||
|
import { Pokemon } from './Pokemon';
|
||||||
|
import { PokemonName, POKEMON_NAMES } from './pokemon.data';
|
||||||
|
import './styles.css';
|
||||||
|
|
||||||
|
const getRandomPokemonName = () =>
|
||||||
|
POKEMON_NAMES[Math.floor(Math.random() * POKEMON_NAMES.length)];
|
||||||
|
|
||||||
|
export default function App() {
|
||||||
|
const [pokemon, setPokemon] = React.useState<PokemonName[]>(['bulbasaur']);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="App">
|
||||||
|
<div>
|
||||||
|
<button
|
||||||
|
onClick={() =>
|
||||||
|
setPokemon((prev) => [...prev, getRandomPokemonName()])
|
||||||
|
}
|
||||||
|
>
|
||||||
|
Add random pokemon
|
||||||
|
</button>
|
||||||
|
<button onClick={() => setPokemon((prev) => [...prev, 'bulbasaur'])}>
|
||||||
|
Add bulbasaur
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{pokemon.map((name, index) => (
|
||||||
|
<Pokemon key={index} name={name} />
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
|
@ -0,0 +1,14 @@
|
||||||
|
import * as React from 'react';
|
||||||
|
import { createDevTools } from '@redux-devtools/core';
|
||||||
|
import DockMonitor from '@redux-devtools/dock-monitor';
|
||||||
|
import RtkQueryInspectorMonitor from './generated-module/RtkQueryInspectorMonitor.js';
|
||||||
|
|
||||||
|
export default createDevTools(
|
||||||
|
<DockMonitor
|
||||||
|
toggleVisibilityKey="ctrl-h"
|
||||||
|
changePositionKey="ctrl-q"
|
||||||
|
changeMonitorKey="ctrl-m"
|
||||||
|
>
|
||||||
|
<RtkQueryInspectorMonitor />
|
||||||
|
</DockMonitor>,
|
||||||
|
);
|
|
@ -0,0 +1,77 @@
|
||||||
|
import React, { useState } from 'react';
|
||||||
|
import { useGetPokemonByNameQuery } from './services/pokemon';
|
||||||
|
import type { PokemonName } from './pokemon.data';
|
||||||
|
|
||||||
|
const intervalOptions = [
|
||||||
|
{ label: 'Off', value: 0 },
|
||||||
|
{ label: '3s', value: 3000 },
|
||||||
|
{ label: '5s', value: 5000 },
|
||||||
|
{ label: '10s', value: 10000 },
|
||||||
|
{ label: '1m', value: 60000 },
|
||||||
|
];
|
||||||
|
|
||||||
|
const getRandomIntervalValue = () =>
|
||||||
|
intervalOptions[Math.floor(Math.random() * intervalOptions.length)].value;
|
||||||
|
|
||||||
|
export function Pokemon({ name }: { name: PokemonName }) {
|
||||||
|
const [pollingInterval, setPollingInterval] = useState(60000);
|
||||||
|
|
||||||
|
const {
|
||||||
|
data,
|
||||||
|
error,
|
||||||
|
isLoading,
|
||||||
|
isFetching,
|
||||||
|
refetch,
|
||||||
|
} = useGetPokemonByNameQuery(name, {
|
||||||
|
pollingInterval,
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
float: 'left',
|
||||||
|
textAlign: 'center',
|
||||||
|
...(isFetching ? { background: '#e6ffe8' } : {}),
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{error ? (
|
||||||
|
<>Oh no, there was an error loading {name}</>
|
||||||
|
) : isLoading ? (
|
||||||
|
<>Loading...</>
|
||||||
|
) : data ? (
|
||||||
|
<>
|
||||||
|
<h3>{data.species.name}</h3>
|
||||||
|
<div style={{ minWidth: 96, minHeight: 96 }}>
|
||||||
|
<img
|
||||||
|
src={data.sprites.front_shiny}
|
||||||
|
alt={data.species.name}
|
||||||
|
style={{ ...(isFetching ? { opacity: 0.3 } : {}) }}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label style={{ display: 'block' }}>Polling interval</label>
|
||||||
|
<select
|
||||||
|
value={pollingInterval}
|
||||||
|
onChange={({ target: { value } }) =>
|
||||||
|
setPollingInterval(Number(value))
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{intervalOptions.map(({ label, value }) => (
|
||||||
|
<option key={value} value={value}>
|
||||||
|
{label}
|
||||||
|
</option>
|
||||||
|
))}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<button onClick={refetch} disabled={isFetching}>
|
||||||
|
{isFetching ? 'Loading' : 'Manually refetch'}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
'No Data'
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
|
@ -0,0 +1,13 @@
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
|
||||||
|
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
|
||||||
|
sans-serif;
|
||||||
|
-webkit-font-smoothing: antialiased;
|
||||||
|
-moz-osx-font-smoothing: grayscale;
|
||||||
|
}
|
||||||
|
|
||||||
|
code {
|
||||||
|
font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
|
||||||
|
monospace;
|
||||||
|
}
|
|
@ -0,0 +1,16 @@
|
||||||
|
import React from 'react';
|
||||||
|
import ReactDOM from 'react-dom';
|
||||||
|
import { Provider } from 'react-redux';
|
||||||
|
import App from './App';
|
||||||
|
import { store } from './store';
|
||||||
|
import DevTools from './DevTools';
|
||||||
|
|
||||||
|
const rootElement = document.getElementById('root');
|
||||||
|
|
||||||
|
ReactDOM.render(
|
||||||
|
<Provider store={store}>
|
||||||
|
<App />
|
||||||
|
<DevTools />
|
||||||
|
</Provider>,
|
||||||
|
rootElement,
|
||||||
|
);
|
|
@ -0,0 +1,6 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 841.9 595.3">
|
||||||
|
<g fill="#61DAFB">
|
||||||
|
<path d="M666.3 296.5c0-32.5-40.7-63.3-103.1-82.4 14.4-63.6 8-114.2-20.2-130.4a43.8 43.8 0 00-22.4-5.6v22.3c4.6 0 8.3.9 11.4 2.6 13.6 7.8 19.5 37.5 14.9 75.7-1.1 9.4-2.9 19.3-5.1 29.4-19.6-4.8-41-8.5-63.5-10.9a487.8 487.8 0 00-41.6-50c32.6-30.3 63.2-46.9 84-46.9V78c-27.5 0-63.5 19.6-99.9 53.6-36.4-33.8-72.4-53.2-99.9-53.2v22.3c20.7 0 51.4 16.5 84 46.6-14 14.7-28 31.4-41.3 49.9a467 467 0 00-63.6 11c-2.3-10-4-19.7-5.2-29-4.7-38.2 1.1-67.9 14.6-75.8 3-1.8 6.9-2.6 11.5-2.6V78.5c-8.4 0-16 1.8-22.6 5.6-28.1 16.2-34.4 66.7-19.9 130.1-62.2 19.2-102.7 49.9-102.7 82.3 0 32.5 40.7 63.3 103.1 82.4-14.4 63.6-8 114.2 20.2 130.4a44 44 0 0022.5 5.6c27.5 0 63.5-19.6 99.9-53.6 36.4 33.8 72.4 53.2 99.9 53.2 8.4 0 16-1.8 22.6-5.6 28.1-16.2 34.4-66.7 19.9-130.1 62-19.1 102.5-49.9 102.5-82.3zm-130.2-66.7a450.4 450.4 0 01-13.5 39.5 473.3 473.3 0 00-27.5-47.4c14.2 2.1 27.9 4.7 41 7.9zm-45.8 106.5a532.7 532.7 0 01-24.1 38.2 520.3 520.3 0 01-90.2.1 551.2 551.2 0 01-45-77.8 521.5 521.5 0 0144.8-78.1 520.3 520.3 0 0190.2-.1 551.2 551.2 0 0145 77.8 560 560 0 01-20.7 39.9zm32.3-13c5.4 13.4 10 26.8 13.8 39.8a448.8 448.8 0 01-41.2 8 552.4 552.4 0 0027.4-47.8zM421.2 430a412.3 412.3 0 01-27.8-32 619 619 0 0055.3 0c-9 11.7-18.3 22.4-27.5 32zm-74.4-58.9a451.2 451.2 0 01-41-7.9c3.7-12.9 8.3-26.2 13.5-39.5a473.3 473.3 0 0027.5 47.4zM420.7 163c9.3 9.6 18.6 20.3 27.8 32a619 619 0 00-55.3 0c9-11.7 18.3-22.4 27.5-32zm-74 58.9a552.4 552.4 0 00-27.4 47.7c-5.4-13.4-10-26.8-13.8-39.8 13.1-3.1 26.9-5.8 41.2-7.9zm-90.5 125.2c-35.4-15.1-58.3-34.9-58.3-50.6 0-15.7 22.9-35.6 58.3-50.6 8.6-3.7 18-7 27.7-10.1 5.7 19.6 13.2 40 22.5 60.9a473.5 473.5 0 00-22.2 60.6c-9.9-3.1-19.3-6.5-28-10.2zM310 490c-13.6-7.8-19.5-37.5-14.9-75.7 1.1-9.4 2.9-19.3 5.1-29.4 19.6 4.8 41 8.5 63.5 10.9a487.8 487.8 0 0041.6 50c-32.6 30.3-63.2 46.9-84 46.9-4.5-.1-8.3-1-11.3-2.7zm237.2-76.2c4.7 38.2-1.1 67.9-14.6 75.8-3 1.8-6.9 2.6-11.5 2.6-20.7 0-51.4-16.5-84-46.6 14-14.7 28-31.4 41.3-49.9a467 467 0 0063.6-11 280 280 0 015.2 29.1zm38.5-66.7c-8.6 3.7-18 7-27.7 10.1-5.7-19.6-13.2-40-22.5-60.9a473.5 473.5 0 0022.2-60.6c9.9 3.1 19.3 6.5 28.1 10.2 35.4 15.1 58.3 34.9 58.3 50.6-.1 15.7-23 35.6-58.4 50.6zM320.8 78.4z"/>
|
||||||
|
<circle cx="420.9" cy="296.5" r="45.7"/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 2.3 KiB |
|
@ -0,0 +1,155 @@
|
||||||
|
export const POKEMON_NAMES = [
|
||||||
|
'bulbasaur',
|
||||||
|
'ivysaur',
|
||||||
|
'venusaur',
|
||||||
|
'charmander',
|
||||||
|
'charmeleon',
|
||||||
|
'charizard',
|
||||||
|
'squirtle',
|
||||||
|
'wartortle',
|
||||||
|
'blastoise',
|
||||||
|
'caterpie',
|
||||||
|
'metapod',
|
||||||
|
'butterfree',
|
||||||
|
'weedle',
|
||||||
|
'kakuna',
|
||||||
|
'beedrill',
|
||||||
|
'pidgey',
|
||||||
|
'pidgeotto',
|
||||||
|
'pidgeot',
|
||||||
|
'rattata',
|
||||||
|
'raticate',
|
||||||
|
'spearow',
|
||||||
|
'fearow',
|
||||||
|
'ekans',
|
||||||
|
'arbok',
|
||||||
|
'pikachu',
|
||||||
|
'raichu',
|
||||||
|
'sandshrew',
|
||||||
|
'sandslash',
|
||||||
|
'nidoran',
|
||||||
|
'nidorina',
|
||||||
|
'nidoqueen',
|
||||||
|
'nidoran',
|
||||||
|
'nidorino',
|
||||||
|
'nidoking',
|
||||||
|
'clefairy',
|
||||||
|
'clefable',
|
||||||
|
'vulpix',
|
||||||
|
'ninetales',
|
||||||
|
'jigglypuff',
|
||||||
|
'wigglytuff',
|
||||||
|
'zubat',
|
||||||
|
'golbat',
|
||||||
|
'oddish',
|
||||||
|
'gloom',
|
||||||
|
'vileplume',
|
||||||
|
'paras',
|
||||||
|
'parasect',
|
||||||
|
'venonat',
|
||||||
|
'venomoth',
|
||||||
|
'diglett',
|
||||||
|
'dugtrio',
|
||||||
|
'meowth',
|
||||||
|
'persian',
|
||||||
|
'psyduck',
|
||||||
|
'golduck',
|
||||||
|
'mankey',
|
||||||
|
'primeape',
|
||||||
|
'growlithe',
|
||||||
|
'arcanine',
|
||||||
|
'poliwag',
|
||||||
|
'poliwhirl',
|
||||||
|
'poliwrath',
|
||||||
|
'abra',
|
||||||
|
'kadabra',
|
||||||
|
'alakazam',
|
||||||
|
'machop',
|
||||||
|
'machoke',
|
||||||
|
'machamp',
|
||||||
|
'bellsprout',
|
||||||
|
'weepinbell',
|
||||||
|
'victreebel',
|
||||||
|
'tentacool',
|
||||||
|
'tentacruel',
|
||||||
|
'geodude',
|
||||||
|
'graveler',
|
||||||
|
'golem',
|
||||||
|
'ponyta',
|
||||||
|
'rapidash',
|
||||||
|
'slowpoke',
|
||||||
|
'slowbro',
|
||||||
|
'magnemite',
|
||||||
|
'magneton',
|
||||||
|
"farfetch'd",
|
||||||
|
'doduo',
|
||||||
|
'dodrio',
|
||||||
|
'seel',
|
||||||
|
'dewgong',
|
||||||
|
'grimer',
|
||||||
|
'muk',
|
||||||
|
'shellder',
|
||||||
|
'cloyster',
|
||||||
|
'gastly',
|
||||||
|
'haunter',
|
||||||
|
'gengar',
|
||||||
|
'onix',
|
||||||
|
'drowzee',
|
||||||
|
'hypno',
|
||||||
|
'krabby',
|
||||||
|
'kingler',
|
||||||
|
'voltorb',
|
||||||
|
'electrode',
|
||||||
|
'exeggcute',
|
||||||
|
'exeggutor',
|
||||||
|
'cubone',
|
||||||
|
'marowak',
|
||||||
|
'hitmonlee',
|
||||||
|
'hitmonchan',
|
||||||
|
'lickitung',
|
||||||
|
'koffing',
|
||||||
|
'weezing',
|
||||||
|
'rhyhorn',
|
||||||
|
'rhydon',
|
||||||
|
'chansey',
|
||||||
|
'tangela',
|
||||||
|
'kangaskhan',
|
||||||
|
'horsea',
|
||||||
|
'seadra',
|
||||||
|
'goldeen',
|
||||||
|
'seaking',
|
||||||
|
'staryu',
|
||||||
|
'starmie',
|
||||||
|
'mr. mime',
|
||||||
|
'scyther',
|
||||||
|
'jynx',
|
||||||
|
'electabuzz',
|
||||||
|
'magmar',
|
||||||
|
'pinsir',
|
||||||
|
'tauros',
|
||||||
|
'magikarp',
|
||||||
|
'gyarados',
|
||||||
|
'lapras',
|
||||||
|
'ditto',
|
||||||
|
'eevee',
|
||||||
|
'vaporeon',
|
||||||
|
'jolteon',
|
||||||
|
'flareon',
|
||||||
|
'porygon',
|
||||||
|
'omanyte',
|
||||||
|
'omastar',
|
||||||
|
'kabuto',
|
||||||
|
'kabutops',
|
||||||
|
'aerodactyl',
|
||||||
|
'snorlax',
|
||||||
|
'articuno',
|
||||||
|
'zapdos',
|
||||||
|
'moltres',
|
||||||
|
'dratini',
|
||||||
|
'dragonair',
|
||||||
|
'dragonite',
|
||||||
|
'mewtwo',
|
||||||
|
'mew',
|
||||||
|
] as const;
|
||||||
|
|
||||||
|
export type PokemonName = typeof POKEMON_NAMES[number];
|
5
packages/redux-devtools-rtk-query-inspector-monitor/demo/src/react-app-env.d.ts
vendored
Normal file
5
packages/redux-devtools-rtk-query-inspector-monitor/demo/src/react-app-env.d.ts
vendored
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
/// <reference types="react-scripts" />
|
||||||
|
|
||||||
|
declare module '@redux-devtools/app';
|
||||||
|
|
||||||
|
declare module 'remote-redux-devtools';
|
|
@ -0,0 +1,15 @@
|
||||||
|
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react';
|
||||||
|
import type { PokemonName } from '../pokemon.data';
|
||||||
|
|
||||||
|
export const pokemonApi = createApi({
|
||||||
|
reducerPath: 'pokemonApi',
|
||||||
|
baseQuery: fetchBaseQuery({ baseUrl: 'https://pokeapi.co/api/v2/' }),
|
||||||
|
endpoints: (builder) => ({
|
||||||
|
getPokemonByName: builder.query({
|
||||||
|
query: (name: PokemonName) => `pokemon/${name}`,
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
// Export hooks for usage in functional components
|
||||||
|
export const { useGetPokemonByNameQuery } = pokemonApi;
|
|
@ -0,0 +1,13 @@
|
||||||
|
import { configureStore } from '@reduxjs/toolkit';
|
||||||
|
import { pokemonApi } from './services/pokemon';
|
||||||
|
import DevTools from './DevTools';
|
||||||
|
export const store = configureStore({
|
||||||
|
reducer: {
|
||||||
|
[pokemonApi.reducerPath]: pokemonApi.reducer,
|
||||||
|
},
|
||||||
|
devTools: false,
|
||||||
|
// adding the api middleware enables caching, invalidation, polling and other features of `rtk-query`
|
||||||
|
middleware: (getDefaultMiddleware) =>
|
||||||
|
getDefaultMiddleware().concat(pokemonApi.middleware),
|
||||||
|
enhancers: [DevTools.instrument()]
|
||||||
|
});
|
|
@ -0,0 +1,4 @@
|
||||||
|
.App {
|
||||||
|
font-family: sans-serif;
|
||||||
|
text-align: center;
|
||||||
|
}
|
|
@ -0,0 +1,25 @@
|
||||||
|
{
|
||||||
|
"include": [
|
||||||
|
"./src/**/*"
|
||||||
|
],
|
||||||
|
"compilerOptions": {
|
||||||
|
"strict": true,
|
||||||
|
"esModuleInterop": true,
|
||||||
|
"lib": [
|
||||||
|
"dom",
|
||||||
|
"es2015"
|
||||||
|
],
|
||||||
|
"jsx": "react-jsx",
|
||||||
|
"target": "es5",
|
||||||
|
"allowJs": true,
|
||||||
|
"skipLibCheck": true,
|
||||||
|
"allowSyntheticDefaultImports": true,
|
||||||
|
"forceConsistentCasingInFileNames": true,
|
||||||
|
"noFallthroughCasesInSwitch": true,
|
||||||
|
"module": "esnext",
|
||||||
|
"moduleResolution": "node",
|
||||||
|
"resolveJsonModule": true,
|
||||||
|
"isolatedModules": true,
|
||||||
|
"noEmit": true
|
||||||
|
}
|
||||||
|
}
|
59
packages/redux-devtools-rtk-query-inspector-monitor/demo/types/static.d.ts
vendored
Normal file
59
packages/redux-devtools-rtk-query-inspector-monitor/demo/types/static.d.ts
vendored
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
/* Use this file to declare any custom file extensions for importing */
|
||||||
|
/* Use this folder to also add/extend a package d.ts file, if needed. */
|
||||||
|
|
||||||
|
/* CSS MODULES */
|
||||||
|
declare module '*.module.css' {
|
||||||
|
const classes: { [key: string]: string };
|
||||||
|
export default classes;
|
||||||
|
}
|
||||||
|
declare module '*.module.scss' {
|
||||||
|
const classes: { [key: string]: string };
|
||||||
|
export default classes;
|
||||||
|
}
|
||||||
|
declare module '*.module.sass' {
|
||||||
|
const classes: { [key: string]: string };
|
||||||
|
export default classes;
|
||||||
|
}
|
||||||
|
declare module '*.module.less' {
|
||||||
|
const classes: { [key: string]: string };
|
||||||
|
export default classes;
|
||||||
|
}
|
||||||
|
declare module '*.module.styl' {
|
||||||
|
const classes: { [key: string]: string };
|
||||||
|
export default classes;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* CSS */
|
||||||
|
declare module '*.css';
|
||||||
|
declare module '*.scss';
|
||||||
|
declare module '*.sass';
|
||||||
|
declare module '*.less';
|
||||||
|
declare module '*.styl';
|
||||||
|
|
||||||
|
/* IMAGES */
|
||||||
|
declare module '*.svg' {
|
||||||
|
const ref: string;
|
||||||
|
export default ref;
|
||||||
|
}
|
||||||
|
declare module '*.bmp' {
|
||||||
|
const ref: string;
|
||||||
|
export default ref;
|
||||||
|
}
|
||||||
|
declare module '*.gif' {
|
||||||
|
const ref: string;
|
||||||
|
export default ref;
|
||||||
|
}
|
||||||
|
declare module '*.jpg' {
|
||||||
|
const ref: string;
|
||||||
|
export default ref;
|
||||||
|
}
|
||||||
|
declare module '*.jpeg' {
|
||||||
|
const ref: string;
|
||||||
|
export default ref;
|
||||||
|
}
|
||||||
|
declare module '*.png' {
|
||||||
|
const ref: string;
|
||||||
|
export default ref;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* CUSTOM: ADD YOUR OWN HERE */
|
11911
packages/redux-devtools-rtk-query-inspector-monitor/demo/yarn.lock
Normal file
11911
packages/redux-devtools-rtk-query-inspector-monitor/demo/yarn.lock
Normal file
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,64 @@
|
||||||
|
{
|
||||||
|
"name": "@redux-devtools/rtk-query-inspector-monitor",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "rtk-query Monitor for Redux DevTools",
|
||||||
|
"keywords": [
|
||||||
|
"redux",
|
||||||
|
"devtools",
|
||||||
|
"flux",
|
||||||
|
"react",
|
||||||
|
"redux-toolkit",
|
||||||
|
"rtk-query"
|
||||||
|
],
|
||||||
|
"homepage": "https://github.com/reduxjs/redux-devtools/tree/master/packages/redux-devtools-chart-monitor",
|
||||||
|
"bugs": {
|
||||||
|
"url": "https://github.com/reduxjs/redux-devtools/issues"
|
||||||
|
},
|
||||||
|
"license": "MIT",
|
||||||
|
"author": "FaberVitale",
|
||||||
|
"files": [
|
||||||
|
"lib",
|
||||||
|
"src"
|
||||||
|
],
|
||||||
|
"main": "lib/index.js",
|
||||||
|
"types": "lib/index.d.ts",
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/reduxjs/redux-devtools.git"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"build": "npm run build:types && npm run build:js",
|
||||||
|
"build:types": "tsc --emitDeclarationOnly",
|
||||||
|
"start:dev": "tsc -p ./tsconfig.dev.json --watch",
|
||||||
|
"build:js": "babel src --out-dir lib --extensions \".ts,.tsx\" --source-maps inline",
|
||||||
|
"clean": "rimraf lib",
|
||||||
|
"lint": "eslint . --ext .ts,.tsx",
|
||||||
|
"lint:fix": "eslint . --ext .ts,.tsx --fix",
|
||||||
|
"type-check": "tsc --noEmit",
|
||||||
|
"type-check:watch": "npm run type-check -- --watch",
|
||||||
|
"preversion": "npm run type-check && npm run lint",
|
||||||
|
"prepublishOnly": "npm run clean && npm run build"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@types/prop-types": "^15.7.3",
|
||||||
|
"@types/redux-devtools-themes": "^1.0.0",
|
||||||
|
"@redux-devtools/dock-monitor": "^1.4.0",
|
||||||
|
"prop-types": "^15.7.2",
|
||||||
|
"redux-devtools-themes": "^1.0.0",
|
||||||
|
"devui": "^1.0.0-8"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@redux-devtools/core": "^3.9.0",
|
||||||
|
"@types/react": "^16.9.46",
|
||||||
|
"@reduxjs/toolkit": "^1.6.0",
|
||||||
|
"react": "^16.13.1",
|
||||||
|
"redux": "^4.0.5"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@redux-devtools/core": "^3.7.0",
|
||||||
|
"@reduxjs/toolkit": "^1.6.0",
|
||||||
|
"@types/react": "^16.3.0 || ^17.0.0",
|
||||||
|
"react": "^16.3.0 || ^17.0.0",
|
||||||
|
"redux": "^3.4.0 || ^4.0.0"
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,105 @@
|
||||||
|
import React, { Component, createRef, CSSProperties, ReactNode } from 'react';
|
||||||
|
import { AnyAction, Dispatch, Action } from 'redux';
|
||||||
|
import { LiftedAction, LiftedState } from '@redux-devtools/core';
|
||||||
|
import * as themes from 'redux-devtools-themes';
|
||||||
|
import { Base16Theme } from 'react-base16-styling';
|
||||||
|
import { QueryInfo, RtkQueryInspectorMonitorState } from './types';
|
||||||
|
import { createInspectorSelectors, computeSelectorSource } from './selectors';
|
||||||
|
import { selectQueryKey } from './reducers';
|
||||||
|
import { QueryList } from './components/QueryList';
|
||||||
|
import { StyleUtils } from './styles/createStylingFromTheme';
|
||||||
|
import { QueryForm } from './components/QueryForm';
|
||||||
|
|
||||||
|
const wrapperStyle: CSSProperties = {
|
||||||
|
width: '100%',
|
||||||
|
height: '100%',
|
||||||
|
};
|
||||||
|
|
||||||
|
type SelectorsSource<S> = {
|
||||||
|
currentState: S | null;
|
||||||
|
monitorState: RtkQueryInspectorMonitorState;
|
||||||
|
};
|
||||||
|
|
||||||
|
export interface RtkQueryInspectorProps<S, A extends Action<unknown>>
|
||||||
|
extends LiftedState<S, A, RtkQueryInspectorMonitorState> {
|
||||||
|
dispatch: Dispatch<LiftedAction<S, A, RtkQueryInspectorMonitorState>>;
|
||||||
|
theme: keyof typeof themes | Base16Theme;
|
||||||
|
invertTheme: boolean;
|
||||||
|
state: S | null;
|
||||||
|
styleUtils: StyleUtils;
|
||||||
|
}
|
||||||
|
|
||||||
|
type RtkQueryInspectorState<S> = { selectorsSource: SelectorsSource<S> };
|
||||||
|
|
||||||
|
class RtkQueryInspector<S, A extends Action<unknown>> extends Component<
|
||||||
|
RtkQueryInspectorProps<S, A>,
|
||||||
|
RtkQueryInspectorState<S>
|
||||||
|
> {
|
||||||
|
divRef = createRef<HTMLDivElement>();
|
||||||
|
|
||||||
|
constructor(props: RtkQueryInspectorProps<S, A>) {
|
||||||
|
super(props);
|
||||||
|
|
||||||
|
this.state = {
|
||||||
|
selectorsSource: computeSelectorSource(props, null),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
static getDerivedStateFromProps(
|
||||||
|
props: RtkQueryInspectorProps<unknown, Action<unknown>>,
|
||||||
|
state: RtkQueryInspectorState<unknown>
|
||||||
|
): null | RtkQueryInspectorState<unknown> {
|
||||||
|
const selectorsSource = computeSelectorSource<unknown, Action<unknown>>(
|
||||||
|
props,
|
||||||
|
state.selectorsSource
|
||||||
|
);
|
||||||
|
|
||||||
|
if (selectorsSource !== state.selectorsSource) {
|
||||||
|
return {
|
||||||
|
selectorsSource,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
selectors = createInspectorSelectors<S>();
|
||||||
|
|
||||||
|
handleSelectQuery = (queryInfo: QueryInfo) => {
|
||||||
|
this.props.dispatch(selectQueryKey(queryInfo) as AnyAction);
|
||||||
|
};
|
||||||
|
|
||||||
|
render(): ReactNode {
|
||||||
|
const { selectorsSource } = this.state;
|
||||||
|
const {
|
||||||
|
styleUtils: { styling },
|
||||||
|
} = this.props;
|
||||||
|
const apiStates = this.selectors.selectApiStates(selectorsSource);
|
||||||
|
const allSortedQueries = this.selectors.selectAllSortedQueries(
|
||||||
|
selectorsSource
|
||||||
|
);
|
||||||
|
|
||||||
|
console.log('inspector', { apiStates, allSortedQueries, selectorsSource });
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div style={wrapperStyle} ref={this.divRef}>
|
||||||
|
<div {...styling('querySectionWrapper')}>
|
||||||
|
<QueryForm
|
||||||
|
dispatch={this.props.dispatch}
|
||||||
|
queryComparator={selectorsSource.monitorState.queryComparator}
|
||||||
|
isAscendingQueryComparatorOrder={
|
||||||
|
selectorsSource.monitorState.isAscendingQueryComparatorOrder
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<QueryList
|
||||||
|
onSelectQuery={this.handleSelectQuery}
|
||||||
|
queryInfos={allSortedQueries}
|
||||||
|
selectedQueryKey={selectorsSource.monitorState.selectedQueryKey}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default RtkQueryInspector;
|
|
@ -0,0 +1,116 @@
|
||||||
|
import React, { CSSProperties, PureComponent } from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import * as themes from 'redux-devtools-themes';
|
||||||
|
import { Action } from 'redux';
|
||||||
|
import { Base16Theme } from 'react-base16-styling';
|
||||||
|
import RtkQueryInspector from './RtkQueryInspector';
|
||||||
|
import reducer from './reducers';
|
||||||
|
import {
|
||||||
|
ExternalProps,
|
||||||
|
RtkQueryInspectorMonitorProps,
|
||||||
|
RtkQueryInspectorMonitorState,
|
||||||
|
} from './types';
|
||||||
|
import {
|
||||||
|
createThemeState,
|
||||||
|
StyleUtils,
|
||||||
|
StyleUtilsContext,
|
||||||
|
} from './styles/createStylingFromTheme';
|
||||||
|
|
||||||
|
const styles: { container: CSSProperties } = {
|
||||||
|
container: {
|
||||||
|
fontFamily: 'monaco, Consolas, Lucida Console, monospace',
|
||||||
|
position: 'relative',
|
||||||
|
overflowY: 'hidden',
|
||||||
|
width: '100%',
|
||||||
|
height: '100%',
|
||||||
|
minWidth: 300,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
interface DefaultProps<S> {
|
||||||
|
select: (state: unknown) => unknown;
|
||||||
|
theme: keyof typeof themes | Base16Theme;
|
||||||
|
preserveScrollTop: boolean;
|
||||||
|
expandActionRoot: boolean;
|
||||||
|
expandStateRoot: boolean;
|
||||||
|
markStateDiff: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface RtkQueryInspectorComponentState {
|
||||||
|
readonly styleUtils: StyleUtils;
|
||||||
|
}
|
||||||
|
|
||||||
|
class RtkQueryInspectorMonitor<
|
||||||
|
S,
|
||||||
|
A extends Action<unknown>
|
||||||
|
> extends PureComponent<
|
||||||
|
RtkQueryInspectorMonitorProps<S, A>,
|
||||||
|
RtkQueryInspectorComponentState
|
||||||
|
> {
|
||||||
|
constructor(props: RtkQueryInspectorMonitorProps<S, A>) {
|
||||||
|
super(props);
|
||||||
|
|
||||||
|
this.state = {
|
||||||
|
styleUtils: createThemeState<S, A>(props),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
static update = reducer;
|
||||||
|
|
||||||
|
static propTypes = {
|
||||||
|
dispatch: PropTypes.func,
|
||||||
|
computedStates: PropTypes.array,
|
||||||
|
currentStateIndex: PropTypes.number,
|
||||||
|
actionsById: PropTypes.object,
|
||||||
|
stagedActionIds: PropTypes.array,
|
||||||
|
skippedActionIds: PropTypes.array,
|
||||||
|
monitorState: PropTypes.shape({
|
||||||
|
initialScrollTop: PropTypes.number,
|
||||||
|
}),
|
||||||
|
|
||||||
|
preserveScrollTop: PropTypes.bool,
|
||||||
|
select: PropTypes.func.isRequired,
|
||||||
|
theme: PropTypes.oneOfType([PropTypes.object, PropTypes.string]),
|
||||||
|
invertTheme: PropTypes.bool,
|
||||||
|
};
|
||||||
|
|
||||||
|
static defaultProps: DefaultProps<unknown> = {
|
||||||
|
select: (state: unknown) => state,
|
||||||
|
theme: 'nicinabox',
|
||||||
|
preserveScrollTop: true,
|
||||||
|
expandActionRoot: true,
|
||||||
|
expandStateRoot: true,
|
||||||
|
markStateDiff: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const {
|
||||||
|
styleUtils: { base16Theme, styling },
|
||||||
|
} = this.state;
|
||||||
|
|
||||||
|
const RtkQueryInspectorAsAny = RtkQueryInspector as any;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<StyleUtilsContext.Provider value={this.state.styleUtils}>
|
||||||
|
<div {...styling(['inspector'])}>
|
||||||
|
<RtkQueryInspectorAsAny
|
||||||
|
{...this.props}
|
||||||
|
theme={base16Theme}
|
||||||
|
styleUtils={this.state.styleUtils}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</StyleUtilsContext.Provider>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default (RtkQueryInspectorMonitor as unknown) as React.ComponentType<
|
||||||
|
ExternalProps<unknown, Action<unknown>>
|
||||||
|
> & {
|
||||||
|
update(
|
||||||
|
monitorProps: ExternalProps<unknown, Action<unknown>>,
|
||||||
|
state: RtkQueryInspectorMonitorState | undefined,
|
||||||
|
action: Action
|
||||||
|
): RtkQueryInspectorMonitorState;
|
||||||
|
defaultProps: DefaultProps<unknown>;
|
||||||
|
};
|
|
@ -0,0 +1,132 @@
|
||||||
|
import React, { ReactNode, FormEvent, MouseEvent } from 'react';
|
||||||
|
import { RtkQueryInspectorMonitorState } from '../types';
|
||||||
|
import { StyleUtilsContext } from '../styles/createStylingFromTheme';
|
||||||
|
import { Select } from 'devui';
|
||||||
|
import { AnyAction } from 'redux';
|
||||||
|
import { sortQueryOptions, QueryComparators } from '../utils/comparators';
|
||||||
|
import {
|
||||||
|
changeIsAscendingQueryComparatorOrder,
|
||||||
|
changeQueryComparator,
|
||||||
|
} from '../reducers';
|
||||||
|
export interface QueryFormProps
|
||||||
|
extends Pick<
|
||||||
|
RtkQueryInspectorMonitorState,
|
||||||
|
'isAscendingQueryComparatorOrder' | 'queryComparator'
|
||||||
|
> {
|
||||||
|
dispatch: (action: AnyAction) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ascId = 'rtk-query-rb-asc';
|
||||||
|
const descId = 'rtk-query-rb-desc';
|
||||||
|
const selectId = 'rtk-query-comp-select';
|
||||||
|
const searchId = 'rtk-query-search-query';
|
||||||
|
|
||||||
|
const searchPlaceholder = 'filter query...';
|
||||||
|
|
||||||
|
export class QueryForm extends React.PureComponent<QueryFormProps> {
|
||||||
|
handleSubmit = (evt: FormEvent<HTMLFormElement>): void => {
|
||||||
|
evt.preventDefault();
|
||||||
|
};
|
||||||
|
|
||||||
|
handleButtonGroupClick = ({ target }: MouseEvent<HTMLElement>): void => {
|
||||||
|
const { isAscendingQueryComparatorOrder: isAsc, dispatch } = this.props;
|
||||||
|
|
||||||
|
const targetId = (target as HTMLElement)?.id ?? null;
|
||||||
|
|
||||||
|
if (targetId === ascId && !isAsc) {
|
||||||
|
dispatch(changeIsAscendingQueryComparatorOrder(true));
|
||||||
|
} else if (targetId === descId && isAsc) {
|
||||||
|
this.props.dispatch(changeIsAscendingQueryComparatorOrder(false));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
handleSelectComparator = (option: { value: string }): void => {
|
||||||
|
const { dispatch } = this.props;
|
||||||
|
|
||||||
|
if (typeof option?.value === 'string') {
|
||||||
|
dispatch(changeQueryComparator(option.value as QueryComparators));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
getSelectedOption = (option: { value: string }): string => option?.value;
|
||||||
|
|
||||||
|
render(): ReactNode {
|
||||||
|
const {
|
||||||
|
isAscendingQueryComparatorOrder: isAsc,
|
||||||
|
queryComparator,
|
||||||
|
} = this.props;
|
||||||
|
|
||||||
|
const isDesc = !isAsc;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<StyleUtilsContext.Consumer>
|
||||||
|
{({ styling, base16Theme }) => {
|
||||||
|
return (
|
||||||
|
<form
|
||||||
|
action="#"
|
||||||
|
onSubmit={this.handleSubmit}
|
||||||
|
{...styling('queryForm')}
|
||||||
|
>
|
||||||
|
<div {...styling('queryListHeader')}>
|
||||||
|
<label htmlFor={searchId} {...styling('srOnly')}>
|
||||||
|
filter query
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
type="search"
|
||||||
|
placeholder={searchPlaceholder}
|
||||||
|
{...styling('querySearch')}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div {...styling('sortBySection')}>
|
||||||
|
<label htmlFor={selectId}>Sort by</label>
|
||||||
|
<Select
|
||||||
|
id={selectId}
|
||||||
|
isSearchable={false}
|
||||||
|
openOuterUp
|
||||||
|
theme={base16Theme}
|
||||||
|
value={sortQueryOptions.find(
|
||||||
|
(opt) => opt?.value === queryComparator
|
||||||
|
)}
|
||||||
|
options={sortQueryOptions}
|
||||||
|
onChange={this.handleSelectComparator}
|
||||||
|
selectOption={this.getSelectedOption}
|
||||||
|
/>
|
||||||
|
<div
|
||||||
|
tabIndex={0}
|
||||||
|
role="radiogroup"
|
||||||
|
aria-activedescendant={isAsc ? ascId : descId}
|
||||||
|
onClick={this.handleButtonGroupClick}
|
||||||
|
>
|
||||||
|
<button
|
||||||
|
role="radio"
|
||||||
|
type="button"
|
||||||
|
id={ascId}
|
||||||
|
aria-checked={isAsc}
|
||||||
|
{...styling(
|
||||||
|
['selectorButton', isAsc && 'selectorButtonSelected'],
|
||||||
|
isAsc
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
asc
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
id={descId}
|
||||||
|
role="radio"
|
||||||
|
type="button"
|
||||||
|
aria-checked={isDesc}
|
||||||
|
{...styling(
|
||||||
|
['selectorButton', isDesc && 'selectorButtonSelected'],
|
||||||
|
isDesc
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
desc
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
</StyleUtilsContext.Consumer>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,53 @@
|
||||||
|
import React, { PureComponent, ReactNode, MouseEvent } from 'react';
|
||||||
|
import { StyleUtilsContext } from '../styles/createStylingFromTheme';
|
||||||
|
import { QueryInfo, RtkQueryInspectorMonitorState } from '../types';
|
||||||
|
import { isQuerySelected } from '../utils/rtk-query';
|
||||||
|
|
||||||
|
export interface QueryListProps {
|
||||||
|
queryInfos: QueryInfo[];
|
||||||
|
selectedQueryKey: RtkQueryInspectorMonitorState['selectedQueryKey'];
|
||||||
|
onSelectQuery: (query: QueryInfo) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class QueryList extends PureComponent<QueryListProps> {
|
||||||
|
static isItemSelected(
|
||||||
|
selectedQueryKey: QueryListProps['selectedQueryKey'],
|
||||||
|
queryInfo: QueryInfo
|
||||||
|
): boolean {
|
||||||
|
return (
|
||||||
|
!!selectedQueryKey &&
|
||||||
|
selectedQueryKey.queryKey === queryInfo.queryKey &&
|
||||||
|
selectedQueryKey.reducerPath === queryInfo.reducerPath
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
render(): ReactNode {
|
||||||
|
const { queryInfos, selectedQueryKey, onSelectQuery } = this.props;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<StyleUtilsContext.Consumer>
|
||||||
|
{({ styling }) => (
|
||||||
|
<ul {...styling('queryList')}>
|
||||||
|
{queryInfos.map((queryInfo) => {
|
||||||
|
const isSelected = isQuerySelected(selectedQueryKey, queryInfo);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<li
|
||||||
|
key={queryInfo.queryKey}
|
||||||
|
onClick={() => onSelectQuery(queryInfo)}
|
||||||
|
{...styling(
|
||||||
|
['queryListItem', isSelected && 'queryListItemSelected'],
|
||||||
|
isSelected
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<p>{queryInfo.queryKey}</p>
|
||||||
|
<p {...styling('queryStatus')}>{queryInfo.query.status}</p>
|
||||||
|
</li>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</ul>
|
||||||
|
)}
|
||||||
|
</StyleUtilsContext.Consumer>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,2 @@
|
||||||
|
export { default } from './RtkQueryInspectorMonitor';
|
||||||
|
export { ExternalProps } from './types';
|
|
@ -0,0 +1,52 @@
|
||||||
|
import { Action, AnyAction } from 'redux';
|
||||||
|
import { createSlice, PayloadAction } from '@reduxjs/toolkit';
|
||||||
|
import { RtkQueryInspectorProps } from './RtkQueryInspector';
|
||||||
|
import { QueryInfo, RtkQueryInspectorMonitorState } from './types';
|
||||||
|
import { QueryComparators } from './utils/comparators';
|
||||||
|
|
||||||
|
const initialState: RtkQueryInspectorMonitorState = {
|
||||||
|
queryComparator: QueryComparators.fulfilledTimeStamp,
|
||||||
|
isAscendingQueryComparatorOrder: false,
|
||||||
|
selectedQueryKey: null,
|
||||||
|
};
|
||||||
|
|
||||||
|
const monitorSlice = createSlice({
|
||||||
|
name: 'rtk-query-monitor',
|
||||||
|
initialState,
|
||||||
|
reducers: {
|
||||||
|
changeQueryComparator(state, action: PayloadAction<QueryComparators>) {
|
||||||
|
state.queryComparator = action.payload;
|
||||||
|
},
|
||||||
|
changeIsAscendingQueryComparatorOrder(
|
||||||
|
state,
|
||||||
|
action: PayloadAction<boolean>
|
||||||
|
) {
|
||||||
|
state.isAscendingQueryComparatorOrder = !!action.payload;
|
||||||
|
},
|
||||||
|
selectQueryKey(
|
||||||
|
state,
|
||||||
|
action: PayloadAction<Pick<QueryInfo, 'reducerPath' | 'queryKey'>>
|
||||||
|
) {
|
||||||
|
state.selectedQueryKey = {
|
||||||
|
queryKey: action.payload.queryKey,
|
||||||
|
reducerPath: action.payload.reducerPath,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export default function reducer<S, A extends Action<unknown>>(
|
||||||
|
props: RtkQueryInspectorProps<S, A>,
|
||||||
|
state: RtkQueryInspectorMonitorState | undefined = initialState,
|
||||||
|
action: AnyAction
|
||||||
|
): RtkQueryInspectorMonitorState {
|
||||||
|
const output = monitorSlice.reducer(state, action);
|
||||||
|
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const {
|
||||||
|
changeIsAscendingQueryComparatorOrder,
|
||||||
|
changeQueryComparator,
|
||||||
|
selectQueryKey,
|
||||||
|
} = monitorSlice.actions;
|
|
@ -0,0 +1,94 @@
|
||||||
|
import { Action, createSelector, Selector } from '@reduxjs/toolkit';
|
||||||
|
import { RtkQueryInspectorProps } from './RtkQueryInspector';
|
||||||
|
import { QueryInfo, RtkQueryInspectorMonitorState } from './types';
|
||||||
|
import { Comparator, queryComparators } from './utils/comparators';
|
||||||
|
import {
|
||||||
|
getApiStatesOf,
|
||||||
|
extractAllApiQueries,
|
||||||
|
flipComparator,
|
||||||
|
} from './utils/rtk-query';
|
||||||
|
|
||||||
|
type SelectorsSource<S> = {
|
||||||
|
currentState: S | null;
|
||||||
|
monitorState: RtkQueryInspectorMonitorState;
|
||||||
|
};
|
||||||
|
|
||||||
|
type InspectorSelector<S, Output> = Selector<SelectorsSource<S>, Output>;
|
||||||
|
|
||||||
|
export function computeSelectorSource<S, A extends Action<unknown>>(
|
||||||
|
props: RtkQueryInspectorProps<S, A>,
|
||||||
|
previous: SelectorsSource<S> | null = null
|
||||||
|
): SelectorsSource<S> {
|
||||||
|
const { computedStates, currentStateIndex, monitorState } = props;
|
||||||
|
|
||||||
|
const currentState =
|
||||||
|
computedStates.length > 0 ? computedStates[currentStateIndex].state : null;
|
||||||
|
|
||||||
|
if (
|
||||||
|
!previous ||
|
||||||
|
previous.currentState !== currentState ||
|
||||||
|
previous.monitorState !== monitorState
|
||||||
|
) {
|
||||||
|
return {
|
||||||
|
currentState,
|
||||||
|
monitorState,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return previous;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface InspectorSelectors<S> {
|
||||||
|
readonly selectQueryComparator: InspectorSelector<S, Comparator<QueryInfo>>;
|
||||||
|
readonly selectApiStates: InspectorSelector<
|
||||||
|
S,
|
||||||
|
ReturnType<typeof getApiStatesOf>
|
||||||
|
>;
|
||||||
|
readonly selectAllQueries: InspectorSelector<
|
||||||
|
S,
|
||||||
|
ReturnType<typeof extractAllApiQueries>
|
||||||
|
>;
|
||||||
|
readonly selectAllSortedQueries: InspectorSelector<S, QueryInfo[]>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createInspectorSelectors<S>(): InspectorSelectors<S> {
|
||||||
|
const selectQueryComparator = ({
|
||||||
|
monitorState,
|
||||||
|
}: SelectorsSource<S>): Comparator<QueryInfo> => {
|
||||||
|
return queryComparators[monitorState.queryComparator];
|
||||||
|
};
|
||||||
|
|
||||||
|
const selectApiStates = createSelector(
|
||||||
|
({ currentState }: SelectorsSource<S>) => currentState,
|
||||||
|
getApiStatesOf
|
||||||
|
);
|
||||||
|
const selectAllQueries = createSelector(
|
||||||
|
selectApiStates,
|
||||||
|
extractAllApiQueries
|
||||||
|
);
|
||||||
|
|
||||||
|
const selectAllSortedQueries = createSelector(
|
||||||
|
[
|
||||||
|
selectQueryComparator,
|
||||||
|
selectAllQueries,
|
||||||
|
({ monitorState }: SelectorsSource<S>) =>
|
||||||
|
monitorState.isAscendingQueryComparatorOrder,
|
||||||
|
],
|
||||||
|
(comparator, queryList, isAscending) => {
|
||||||
|
console.log({ comparator, queryList, isAscending });
|
||||||
|
|
||||||
|
const computedComparator = isAscending
|
||||||
|
? comparator
|
||||||
|
: flipComparator(comparator);
|
||||||
|
|
||||||
|
return queryList.slice().sort(computedComparator);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
selectQueryComparator,
|
||||||
|
selectApiStates,
|
||||||
|
selectAllQueries,
|
||||||
|
selectAllSortedQueries,
|
||||||
|
};
|
||||||
|
}
|
|
@ -0,0 +1,276 @@
|
||||||
|
import jss, { StyleSheet } from 'jss';
|
||||||
|
import preset from 'jss-preset-default';
|
||||||
|
import {
|
||||||
|
createStyling,
|
||||||
|
getBase16Theme,
|
||||||
|
invertTheme,
|
||||||
|
StylingFunction,
|
||||||
|
} from 'react-base16-styling';
|
||||||
|
import rgba from 'hex-rgba';
|
||||||
|
import { Base16Theme } from 'redux-devtools-themes';
|
||||||
|
import { rtkInspectorTheme } from './theme';
|
||||||
|
import * as reduxThemes from 'redux-devtools-themes';
|
||||||
|
import { Action } from 'redux';
|
||||||
|
import { RtkQueryInspectorMonitorProps } from '../types';
|
||||||
|
import { createContext } from 'react';
|
||||||
|
|
||||||
|
jss.setup(preset());
|
||||||
|
|
||||||
|
export const colorMap = (theme: Base16Theme) => ({
|
||||||
|
TEXT_COLOR: theme.base06,
|
||||||
|
TEXT_PLACEHOLDER_COLOR: rgba(theme.base06, 60),
|
||||||
|
BACKGROUND_COLOR: theme.base00,
|
||||||
|
SELECTED_BACKGROUND_COLOR: rgba(theme.base03, 20),
|
||||||
|
SKIPPED_BACKGROUND_COLOR: rgba(theme.base03, 10),
|
||||||
|
HEADER_BACKGROUND_COLOR: rgba(theme.base03, 30),
|
||||||
|
HEADER_BORDER_COLOR: rgba(theme.base03, 20),
|
||||||
|
BORDER_COLOR: rgba(theme.base03, 50),
|
||||||
|
LIST_BORDER_COLOR: rgba(theme.base03, 50),
|
||||||
|
ACTION_TIME_BACK_COLOR: rgba(theme.base03, 20),
|
||||||
|
ACTION_TIME_COLOR: theme.base04,
|
||||||
|
PIN_COLOR: theme.base04,
|
||||||
|
ITEM_HINT_COLOR: rgba(theme.base0F, 90),
|
||||||
|
TAB_BACK_SELECTED_COLOR: rgba(theme.base03, 20),
|
||||||
|
TAB_BACK_COLOR: rgba(theme.base00, 70),
|
||||||
|
TAB_BACK_HOVER_COLOR: rgba(theme.base03, 40),
|
||||||
|
TAB_BORDER_COLOR: rgba(theme.base03, 50),
|
||||||
|
DIFF_ADD_COLOR: rgba(theme.base0B, 40),
|
||||||
|
DIFF_REMOVE_COLOR: rgba(theme.base08, 40),
|
||||||
|
DIFF_ARROW_COLOR: theme.base0E,
|
||||||
|
LINK_COLOR: rgba(theme.base0E, 90),
|
||||||
|
LINK_HOVER_COLOR: theme.base0E,
|
||||||
|
ERROR_COLOR: theme.base08,
|
||||||
|
});
|
||||||
|
|
||||||
|
type Color = keyof ReturnType<typeof colorMap>;
|
||||||
|
type ColorMap = {
|
||||||
|
[color in Color]: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
const getSheetFromColorMap = (map: ColorMap) => ({
|
||||||
|
inspector: {
|
||||||
|
display: 'flex',
|
||||||
|
'flex-direction': 'column',
|
||||||
|
width: '100%',
|
||||||
|
height: '100%',
|
||||||
|
'font-family': 'monaco, Consolas, "Lucida Console", monospace',
|
||||||
|
'font-size': '12px',
|
||||||
|
'font-smoothing': 'antialiased',
|
||||||
|
'line-height': '1.5em',
|
||||||
|
|
||||||
|
'background-color': map.BACKGROUND_COLOR,
|
||||||
|
color: map.TEXT_COLOR,
|
||||||
|
},
|
||||||
|
|
||||||
|
querySectionWrapper: {
|
||||||
|
display: 'flex',
|
||||||
|
height: '100%',
|
||||||
|
flexFlow: 'column nowrap',
|
||||||
|
'& > :first-child': {
|
||||||
|
flex: '0 0 auto',
|
||||||
|
'border-bottom-width': '1px',
|
||||||
|
'border-bottom-style': 'solid',
|
||||||
|
'border-color': map.LIST_BORDER_COLOR,
|
||||||
|
},
|
||||||
|
'& > :nth-child(n + 2)': {
|
||||||
|
flex: '1 1 auto',
|
||||||
|
overflowX: 'hidden',
|
||||||
|
overflowY: 'auto',
|
||||||
|
maxHeight: 'calc(100% - 70px)',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
queryList: {
|
||||||
|
listStyle: 'none',
|
||||||
|
margin: '0',
|
||||||
|
padding: '0',
|
||||||
|
},
|
||||||
|
|
||||||
|
queryListItem: {
|
||||||
|
'border-bottom-width': '1px',
|
||||||
|
'border-bottom-style': 'solid',
|
||||||
|
display: 'flex',
|
||||||
|
'justify-content': 'space-between',
|
||||||
|
padding: '5px 10px',
|
||||||
|
cursor: 'pointer',
|
||||||
|
'user-select': 'none',
|
||||||
|
|
||||||
|
'& > :first-child': {
|
||||||
|
whiteSpace: 'nowrap',
|
||||||
|
overflowX: 'hidden',
|
||||||
|
maxWidth: 'calc(100% - 70px)',
|
||||||
|
textOverflow: 'ellipsis',
|
||||||
|
},
|
||||||
|
'&:last-child': {
|
||||||
|
'border-bottom-width': 0,
|
||||||
|
},
|
||||||
|
|
||||||
|
'border-bottom-color': map.BORDER_COLOR,
|
||||||
|
},
|
||||||
|
|
||||||
|
queryListHeader: {
|
||||||
|
display: 'flex',
|
||||||
|
flex: '0 0 auto',
|
||||||
|
'align-items': 'center',
|
||||||
|
'border-bottom-width': '1px',
|
||||||
|
'border-bottom-style': 'solid',
|
||||||
|
|
||||||
|
'border-color': map.LIST_BORDER_COLOR,
|
||||||
|
},
|
||||||
|
|
||||||
|
queryStatus: {
|
||||||
|
display: 'inline-flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center',
|
||||||
|
height: 22,
|
||||||
|
padding: '0 6px',
|
||||||
|
'border-radius': '3px',
|
||||||
|
'font-size': '0.7em',
|
||||||
|
'line-height': '1em',
|
||||||
|
'flex-shrink': 0,
|
||||||
|
'background-color': map.ACTION_TIME_BACK_COLOR,
|
||||||
|
color: map.ACTION_TIME_COLOR,
|
||||||
|
},
|
||||||
|
|
||||||
|
queryListItemSelected: {
|
||||||
|
'background-color': map.SELECTED_BACKGROUND_COLOR,
|
||||||
|
},
|
||||||
|
|
||||||
|
tabSelector: {
|
||||||
|
position: 'relative',
|
||||||
|
'z-index': 1,
|
||||||
|
display: 'inline-flex',
|
||||||
|
float: 'right',
|
||||||
|
},
|
||||||
|
|
||||||
|
srOnly: {
|
||||||
|
position: 'absolute',
|
||||||
|
width: 1,
|
||||||
|
height: 1,
|
||||||
|
padding: 0,
|
||||||
|
margin: '-1px',
|
||||||
|
overflow: 'hidden',
|
||||||
|
clip: 'rect(0,0,0,0)',
|
||||||
|
border: 0,
|
||||||
|
},
|
||||||
|
|
||||||
|
selectorButton: {
|
||||||
|
cursor: 'pointer',
|
||||||
|
position: 'relative',
|
||||||
|
padding: '6.5px 10px',
|
||||||
|
color: map.TEXT_COLOR,
|
||||||
|
'border-style': 'solid',
|
||||||
|
'border-width': '1px',
|
||||||
|
'border-left-width': 0,
|
||||||
|
|
||||||
|
'&:first-child': {
|
||||||
|
'border-left-width': '1px',
|
||||||
|
'border-top-left-radius': '3px',
|
||||||
|
'border-bottom-left-radius': '3px',
|
||||||
|
},
|
||||||
|
|
||||||
|
'&:last-child': {
|
||||||
|
'border-top-right-radius': '3px',
|
||||||
|
'border-bottom-right-radius': '3px',
|
||||||
|
},
|
||||||
|
|
||||||
|
'background-color': map.TAB_BACK_COLOR,
|
||||||
|
|
||||||
|
'&:hover': {
|
||||||
|
'background-color': map.TAB_BACK_HOVER_COLOR,
|
||||||
|
},
|
||||||
|
|
||||||
|
'border-color': map.TAB_BORDER_COLOR,
|
||||||
|
},
|
||||||
|
|
||||||
|
selectorButtonSmall: {
|
||||||
|
padding: '0px 8px',
|
||||||
|
'font-size': '0.8em',
|
||||||
|
},
|
||||||
|
|
||||||
|
selectorButtonSelected: {
|
||||||
|
'background-color': map.TAB_BACK_SELECTED_COLOR,
|
||||||
|
},
|
||||||
|
|
||||||
|
queryForm: {
|
||||||
|
display: 'flex',
|
||||||
|
flexFlow: 'column nowrap',
|
||||||
|
},
|
||||||
|
sortBySection: {
|
||||||
|
display: 'flex',
|
||||||
|
padding: '0.4em',
|
||||||
|
'& > [role="radiogroup"]': {
|
||||||
|
flex: '0 0 auto',
|
||||||
|
padding: '0 0 0 0.4em',
|
||||||
|
},
|
||||||
|
'& label': {
|
||||||
|
display: 'flex',
|
||||||
|
flex: '0 0 auto',
|
||||||
|
whiteSpace: 'noWrap',
|
||||||
|
alignItems: 'center',
|
||||||
|
paddingRight: '0.4em',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
querySearch: {
|
||||||
|
outline: 'none',
|
||||||
|
border: 'none',
|
||||||
|
width: '100%',
|
||||||
|
padding: '5px 10px',
|
||||||
|
'font-size': '1em',
|
||||||
|
'font-family': 'monaco, Consolas, "Lucida Console", monospace',
|
||||||
|
|
||||||
|
'background-color': map.BACKGROUND_COLOR,
|
||||||
|
color: map.TEXT_COLOR,
|
||||||
|
|
||||||
|
'&::-webkit-input-placeholder': {
|
||||||
|
color: map.TEXT_PLACEHOLDER_COLOR,
|
||||||
|
},
|
||||||
|
|
||||||
|
'&::-moz-placeholder': {
|
||||||
|
color: map.TEXT_PLACEHOLDER_COLOR,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
let themeSheet: StyleSheet;
|
||||||
|
|
||||||
|
const getDefaultThemeStyling = (theme: Base16Theme) => {
|
||||||
|
if (themeSheet) {
|
||||||
|
themeSheet.detach();
|
||||||
|
}
|
||||||
|
|
||||||
|
themeSheet = jss
|
||||||
|
.createStyleSheet(getSheetFromColorMap(colorMap(theme)))
|
||||||
|
.attach();
|
||||||
|
|
||||||
|
return themeSheet.classes;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const base16Themes = { ...reduxThemes };
|
||||||
|
|
||||||
|
export const createStylingFromTheme = createStyling(getDefaultThemeStyling, {
|
||||||
|
defaultBase16: rtkInspectorTheme,
|
||||||
|
base16Themes,
|
||||||
|
});
|
||||||
|
|
||||||
|
export interface StyleUtils {
|
||||||
|
base16Theme: Base16Theme;
|
||||||
|
styling: StylingFunction;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createThemeState<S, A extends Action<unknown>>(
|
||||||
|
props: RtkQueryInspectorMonitorProps<S, A>
|
||||||
|
): StyleUtils {
|
||||||
|
const base16Theme =
|
||||||
|
getBase16Theme(props.theme, base16Themes) ?? rtkInspectorTheme;
|
||||||
|
|
||||||
|
const theme = props.invertTheme ? invertTheme(props.theme) : props.theme;
|
||||||
|
const styling = createStylingFromTheme(theme);
|
||||||
|
|
||||||
|
return { base16Theme, styling };
|
||||||
|
}
|
||||||
|
|
||||||
|
export const StyleUtilsContext = createContext<StyleUtils>({
|
||||||
|
base16Theme: rtkInspectorTheme,
|
||||||
|
styling: (...args: any[]) => ({ className: '', style: {} }),
|
||||||
|
});
|
|
@ -0,0 +1,25 @@
|
||||||
|
import { Base16Theme } from 'react-base16-styling';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lifted from `packages/redux-devtools-inspector-monitor/src/themes/inspector.ts`
|
||||||
|
*/
|
||||||
|
export const rtkInspectorTheme: Base16Theme = {
|
||||||
|
scheme: 'rtk-inspector',
|
||||||
|
author: 'Alexander Kuznetsov (alexkuz@gmail.com)',
|
||||||
|
base00: '#181818',
|
||||||
|
base01: '#282828',
|
||||||
|
base02: '#383838',
|
||||||
|
base03: '#585858',
|
||||||
|
base04: '#b8b8b8',
|
||||||
|
base05: '#d8d8d8',
|
||||||
|
base06: '#e8e8e8',
|
||||||
|
base07: '#FFFFFF',
|
||||||
|
base08: '#E92F28',
|
||||||
|
base09: '#dc9656',
|
||||||
|
base0A: '#f7ca88',
|
||||||
|
base0B: '#65AD00',
|
||||||
|
base0C: '#86c1b9',
|
||||||
|
base0D: '#347BD9',
|
||||||
|
base0E: '#EC31C0',
|
||||||
|
base0F: '#a16946',
|
||||||
|
};
|
|
@ -0,0 +1,65 @@
|
||||||
|
import { LiftedAction, LiftedState } from '@redux-devtools/instrument';
|
||||||
|
import type { createApi } from '@reduxjs/toolkit/query';
|
||||||
|
import { Dispatch } from 'react';
|
||||||
|
import { Base16Theme } from 'react-base16-styling';
|
||||||
|
import { Action } from 'redux';
|
||||||
|
import * as themes from 'redux-devtools-themes';
|
||||||
|
import { QueryComparators } from './utils/comparators';
|
||||||
|
|
||||||
|
export interface RtkQueryInspectorMonitorState {
|
||||||
|
queryComparator: QueryComparators;
|
||||||
|
isAscendingQueryComparatorOrder: boolean;
|
||||||
|
selectedQueryKey: Pick<QueryInfo, 'reducerPath' | 'queryKey'> | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface RtkQueryInspectorMonitorProps<S, A extends Action<unknown>>
|
||||||
|
extends LiftedState<S, A, RtkQueryInspectorMonitorState> {
|
||||||
|
dispatch: Dispatch<
|
||||||
|
Action | LiftedAction<S, A, RtkQueryInspectorMonitorState>
|
||||||
|
>;
|
||||||
|
|
||||||
|
preserveScrollTop: boolean;
|
||||||
|
select: (state: S) => unknown;
|
||||||
|
theme: keyof typeof themes | Base16Theme;
|
||||||
|
expandActionRoot: boolean;
|
||||||
|
expandStateRoot: boolean;
|
||||||
|
markStateDiff: boolean;
|
||||||
|
hideMainButtons?: boolean;
|
||||||
|
invertTheme?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type RtkQueryApiState = ReturnType<
|
||||||
|
ReturnType<typeof createApi>['reducer']
|
||||||
|
>;
|
||||||
|
|
||||||
|
export type RtkQueryState = NonNullable<
|
||||||
|
RtkQueryApiState['queries'][keyof RtkQueryApiState]
|
||||||
|
>;
|
||||||
|
|
||||||
|
export interface ExternalProps<S, A extends Action<unknown>> {
|
||||||
|
dispatch: Dispatch<
|
||||||
|
Action | LiftedAction<S, A, RtkQueryInspectorMonitorState>
|
||||||
|
>;
|
||||||
|
|
||||||
|
preserveScrollTop: boolean;
|
||||||
|
select: (state: S) => unknown;
|
||||||
|
theme: keyof typeof themes | Base16Theme;
|
||||||
|
expandActionRoot: boolean;
|
||||||
|
expandStateRoot: boolean;
|
||||||
|
markStateDiff: boolean;
|
||||||
|
hideMainButtons?: boolean;
|
||||||
|
invertTheme: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type AnyExternalProps = ExternalProps<unknown, any>;
|
||||||
|
|
||||||
|
export interface QueryInfo {
|
||||||
|
query: RtkQueryState;
|
||||||
|
queryKey: string;
|
||||||
|
reducerPath: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ApiInfo {
|
||||||
|
reducerPath: string;
|
||||||
|
apiState: RtkQueryApiState;
|
||||||
|
}
|
|
@ -0,0 +1,82 @@
|
||||||
|
import { QueryStatus } from '@reduxjs/toolkit/dist/query';
|
||||||
|
import { QueryInfo } from '../types';
|
||||||
|
|
||||||
|
export interface Comparator<T> {
|
||||||
|
(a: T, b: T): number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum QueryComparators {
|
||||||
|
fulfilledTimeStamp = 'timestamp',
|
||||||
|
queryKey = 'key',
|
||||||
|
status = 'status',
|
||||||
|
endpointName = 'endpointName',
|
||||||
|
}
|
||||||
|
|
||||||
|
export const sortQueryOptions: { label: string; value: string }[] = [
|
||||||
|
{ label: 'fulfilledTimeStamp', value: QueryComparators.fulfilledTimeStamp },
|
||||||
|
{ label: 'query key', value: QueryComparators.queryKey },
|
||||||
|
{ label: 'status ', value: QueryComparators.status },
|
||||||
|
{ label: 'endpoint', value: QueryComparators.endpointName },
|
||||||
|
];
|
||||||
|
|
||||||
|
function sortQueryByFulfilled(
|
||||||
|
thisQueryInfo: QueryInfo,
|
||||||
|
thatQueryInfo: QueryInfo
|
||||||
|
): number {
|
||||||
|
const thisFulfilled = thisQueryInfo.query.fulfilledTimeStamp ?? -1;
|
||||||
|
const thatFulfilled = thatQueryInfo.query.fulfilledTimeStamp ?? -1;
|
||||||
|
|
||||||
|
return thisFulfilled - thatFulfilled;
|
||||||
|
}
|
||||||
|
|
||||||
|
const mapStatusToFactor = {
|
||||||
|
[QueryStatus.uninitialized]: 1,
|
||||||
|
[QueryStatus.pending]: 2,
|
||||||
|
[QueryStatus.rejected]: 3,
|
||||||
|
[QueryStatus.fulfilled]: 4,
|
||||||
|
};
|
||||||
|
|
||||||
|
function sortQueryByStatus(
|
||||||
|
thisQueryInfo: QueryInfo,
|
||||||
|
thatQueryInfo: QueryInfo
|
||||||
|
): number {
|
||||||
|
const thisTerm = mapStatusToFactor[thisQueryInfo.query.status] || -1;
|
||||||
|
const thatTerm = mapStatusToFactor[thatQueryInfo.query.status] || -1;
|
||||||
|
|
||||||
|
return thisTerm - thatTerm;
|
||||||
|
}
|
||||||
|
|
||||||
|
function compareStrings(a: string, b: string): number {
|
||||||
|
if (a === b) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return a > b ? 1 : -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
function sortByQueryKey(
|
||||||
|
thisQueryInfo: QueryInfo,
|
||||||
|
thatQueryInfo: QueryInfo
|
||||||
|
): number {
|
||||||
|
return compareStrings(thisQueryInfo.queryKey, thatQueryInfo.queryKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
function sortQueryByEndpointName(
|
||||||
|
thisQueryInfo: QueryInfo,
|
||||||
|
thatQueryInfo: QueryInfo
|
||||||
|
): number {
|
||||||
|
const thisEndpointName = thisQueryInfo.query.endpointName ?? '';
|
||||||
|
const thatEndpointName = thatQueryInfo.query.endpointName ?? '';
|
||||||
|
|
||||||
|
return compareStrings(thisEndpointName, thatEndpointName);
|
||||||
|
}
|
||||||
|
|
||||||
|
export const queryComparators: Readonly<Record<
|
||||||
|
QueryComparators,
|
||||||
|
Comparator<QueryInfo>
|
||||||
|
>> = {
|
||||||
|
[QueryComparators.fulfilledTimeStamp]: sortQueryByFulfilled,
|
||||||
|
[QueryComparators.status]: sortQueryByStatus,
|
||||||
|
[QueryComparators.endpointName]: sortQueryByEndpointName,
|
||||||
|
[QueryComparators.queryKey]: sortByQueryKey,
|
||||||
|
};
|
|
@ -0,0 +1,9 @@
|
||||||
|
import { freeze } from '@reduxjs/toolkit';
|
||||||
|
|
||||||
|
export const emptyArray = freeze([]);
|
||||||
|
|
||||||
|
export const emptyRecord = freeze({});
|
||||||
|
|
||||||
|
export function identity<T>(val: T): T {
|
||||||
|
return val;
|
||||||
|
}
|
|
@ -0,0 +1,109 @@
|
||||||
|
import { isPlainObject } from '@reduxjs/toolkit';
|
||||||
|
import type { createApi } from '@reduxjs/toolkit/query';
|
||||||
|
import { QueryInfo, RtkQueryInspectorMonitorState } from '../types';
|
||||||
|
import { Comparator } from './comparators';
|
||||||
|
import { emptyArray } from './object';
|
||||||
|
|
||||||
|
export type RtkQueryApiState = ReturnType<
|
||||||
|
ReturnType<typeof createApi>['reducer']
|
||||||
|
>;
|
||||||
|
|
||||||
|
const rtkqueryApiStateKeys: ReadonlyArray<keyof RtkQueryApiState> = [
|
||||||
|
'queries',
|
||||||
|
'mutations',
|
||||||
|
'config',
|
||||||
|
'provided',
|
||||||
|
'subscriptions',
|
||||||
|
];
|
||||||
|
|
||||||
|
export function isApiSlice(val: unknown): val is RtkQueryApiState {
|
||||||
|
if (!isPlainObject(val)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let i = 0, len = rtkqueryApiStateKeys.length; i < len; i++) {
|
||||||
|
if (
|
||||||
|
!isPlainObject((val as Record<string, unknown>)[rtkqueryApiStateKeys[i]])
|
||||||
|
) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getApiStatesOf(
|
||||||
|
state: unknown
|
||||||
|
): null | Readonly<Record<string, RtkQueryApiState>> {
|
||||||
|
if (!isPlainObject(state)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const output: null | Record<string, RtkQueryApiState> = {};
|
||||||
|
const keys = Object.keys(state);
|
||||||
|
|
||||||
|
for (let i = 0, len = keys.length; i < len; i++) {
|
||||||
|
const key = keys[i];
|
||||||
|
const value = (state as Record<string, unknown>)[key];
|
||||||
|
|
||||||
|
if (isApiSlice(value)) {
|
||||||
|
output[key] = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Object.keys(output).length === 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function extractAllApiQueries(
|
||||||
|
apiStatesByReducerPath: null | Readonly<Record<string, RtkQueryApiState>>
|
||||||
|
): ReadonlyArray<QueryInfo> {
|
||||||
|
if (!apiStatesByReducerPath) {
|
||||||
|
return emptyArray;
|
||||||
|
}
|
||||||
|
|
||||||
|
const reducerPaths = Object.keys(apiStatesByReducerPath);
|
||||||
|
|
||||||
|
const output: QueryInfo[] = [];
|
||||||
|
|
||||||
|
for (let i = 0, len = reducerPaths.length; i < len; i++) {
|
||||||
|
const reducerPath = reducerPaths[i];
|
||||||
|
const api = apiStatesByReducerPath[reducerPath];
|
||||||
|
const queryKeys = Object.keys(api.queries);
|
||||||
|
|
||||||
|
for (let j = 0, qKeysLen = queryKeys.length; j < qKeysLen; j++) {
|
||||||
|
const queryKey = queryKeys[j];
|
||||||
|
const query = api.queries[queryKey];
|
||||||
|
|
||||||
|
if (query) {
|
||||||
|
output.push({
|
||||||
|
reducerPath,
|
||||||
|
queryKey,
|
||||||
|
query,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function flipComparator<T>(comparator: Comparator<T>): Comparator<T> {
|
||||||
|
return function flipped(a: T, b: T) {
|
||||||
|
return comparator(b, a);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isQuerySelected(
|
||||||
|
selectedQueryKey: RtkQueryInspectorMonitorState['selectedQueryKey'],
|
||||||
|
queryInfo: QueryInfo
|
||||||
|
): boolean {
|
||||||
|
return (
|
||||||
|
!!selectedQueryKey &&
|
||||||
|
selectedQueryKey.queryKey === queryInfo.queryKey &&
|
||||||
|
selectedQueryKey.reducerPath === queryInfo.reducerPath
|
||||||
|
);
|
||||||
|
}
|
|
@ -0,0 +1,10 @@
|
||||||
|
{
|
||||||
|
"extends": "../../tsconfig.react.base.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"outDir": "./demo/src/generated-module",
|
||||||
|
"module": "ES2015",
|
||||||
|
"strict": false
|
||||||
|
},
|
||||||
|
"include": ["src"],
|
||||||
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
{
|
{
|
||||||
"extends": "../../tsconfig.base.json",
|
"extends": "../../tsconfig.react.base.json",
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"outDir": "lib"
|
"outDir": "lib"
|
||||||
},
|
},
|
Loading…
Reference in New Issue
Block a user