mirror of
https://github.com/reduxjs/redux-devtools.git
synced 2025-07-25 15:40:06 +03:00
feat(rtk-query): add regex search
This commit is contained in:
parent
6a87019cb7
commit
95da893616
|
@ -7,9 +7,11 @@ import debounce from 'lodash.debounce';
|
|||
import { sortQueryOptions, QueryComparators } from '../utils/comparators';
|
||||
import { QueryFilters, filterQueryOptions } from '../utils/filters';
|
||||
import { SortOrderButton } from './SortOrderButton';
|
||||
import { RegexIcon } from './RegexIcon';
|
||||
|
||||
export interface QueryFormProps {
|
||||
values: QueryFormValues;
|
||||
searchQueryRegex: RegExp | null;
|
||||
onFormValuesChange: (values: Partial<QueryFormValues>) => void;
|
||||
}
|
||||
|
||||
|
@ -22,6 +24,13 @@ const searchId = 'rtk-query-search-query';
|
|||
const filterSelectId = 'rtk-query-search-query-select';
|
||||
const searchPlaceholder = 'filter query by...';
|
||||
|
||||
const labels = {
|
||||
regexToggle: {
|
||||
info: 'Use regular expression search',
|
||||
error: 'Invalid regular expression provided',
|
||||
},
|
||||
};
|
||||
|
||||
export class QueryForm extends React.PureComponent<
|
||||
QueryFormProps,
|
||||
QueryFormState
|
||||
|
@ -60,6 +69,12 @@ export class QueryForm extends React.PureComponent<
|
|||
}
|
||||
};
|
||||
|
||||
handleRegexSearchClick = (): void => {
|
||||
this.props.onFormValuesChange({
|
||||
isRegexSearch: !this.props.values.isRegexSearch,
|
||||
});
|
||||
};
|
||||
|
||||
restoreCaretPosition = (start: number | null, end: number | null): void => {
|
||||
window.requestAnimationFrame(() => {
|
||||
if (this.inputSearchRef.current) {
|
||||
|
@ -92,13 +107,21 @@ export class QueryForm extends React.PureComponent<
|
|||
|
||||
render(): ReactNode {
|
||||
const {
|
||||
searchQueryRegex,
|
||||
values: {
|
||||
isAscendingQueryComparatorOrder: isAsc,
|
||||
queryComparator,
|
||||
searchValue,
|
||||
queryFilter,
|
||||
isRegexSearch,
|
||||
},
|
||||
} = this.props;
|
||||
|
||||
const isRegexInvalid =
|
||||
isRegexSearch && searchValue.length > 0 && searchQueryRegex == null;
|
||||
const regexToggleType = isRegexInvalid ? 'error' : 'info';
|
||||
const regexToggleLabel = labels.regexToggle[regexToggleType];
|
||||
|
||||
return (
|
||||
<StyleUtilsContext.Consumer>
|
||||
{({ styling, base16Theme }) => {
|
||||
|
@ -129,6 +152,17 @@ export class QueryForm extends React.PureComponent<
|
|||
onClick={this.handleClearSearchClick}
|
||||
{...styling('closeButton')}
|
||||
/>
|
||||
<button
|
||||
type="button"
|
||||
aria-label={regexToggleLabel}
|
||||
title={regexToggleLabel}
|
||||
data-type={regexToggleType}
|
||||
aria-pressed={isRegexSearch}
|
||||
onClick={this.handleRegexSearchClick}
|
||||
{...styling('toggleButton')}
|
||||
>
|
||||
<RegexIcon />
|
||||
</button>
|
||||
</div>
|
||||
<label htmlFor={selectId} {...styling('srOnly')}>
|
||||
filter by
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
import * as React from 'react';
|
||||
|
||||
export type RegexIconProps = Omit<
|
||||
React.HTMLAttributes<SVGElement>,
|
||||
'viewBox' | 'children'
|
||||
>;
|
||||
|
||||
// `OOjs_UI_icon_regular-expression.svg` (MIT License)
|
||||
// from https://commons.wikimedia.org/wiki/File:OOjs_UI_icon_regular-expression.svg
|
||||
export function RegexIcon(
|
||||
props: React.HTMLAttributes<SVGElement>
|
||||
): JSX.Element {
|
||||
return (
|
||||
<svg fill="currentColor" {...props} viewBox="0 0 24 24">
|
||||
<g>
|
||||
<path d="M3 12.045c0-.99.15-1.915.45-2.777A6.886 6.886 0 0 1 4.764 7H6.23a7.923 7.923 0 0 0-1.25 2.374 8.563 8.563 0 0 0 .007 5.314c.29.85.7 1.622 1.23 2.312h-1.45a6.53 6.53 0 0 1-1.314-2.223 8.126 8.126 0 0 1-.45-2.732" />
|
||||
<path id="dot" d="M10 16a1 1 0 1 1-2 0 1 1 0 0 1 2 0z" />
|
||||
<path d="M14.25 7.013l-.24 2.156 2.187-.61.193 1.47-1.992.14 1.307 1.74-1.33.71-.914-1.833-.8 1.822-1.38-.698 1.296-1.74-1.98-.152.23-1.464 2.14.61-.24-2.158h1.534" />
|
||||
<path d="M21 12.045c0 .982-.152 1.896-.457 2.744A6.51 6.51 0 0 1 19.236 17h-1.453a8.017 8.017 0 0 0 1.225-2.31c.29-.855.434-1.74.434-2.66 0-.91-.14-1.797-.422-2.66a7.913 7.913 0 0 0-1.248-2.374h1.465a6.764 6.764 0 0 1 1.313 2.28c.3.86.45 1.782.45 2.764" />
|
||||
</g>
|
||||
</svg>
|
||||
);
|
||||
}
|
|
@ -124,6 +124,9 @@ class RtkQueryInspector<S, A extends Action<unknown>> extends PureComponent<
|
|||
|
||||
const hasNoApi = apiStates == null;
|
||||
|
||||
const searchQueryRegex =
|
||||
this.selectors.selectSearchQueryRegex(selectorsSource);
|
||||
|
||||
return (
|
||||
<div
|
||||
ref={this.inspectorRef}
|
||||
|
@ -135,6 +138,7 @@ class RtkQueryInspector<S, A extends Action<unknown>> extends PureComponent<
|
|||
data-wide-layout={+this.state.isWideLayout}
|
||||
>
|
||||
<QueryForm
|
||||
searchQueryRegex={searchQueryRegex}
|
||||
values={selectorsSource.monitorState.queryForm.values}
|
||||
onFormValuesChange={this.handleQueryFormValuesChange}
|
||||
/>
|
||||
|
|
|
@ -16,6 +16,7 @@ const initialState: RtkQueryMonitorState = {
|
|||
queryComparator: QueryComparators.fulfilledTimeStamp,
|
||||
isAscendingQueryComparatorOrder: false,
|
||||
searchValue: '',
|
||||
isRegexSearch: false,
|
||||
queryFilter: QueryFilters.queryKey,
|
||||
},
|
||||
},
|
||||
|
|
|
@ -111,10 +111,21 @@ export function createInspectorSelectors<S>(): InspectorSelectors<S> {
|
|||
const selectSearchQueryRegex = createSelector(
|
||||
({ monitorState }: SelectorsSource<S>) =>
|
||||
monitorState.queryForm.values.searchValue,
|
||||
(searchValue) => {
|
||||
if (searchValue.length >= 3) {
|
||||
return new RegExp(escapeRegExpSpecialCharacter(searchValue), 'i');
|
||||
({ monitorState }: SelectorsSource<S>) =>
|
||||
monitorState.queryForm.values.isRegexSearch,
|
||||
(searchValue, isRegexSearch) => {
|
||||
if (searchValue) {
|
||||
try {
|
||||
const regexPattern = isRegexSearch
|
||||
? searchValue
|
||||
: escapeRegExpSpecialCharacter(searchValue);
|
||||
|
||||
return new RegExp(regexPattern, 'i');
|
||||
} catch (err) {
|
||||
// We notify that the search regex provided is not valid
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
);
|
||||
|
|
|
@ -43,6 +43,9 @@ export const colorMap = (theme: reduxThemes.Base16Theme) =>
|
|||
ULIST_COLOR: rgba(theme.base06, 60),
|
||||
ULIST_STRONG_COLOR: theme.base0B,
|
||||
TAB_CONTENT_COLOR: rgba(theme.base06, 60),
|
||||
TOGGLE_BUTTON_BACKGROUND: rgba(theme.base00, 70),
|
||||
TOGGLE_BUTTON_SELECTED_BACKGROUND: theme.base04,
|
||||
TOGGLE_BUTTON_ERROR: rgba(theme.base08, 40),
|
||||
} as const);
|
||||
|
||||
type Color = keyof ReturnType<typeof colorMap>;
|
||||
|
@ -264,6 +267,37 @@ const getSheetFromColorMap = (map: ColorMap) => {
|
|||
},
|
||||
},
|
||||
|
||||
toggleButton: {
|
||||
width: '24px',
|
||||
height: '24px',
|
||||
display: 'inline-block',
|
||||
flex: '0 0 auto',
|
||||
color: map.TEXT_PLACEHOLDER_COLOR,
|
||||
cursor: 'pointer',
|
||||
padding: 0,
|
||||
fontSize: '0.7em',
|
||||
letterSpacing: '-0.7px',
|
||||
outline: 'none',
|
||||
boxShadow: 'none',
|
||||
fontWeight: '700',
|
||||
border: 'none',
|
||||
|
||||
'&:hover': {
|
||||
color: map.TEXT_COLOR,
|
||||
},
|
||||
|
||||
backgroundColor: 'transparent',
|
||||
'&[aria-pressed="true"]': {
|
||||
color: map.BACKGROUND_COLOR,
|
||||
backgroundColor: map.TEXT_COLOR,
|
||||
},
|
||||
|
||||
'&[data-type="error"]': {
|
||||
color: map.TEXT_COLOR,
|
||||
backgroundColor: map.TOGGLE_BUTTON_ERROR,
|
||||
},
|
||||
},
|
||||
|
||||
queryForm: {
|
||||
display: 'flex',
|
||||
flexFlow: 'column nowrap',
|
||||
|
@ -323,6 +357,7 @@ const getSheetFromColorMap = (map: ColorMap) => {
|
|||
outline: 'none',
|
||||
boxShadow: 'none',
|
||||
display: 'block',
|
||||
flex: '0 0 auto',
|
||||
cursor: 'pointer',
|
||||
background: 'transparent',
|
||||
position: 'relative',
|
||||
|
@ -334,10 +369,13 @@ const getSheetFromColorMap = (map: ColorMap) => {
|
|||
content: '"\u00d7"',
|
||||
display: 'block',
|
||||
padding: 4,
|
||||
fontSize: '16px',
|
||||
color: map.TEXT_COLOR,
|
||||
fontSize: '1.2em',
|
||||
color: map.TEXT_PLACEHOLDER_COLOR,
|
||||
background: 'transparent',
|
||||
},
|
||||
'&:hover::after': {
|
||||
color: map.TEXT_COLOR,
|
||||
},
|
||||
},
|
||||
|
||||
noApiFound: {
|
||||
|
|
|
@ -19,6 +19,7 @@ export interface QueryFormValues {
|
|||
queryComparator: QueryComparators;
|
||||
isAscendingQueryComparatorOrder: boolean;
|
||||
searchValue: string;
|
||||
isRegexSearch: boolean;
|
||||
queryFilter: QueryFilters;
|
||||
}
|
||||
export interface RtkQueryMonitorState {
|
||||
|
|
Loading…
Reference in New Issue
Block a user