mirror of
https://github.com/reduxjs/redux-devtools.git
synced 2025-07-27 00:19:55 +03:00
feat(search): add search logic and refactor monitor state shape
This commit is contained in:
parent
143a01edba
commit
ad5b52a4e7
|
@ -18,7 +18,8 @@
|
||||||
"react-scripts": "4.0.2",
|
"react-scripts": "4.0.2",
|
||||||
"redux": "^4.0.5",
|
"redux": "^4.0.5",
|
||||||
"redux-devtools-themes": "^1.0.0",
|
"redux-devtools-themes": "^1.0.0",
|
||||||
"react-json-tree": "^0.15.0"
|
"react-json-tree": "^0.15.0",
|
||||||
|
"lodash.debounce": "^4.0.8"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/react": "17.0.0",
|
"@types/react": "17.0.0",
|
||||||
|
|
|
@ -46,7 +46,8 @@
|
||||||
"prop-types": "^15.7.2",
|
"prop-types": "^15.7.2",
|
||||||
"redux-devtools-themes": "^1.0.0",
|
"redux-devtools-themes": "^1.0.0",
|
||||||
"devui": "^1.0.0-8",
|
"devui": "^1.0.0-8",
|
||||||
"react-json-tree": "^0.15.0"
|
"react-json-tree": "^0.15.0",
|
||||||
|
"lodash.debounce": "^4.0.8"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@redux-devtools/core": "^3.9.0",
|
"@redux-devtools/core": "^3.9.0",
|
||||||
|
|
|
@ -3,16 +3,20 @@ import { AnyAction, Dispatch, Action } from 'redux';
|
||||||
import { LiftedAction, LiftedState } from '@redux-devtools/core';
|
import { LiftedAction, LiftedState } from '@redux-devtools/core';
|
||||||
import * as themes from 'redux-devtools-themes';
|
import * as themes from 'redux-devtools-themes';
|
||||||
import { Base16Theme } from 'react-base16-styling';
|
import { Base16Theme } from 'react-base16-styling';
|
||||||
import { QueryInfo, RtkQueryInspectorMonitorState } from './types';
|
import {
|
||||||
|
QueryFormValues,
|
||||||
|
QueryInfo,
|
||||||
|
RtkQueryInspectorMonitorState,
|
||||||
|
} from './types';
|
||||||
import { createInspectorSelectors, computeSelectorSource } from './selectors';
|
import { createInspectorSelectors, computeSelectorSource } from './selectors';
|
||||||
import { selectQueryKey } from './reducers';
|
import { changeQueryFormValues, selectQueryKey } from './reducers';
|
||||||
import { QueryList } from './components/QueryList';
|
import { QueryList } from './components/QueryList';
|
||||||
import { StyleUtils } from './styles/createStylingFromTheme';
|
import { StyleUtils } from './styles/createStylingFromTheme';
|
||||||
import { QueryForm } from './components/QueryForm';
|
import { QueryForm } from './components/QueryForm';
|
||||||
import { QueryPreview } from './components/QueryPreview';
|
import { QueryPreview } from './components/QueryPreview';
|
||||||
|
|
||||||
type SelectorsSource<S> = {
|
type SelectorsSource<S> = {
|
||||||
currentState: S | null;
|
userState: S | null;
|
||||||
monitorState: RtkQueryInspectorMonitorState;
|
monitorState: RtkQueryInspectorMonitorState;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -47,7 +51,7 @@ class RtkQueryInspector<S, A extends Action<unknown>> extends Component<
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
static wideLayout = 500;
|
static wideLayout = 650;
|
||||||
|
|
||||||
static getDerivedStateFromProps(
|
static getDerivedStateFromProps(
|
||||||
props: RtkQueryInspectorProps<unknown, Action<unknown>>,
|
props: RtkQueryInspectorProps<unknown, Action<unknown>>,
|
||||||
|
@ -80,18 +84,22 @@ class RtkQueryInspector<S, A extends Action<unknown>> extends Component<
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount(): void {
|
||||||
this.updateSizeMode();
|
this.updateSizeMode();
|
||||||
|
|
||||||
this.isWideIntervalRef = setInterval(this.updateSizeMode, 200);
|
this.isWideIntervalRef = setInterval(this.updateSizeMode, 200);
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillUnmount() {
|
componentWillUnmount(): void {
|
||||||
if (this.isWideIntervalRef) {
|
if (this.isWideIntervalRef) {
|
||||||
clearTimeout(this.isWideIntervalRef as any);
|
clearTimeout(this.isWideIntervalRef as any);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handleQueryFormValuesChange = (values: Partial<QueryFormValues>): void => {
|
||||||
|
this.props.dispatch(changeQueryFormValues(values) as AnyAction);
|
||||||
|
};
|
||||||
|
|
||||||
handleSelectQuery = (queryInfo: QueryInfo): void => {
|
handleSelectQuery = (queryInfo: QueryInfo): void => {
|
||||||
this.props.dispatch(selectQueryKey(queryInfo) as AnyAction);
|
this.props.dispatch(selectQueryKey(queryInfo) as AnyAction);
|
||||||
};
|
};
|
||||||
|
@ -102,7 +110,7 @@ class RtkQueryInspector<S, A extends Action<unknown>> extends Component<
|
||||||
styleUtils: { styling },
|
styleUtils: { styling },
|
||||||
} = this.props;
|
} = this.props;
|
||||||
const apiStates = this.selectors.selectApiStates(selectorsSource);
|
const apiStates = this.selectors.selectApiStates(selectorsSource);
|
||||||
const allSortedQueries = this.selectors.selectAllSortedQueries(
|
const allVisibleQueries = this.selectors.selectAllVisbileQueries(
|
||||||
selectorsSource
|
selectorsSource
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -112,7 +120,7 @@ class RtkQueryInspector<S, A extends Action<unknown>> extends Component<
|
||||||
|
|
||||||
console.log('inspector', {
|
console.log('inspector', {
|
||||||
apiStates,
|
apiStates,
|
||||||
allSortedQueries,
|
allVisibleQueries,
|
||||||
selectorsSource,
|
selectorsSource,
|
||||||
currentQueryInfo,
|
currentQueryInfo,
|
||||||
});
|
});
|
||||||
|
@ -129,14 +137,12 @@ class RtkQueryInspector<S, A extends Action<unknown>> extends Component<
|
||||||
>
|
>
|
||||||
<QueryForm
|
<QueryForm
|
||||||
dispatch={this.props.dispatch}
|
dispatch={this.props.dispatch}
|
||||||
queryComparator={selectorsSource.monitorState.queryComparator}
|
values={selectorsSource.monitorState.queryForm.values}
|
||||||
isAscendingQueryComparatorOrder={
|
onFormValuesChange={this.handleQueryFormValuesChange}
|
||||||
selectorsSource.monitorState.isAscendingQueryComparatorOrder
|
|
||||||
}
|
|
||||||
/>
|
/>
|
||||||
<QueryList
|
<QueryList
|
||||||
onSelectQuery={this.handleSelectQuery}
|
onSelectQuery={this.handleSelectQuery}
|
||||||
queryInfos={allSortedQueries}
|
queryInfos={allVisibleQueries}
|
||||||
selectedQueryKey={selectorsSource.monitorState.selectedQueryKey}
|
selectedQueryKey={selectorsSource.monitorState.selectedQueryKey}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,19 +1,20 @@
|
||||||
import React, { ReactNode, FormEvent, MouseEvent } from 'react';
|
import React, { ReactNode, FormEvent, MouseEvent, ChangeEvent } from 'react';
|
||||||
import { RtkQueryInspectorMonitorState } from '../types';
|
import { QueryFormValues } from '../types';
|
||||||
import { StyleUtilsContext } from '../styles/createStylingFromTheme';
|
import { StyleUtilsContext } from '../styles/createStylingFromTheme';
|
||||||
import { Select } from 'devui';
|
import { Select } from 'devui';
|
||||||
|
import { SelectOption } from '../types';
|
||||||
import { AnyAction } from 'redux';
|
import { AnyAction } from 'redux';
|
||||||
|
import debounce from 'lodash.debounce';
|
||||||
import { sortQueryOptions, QueryComparators } from '../utils/comparators';
|
import { sortQueryOptions, QueryComparators } from '../utils/comparators';
|
||||||
import {
|
|
||||||
changeIsAscendingQueryComparatorOrder,
|
export interface QueryFormProps {
|
||||||
changeQueryComparator,
|
|
||||||
} from '../reducers';
|
|
||||||
export interface QueryFormProps
|
|
||||||
extends Pick<
|
|
||||||
RtkQueryInspectorMonitorState,
|
|
||||||
'isAscendingQueryComparatorOrder' | 'queryComparator'
|
|
||||||
> {
|
|
||||||
dispatch: (action: AnyAction) => void;
|
dispatch: (action: AnyAction) => void;
|
||||||
|
values: QueryFormValues;
|
||||||
|
onFormValuesChange: (values: Partial<QueryFormValues>) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface QueryFormState {
|
||||||
|
searchValue: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const ascId = 'rtk-query-rb-asc';
|
const ascId = 'rtk-query-rb-asc';
|
||||||
|
@ -23,37 +24,71 @@ const searchId = 'rtk-query-search-query';
|
||||||
|
|
||||||
const searchPlaceholder = 'filter query...';
|
const searchPlaceholder = 'filter query...';
|
||||||
|
|
||||||
export class QueryForm extends React.PureComponent<QueryFormProps> {
|
export class QueryForm extends React.PureComponent<
|
||||||
|
QueryFormProps,
|
||||||
|
QueryFormState
|
||||||
|
> {
|
||||||
|
constructor(props: QueryFormProps) {
|
||||||
|
super(props);
|
||||||
|
|
||||||
|
this.state = {
|
||||||
|
searchValue: props.values.searchValue,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
inputSearchRef = React.createRef<HTMLInputElement>();
|
||||||
|
|
||||||
handleSubmit = (evt: FormEvent<HTMLFormElement>): void => {
|
handleSubmit = (evt: FormEvent<HTMLFormElement>): void => {
|
||||||
evt.preventDefault();
|
evt.preventDefault();
|
||||||
};
|
};
|
||||||
|
|
||||||
handleButtonGroupClick = ({ target }: MouseEvent<HTMLElement>): void => {
|
handleButtonGroupClick = ({ target }: MouseEvent<HTMLElement>): void => {
|
||||||
const { isAscendingQueryComparatorOrder: isAsc, dispatch } = this.props;
|
const {
|
||||||
|
values: { isAscendingQueryComparatorOrder: isAsc },
|
||||||
|
onFormValuesChange,
|
||||||
|
} = this.props;
|
||||||
|
|
||||||
const targetId = (target as HTMLElement)?.id ?? null;
|
const targetId = (target as HTMLElement)?.id ?? null;
|
||||||
|
|
||||||
if (targetId === ascId && !isAsc) {
|
if (targetId === ascId && !isAsc) {
|
||||||
dispatch(changeIsAscendingQueryComparatorOrder(true));
|
onFormValuesChange({ isAscendingQueryComparatorOrder: true });
|
||||||
} else if (targetId === descId && isAsc) {
|
} else if (targetId === descId && isAsc) {
|
||||||
this.props.dispatch(changeIsAscendingQueryComparatorOrder(false));
|
onFormValuesChange({ isAscendingQueryComparatorOrder: false });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
handleSelectComparator = (option: { value: string }): void => {
|
handleSelectComparator = (
|
||||||
const { dispatch } = this.props;
|
option: SelectOption<QueryComparators> | undefined | null
|
||||||
|
): void => {
|
||||||
if (typeof option?.value === 'string') {
|
if (typeof option?.value === 'string') {
|
||||||
dispatch(changeQueryComparator(option.value as QueryComparators));
|
this.props.onFormValuesChange({ queryComparator: option.value });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
getSelectedOption = (option: { value: string }): string => option?.value;
|
restoreCaretPosition = (start: number | null, end: number | null): void => {
|
||||||
|
window.requestAnimationFrame(() => {
|
||||||
|
if (this.inputSearchRef.current) {
|
||||||
|
this.inputSearchRef.current.selectionStart = start;
|
||||||
|
this.inputSearchRef.current.selectionEnd = end;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
invalidateSearchValueFromProps = debounce(() => {
|
||||||
|
this.props.onFormValuesChange({
|
||||||
|
searchValue: this.state.searchValue,
|
||||||
|
});
|
||||||
|
}, 150);
|
||||||
|
|
||||||
|
handleSearchChange = (evt: ChangeEvent<HTMLInputElement>): void => {
|
||||||
|
const searchValue = evt.target.value.trim();
|
||||||
|
this.setState({ searchValue });
|
||||||
|
this.invalidateSearchValueFromProps();
|
||||||
|
};
|
||||||
|
|
||||||
render(): ReactNode {
|
render(): ReactNode {
|
||||||
const {
|
const {
|
||||||
isAscendingQueryComparatorOrder: isAsc,
|
values: { isAscendingQueryComparatorOrder: isAsc, queryComparator },
|
||||||
queryComparator,
|
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
const isDesc = !isAsc;
|
const isDesc = !isAsc;
|
||||||
|
@ -72,24 +107,25 @@ export class QueryForm extends React.PureComponent<QueryFormProps> {
|
||||||
filter query
|
filter query
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
|
ref={this.inputSearchRef}
|
||||||
type="search"
|
type="search"
|
||||||
|
value={this.state.searchValue}
|
||||||
|
onChange={this.handleSearchChange}
|
||||||
placeholder={searchPlaceholder}
|
placeholder={searchPlaceholder}
|
||||||
{...styling('querySearch')}
|
{...styling('querySearch')}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div {...styling('sortBySection')}>
|
<div {...styling('sortBySection')}>
|
||||||
<label htmlFor={selectId}>Sort by</label>
|
<label htmlFor={selectId}>Sort by</label>
|
||||||
<Select
|
<Select<SelectOption<QueryComparators>>
|
||||||
id={selectId}
|
id={selectId}
|
||||||
isSearchable={false}
|
isSearchable={false}
|
||||||
openOuterUp
|
theme={base16Theme as any}
|
||||||
theme={base16Theme}
|
|
||||||
value={sortQueryOptions.find(
|
value={sortQueryOptions.find(
|
||||||
(opt) => opt?.value === queryComparator
|
(opt) => opt?.value === queryComparator
|
||||||
)}
|
)}
|
||||||
options={sortQueryOptions}
|
options={sortQueryOptions}
|
||||||
onChange={this.handleSelectComparator}
|
onChange={this.handleSelectComparator}
|
||||||
selectOption={this.getSelectedOption}
|
|
||||||
/>
|
/>
|
||||||
<div
|
<div
|
||||||
tabIndex={0}
|
tabIndex={0}
|
||||||
|
|
|
@ -68,7 +68,7 @@ export class QueryPreview extends React.PureComponent<QueryPreviewProps> {
|
||||||
<li>{`loaded at: ${latestFetch}`}</li>
|
<li>{`loaded at: ${latestFetch}`}</li>
|
||||||
<li>{`requested at: ${startedAt}`}</li>
|
<li>{`requested at: ${startedAt}`}</li>
|
||||||
</ul>
|
</ul>
|
||||||
<div style={{ padding: '1em' }}>
|
<div {...styling('treeWrapper')}>
|
||||||
<JSONTree
|
<JSONTree
|
||||||
data={data}
|
data={data}
|
||||||
labelRenderer={this.labelRenderer}
|
labelRenderer={this.labelRenderer}
|
||||||
|
|
|
@ -1,12 +1,21 @@
|
||||||
import { Action, AnyAction } from 'redux';
|
import { Action, AnyAction } from 'redux';
|
||||||
import { createSlice, PayloadAction } from '@reduxjs/toolkit';
|
import { createSlice, PayloadAction } from '@reduxjs/toolkit';
|
||||||
import { RtkQueryInspectorProps } from './RtkQueryInspector';
|
import { RtkQueryInspectorProps } from './RtkQueryInspector';
|
||||||
import { QueryInfo, RtkQueryInspectorMonitorState } from './types';
|
import {
|
||||||
|
QueryInfo,
|
||||||
|
RtkQueryInspectorMonitorState,
|
||||||
|
QueryFormValues,
|
||||||
|
} from './types';
|
||||||
import { QueryComparators } from './utils/comparators';
|
import { QueryComparators } from './utils/comparators';
|
||||||
|
|
||||||
const initialState: RtkQueryInspectorMonitorState = {
|
const initialState: RtkQueryInspectorMonitorState = {
|
||||||
queryComparator: QueryComparators.fulfilledTimeStamp,
|
queryForm: {
|
||||||
isAscendingQueryComparatorOrder: false,
|
values: {
|
||||||
|
queryComparator: QueryComparators.fulfilledTimeStamp,
|
||||||
|
isAscendingQueryComparatorOrder: false,
|
||||||
|
searchValue: '',
|
||||||
|
},
|
||||||
|
},
|
||||||
selectedQueryKey: null,
|
selectedQueryKey: null,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -14,14 +23,11 @@ const monitorSlice = createSlice({
|
||||||
name: 'rtk-query-monitor',
|
name: 'rtk-query-monitor',
|
||||||
initialState,
|
initialState,
|
||||||
reducers: {
|
reducers: {
|
||||||
changeQueryComparator(state, action: PayloadAction<QueryComparators>) {
|
changeQueryFormValues(
|
||||||
state.queryComparator = action.payload;
|
|
||||||
},
|
|
||||||
changeIsAscendingQueryComparatorOrder(
|
|
||||||
state,
|
state,
|
||||||
action: PayloadAction<boolean>
|
action: PayloadAction<Partial<QueryFormValues>>
|
||||||
) {
|
) {
|
||||||
state.isAscendingQueryComparatorOrder = !!action.payload;
|
state.queryForm.values = { ...state.queryForm.values, ...action.payload };
|
||||||
},
|
},
|
||||||
selectQueryKey(
|
selectQueryKey(
|
||||||
state,
|
state,
|
||||||
|
@ -45,8 +51,4 @@ export default function reducer<S, A extends Action<unknown>>(
|
||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const {
|
export const { selectQueryKey, changeQueryFormValues } = monitorSlice.actions;
|
||||||
changeIsAscendingQueryComparatorOrder,
|
|
||||||
changeQueryComparator,
|
|
||||||
selectQueryKey,
|
|
||||||
} = monitorSlice.actions;
|
|
||||||
|
|
|
@ -1,18 +1,14 @@
|
||||||
import { Action, createSelector, Selector } from '@reduxjs/toolkit';
|
import { Action, createSelector, Selector } from '@reduxjs/toolkit';
|
||||||
import { RtkQueryInspectorProps } from './RtkQueryInspector';
|
import { RtkQueryInspectorProps } from './RtkQueryInspector';
|
||||||
import { QueryInfo, RtkQueryInspectorMonitorState } from './types';
|
import { QueryInfo, SelectorsSource } from './types';
|
||||||
import { Comparator, queryComparators } from './utils/comparators';
|
import { Comparator, queryComparators } from './utils/comparators';
|
||||||
|
import { escapeRegExpSpecialCharacter } from './utils/regexp';
|
||||||
import {
|
import {
|
||||||
getApiStatesOf,
|
getApiStatesOf,
|
||||||
extractAllApiQueries,
|
extractAllApiQueries,
|
||||||
flipComparator,
|
flipComparator,
|
||||||
} from './utils/rtk-query';
|
} from './utils/rtk-query';
|
||||||
|
|
||||||
type SelectorsSource<S> = {
|
|
||||||
currentState: S | null;
|
|
||||||
monitorState: RtkQueryInspectorMonitorState;
|
|
||||||
};
|
|
||||||
|
|
||||||
type InspectorSelector<S, Output> = Selector<SelectorsSource<S>, Output>;
|
type InspectorSelector<S, Output> = Selector<SelectorsSource<S>, Output>;
|
||||||
|
|
||||||
export function computeSelectorSource<S, A extends Action<unknown>>(
|
export function computeSelectorSource<S, A extends Action<unknown>>(
|
||||||
|
@ -21,16 +17,16 @@ export function computeSelectorSource<S, A extends Action<unknown>>(
|
||||||
): SelectorsSource<S> {
|
): SelectorsSource<S> {
|
||||||
const { computedStates, currentStateIndex, monitorState } = props;
|
const { computedStates, currentStateIndex, monitorState } = props;
|
||||||
|
|
||||||
const currentState =
|
const userState =
|
||||||
computedStates.length > 0 ? computedStates[currentStateIndex].state : null;
|
computedStates.length > 0 ? computedStates[currentStateIndex].state : null;
|
||||||
|
|
||||||
if (
|
if (
|
||||||
!previous ||
|
!previous ||
|
||||||
previous.currentState !== currentState ||
|
previous.userState !== userState ||
|
||||||
previous.monitorState !== monitorState
|
previous.monitorState !== monitorState
|
||||||
) {
|
) {
|
||||||
return {
|
return {
|
||||||
currentState,
|
userState,
|
||||||
monitorState,
|
monitorState,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -48,19 +44,20 @@ export interface InspectorSelectors<S> {
|
||||||
S,
|
S,
|
||||||
ReturnType<typeof extractAllApiQueries>
|
ReturnType<typeof extractAllApiQueries>
|
||||||
>;
|
>;
|
||||||
readonly selectAllSortedQueries: InspectorSelector<S, QueryInfo[]>;
|
readonly selectAllVisbileQueries: InspectorSelector<S, QueryInfo[]>;
|
||||||
readonly selectorCurrentQueryInfo: InspectorSelector<S, QueryInfo | null>;
|
readonly selectorCurrentQueryInfo: InspectorSelector<S, QueryInfo | null>;
|
||||||
|
readonly selectSearchQueryRegex: InspectorSelector<S, RegExp | null>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function createInspectorSelectors<S>(): InspectorSelectors<S> {
|
export function createInspectorSelectors<S>(): InspectorSelectors<S> {
|
||||||
const selectQueryComparator = ({
|
const selectQueryComparator = ({
|
||||||
monitorState,
|
monitorState,
|
||||||
}: SelectorsSource<S>): Comparator<QueryInfo> => {
|
}: SelectorsSource<S>): Comparator<QueryInfo> => {
|
||||||
return queryComparators[monitorState.queryComparator];
|
return queryComparators[monitorState.queryForm.values.queryComparator];
|
||||||
};
|
};
|
||||||
|
|
||||||
const selectApiStates = createSelector(
|
const selectApiStates = createSelector(
|
||||||
({ currentState }: SelectorsSource<S>) => currentState,
|
({ userState }: SelectorsSource<S>) => userState,
|
||||||
getApiStatesOf
|
getApiStatesOf
|
||||||
);
|
);
|
||||||
const selectAllQueries = createSelector(
|
const selectAllQueries = createSelector(
|
||||||
|
@ -68,21 +65,35 @@ export function createInspectorSelectors<S>(): InspectorSelectors<S> {
|
||||||
extractAllApiQueries
|
extractAllApiQueries
|
||||||
);
|
);
|
||||||
|
|
||||||
const selectAllSortedQueries = createSelector(
|
const selectSearchQueryRegex = createSelector(
|
||||||
|
({ monitorState }: SelectorsSource<S>) =>
|
||||||
|
monitorState.queryForm.values.searchValue,
|
||||||
|
(searchValue) => {
|
||||||
|
if (searchValue.length >= 3) {
|
||||||
|
return new RegExp(escapeRegExpSpecialCharacter(searchValue), 'i');
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
const selectAllVisbileQueries = createSelector(
|
||||||
[
|
[
|
||||||
selectQueryComparator,
|
selectQueryComparator,
|
||||||
selectAllQueries,
|
selectAllQueries,
|
||||||
({ monitorState }: SelectorsSource<S>) =>
|
({ monitorState }: SelectorsSource<S>) =>
|
||||||
monitorState.isAscendingQueryComparatorOrder,
|
monitorState.queryForm.values.isAscendingQueryComparatorOrder,
|
||||||
|
selectSearchQueryRegex,
|
||||||
],
|
],
|
||||||
(comparator, queryList, isAscending) => {
|
(comparator, queryList, isAscending, searchRegex) => {
|
||||||
console.log({ comparator, queryList, isAscending });
|
const filteredList = searchRegex
|
||||||
|
? queryList.filter((queryInfo) => searchRegex.test(queryInfo.queryKey))
|
||||||
|
: queryList.slice();
|
||||||
|
|
||||||
const computedComparator = isAscending
|
const computedComparator = isAscending
|
||||||
? comparator
|
? comparator
|
||||||
: flipComparator(comparator);
|
: flipComparator(comparator);
|
||||||
|
|
||||||
return queryList.slice().sort(computedComparator);
|
return filteredList.sort(computedComparator);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -109,7 +120,8 @@ export function createInspectorSelectors<S>(): InspectorSelectors<S> {
|
||||||
selectQueryComparator,
|
selectQueryComparator,
|
||||||
selectApiStates,
|
selectApiStates,
|
||||||
selectAllQueries,
|
selectAllQueries,
|
||||||
selectAllSortedQueries,
|
selectAllVisbileQueries,
|
||||||
|
selectSearchQueryRegex,
|
||||||
selectorCurrentQueryInfo,
|
selectorCurrentQueryInfo,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -52,6 +52,7 @@ const getSheetFromColorMap = (map: ColorMap) => ({
|
||||||
inspector: {
|
inspector: {
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
flexFlow: 'column nowrap',
|
flexFlow: 'column nowrap',
|
||||||
|
overflow: 'hidden',
|
||||||
width: '100%',
|
width: '100%',
|
||||||
height: '100%',
|
height: '100%',
|
||||||
'font-family': 'monaco, Consolas, "Lucida Console", monospace',
|
'font-family': 'monaco, Consolas, "Lucida Console", monospace',
|
||||||
|
@ -253,6 +254,8 @@ const getSheetFromColorMap = (map: ColorMap) => ({
|
||||||
|
|
||||||
queryPreview: {
|
queryPreview: {
|
||||||
flex: '1 1 50%',
|
flex: '1 1 50%',
|
||||||
|
overflowX: 'hidden',
|
||||||
|
oveflowY: 'auto',
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
'flex-direction': 'column',
|
'flex-direction': 'column',
|
||||||
'overflow-y': 'hidden',
|
'overflow-y': 'hidden',
|
||||||
|
@ -291,6 +294,12 @@ const getSheetFromColorMap = (map: ColorMap) => ({
|
||||||
treeItemKey: {
|
treeItemKey: {
|
||||||
color: map.TEXT_PLACEHOLDER_COLOR,
|
color: map.TEXT_PLACEHOLDER_COLOR,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
treeWrapper: {
|
||||||
|
overflowX: 'auto',
|
||||||
|
overflowY: 'auto',
|
||||||
|
padding: '1em',
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
let themeSheet: StyleSheet;
|
let themeSheet: StyleSheet;
|
||||||
|
|
|
@ -6,10 +6,16 @@ import { Action } from 'redux';
|
||||||
import * as themes from 'redux-devtools-themes';
|
import * as themes from 'redux-devtools-themes';
|
||||||
import { QueryComparators } from './utils/comparators';
|
import { QueryComparators } from './utils/comparators';
|
||||||
|
|
||||||
export interface RtkQueryInspectorMonitorState {
|
export interface QueryFormValues {
|
||||||
queryComparator: QueryComparators;
|
queryComparator: QueryComparators;
|
||||||
isAscendingQueryComparatorOrder: boolean;
|
isAscendingQueryComparatorOrder: boolean;
|
||||||
selectedQueryKey: Pick<QueryInfo, 'reducerPath' | 'queryKey'> | null;
|
searchValue: string;
|
||||||
|
}
|
||||||
|
export interface RtkQueryInspectorMonitorState {
|
||||||
|
readonly queryForm: {
|
||||||
|
values: QueryFormValues;
|
||||||
|
};
|
||||||
|
readonly selectedQueryKey: Pick<QueryInfo, 'reducerPath' | 'queryKey'> | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface RtkQueryInspectorMonitorProps<S, A extends Action<unknown>>
|
export interface RtkQueryInspectorMonitorProps<S, A extends Action<unknown>>
|
||||||
|
@ -63,3 +69,13 @@ export interface ApiInfo {
|
||||||
reducerPath: string;
|
reducerPath: string;
|
||||||
apiState: RtkQueryApiState;
|
apiState: RtkQueryApiState;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface SelectOption<T = string> {
|
||||||
|
label: string;
|
||||||
|
value: T;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SelectorsSource<S> {
|
||||||
|
userState: S | null;
|
||||||
|
monitorState: RtkQueryInspectorMonitorState;
|
||||||
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { QueryStatus } from '@reduxjs/toolkit/dist/query';
|
import { QueryStatus } from '@reduxjs/toolkit/dist/query';
|
||||||
import { QueryInfo } from '../types';
|
import { QueryInfo, SelectOption } from '../types';
|
||||||
|
|
||||||
export interface Comparator<T> {
|
export interface Comparator<T> {
|
||||||
(a: T, b: T): number;
|
(a: T, b: T): number;
|
||||||
|
@ -12,7 +12,7 @@ export enum QueryComparators {
|
||||||
endpointName = 'endpointName',
|
endpointName = 'endpointName',
|
||||||
}
|
}
|
||||||
|
|
||||||
export const sortQueryOptions: { label: string; value: string }[] = [
|
export const sortQueryOptions: SelectOption<QueryComparators>[] = [
|
||||||
{ label: 'fulfilledTimeStamp', value: QueryComparators.fulfilledTimeStamp },
|
{ label: 'fulfilledTimeStamp', value: QueryComparators.fulfilledTimeStamp },
|
||||||
{ label: 'query key', value: QueryComparators.queryKey },
|
{ label: 'query key', value: QueryComparators.queryKey },
|
||||||
{ label: 'status ', value: QueryComparators.status },
|
{ label: 'status ', value: QueryComparators.status },
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
// https://stackoverflow.com/a/9310752
|
||||||
|
export function escapeRegExpSpecialCharacter(text: string): string {
|
||||||
|
return text.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&');
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user