Added filter preference types and plugged into renderer partly. Lots of work to do still.
This commit is contained in:
parent
9db209ee64
commit
ee1b38e4db
|
@ -14,13 +14,10 @@ import {
|
|||
ElementTypeProfiler,
|
||||
ElementTypeRoot,
|
||||
ElementTypeSuspense,
|
||||
FilterByElementType,
|
||||
FilterByName,
|
||||
FilterByPath,
|
||||
} from 'src/types';
|
||||
import {
|
||||
getDisplayName,
|
||||
getSavedFilters,
|
||||
getSavedFilterPreferences,
|
||||
getUID,
|
||||
utfEncodeString,
|
||||
} from 'src/utils';
|
||||
|
@ -50,7 +47,6 @@ import type {
|
|||
ReactRenderer,
|
||||
RendererInterface,
|
||||
} from './types';
|
||||
import type { ElementType, Filter } from 'src/types';
|
||||
import type { InspectedElement } from 'src/devtools/views/Components/types';
|
||||
|
||||
function getInternalReactConstants(version) {
|
||||
|
@ -268,35 +264,11 @@ export function attach(
|
|||
}
|
||||
};
|
||||
|
||||
const filterByElementTypeMap: Map<ElementType, boolean> = new Map();
|
||||
const filterByNames: Set<RegExp> = new Set();
|
||||
const filterByPaths: Set<RegExp> = new Set();
|
||||
|
||||
function updateFilters(filters: Array<Filter>): void {
|
||||
filterByElementTypeMap.clear();
|
||||
filterByNames.clear();
|
||||
filterByPaths.clear();
|
||||
|
||||
filters.forEach(({ type, value }) => {
|
||||
switch (type) {
|
||||
case FilterByElementType:
|
||||
filterByElementTypeMap.set(((value: any): ElementType), true);
|
||||
break;
|
||||
case FilterByName:
|
||||
filterByNames.add(((value: any): RegExp));
|
||||
break;
|
||||
case FilterByPath:
|
||||
filterByPaths.add(((value: any): RegExp));
|
||||
break;
|
||||
default:
|
||||
console.error(`Unsupported filter type "${type}"`);
|
||||
break;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Initialize to the persisted values
|
||||
updateFilters(getSavedFilters());
|
||||
const {
|
||||
hideElementsWithTypes,
|
||||
// TOOD (filter) hideElementsWithDisplayNames,
|
||||
// TOOD (filter) hideElementsWithPaths,
|
||||
} = getSavedFilterPreferences();
|
||||
|
||||
// NOTICE Keep in sync with getDataForFiber()
|
||||
function shouldFilterFiber(fiber: Fiber): boolean {
|
||||
|
@ -307,21 +279,21 @@ export function attach(
|
|||
switch (tag) {
|
||||
case ClassComponent:
|
||||
case IncompleteClassComponent:
|
||||
return filterByElementTypeMap.get(ElementTypeClass) === true;
|
||||
return hideElementsWithTypes.has(ElementTypeClass);
|
||||
case FunctionComponent:
|
||||
return filterByElementTypeMap.get(ElementTypeFunction) === true;
|
||||
return hideElementsWithTypes.has(ElementTypeFunction);
|
||||
case IndeterminateComponent:
|
||||
return (
|
||||
filterByElementTypeMap.get(ElementTypeClass) === true ||
|
||||
filterByElementTypeMap.get(ElementTypeFunction) === true
|
||||
hideElementsWithTypes.has(ElementTypeClass) ||
|
||||
hideElementsWithTypes.has(ElementTypeFunction)
|
||||
);
|
||||
case ForwardRef:
|
||||
return filterByElementTypeMap.get(ElementTypeForwardRef) === true;
|
||||
return hideElementsWithTypes.has(ElementTypeForwardRef);
|
||||
case MemoComponent:
|
||||
case SimpleMemoComponent:
|
||||
return filterByElementTypeMap.get(ElementTypeMemo) === true;
|
||||
return hideElementsWithTypes.has(ElementTypeMemo);
|
||||
case HostComponent:
|
||||
return filterByElementTypeMap.get(ElementTypeHostComponent) === true;
|
||||
return hideElementsWithTypes.has(ElementTypeHostComponent);
|
||||
case HostRoot:
|
||||
return false; // We never support filtering roots
|
||||
case DehydratedSuspenseComponent:
|
||||
|
@ -350,14 +322,14 @@ export function attach(
|
|||
case CONTEXT_PROVIDER_SYMBOL_STRING:
|
||||
case CONTEXT_CONSUMER_NUMBER:
|
||||
case CONTEXT_CONSUMER_SYMBOL_STRING:
|
||||
return filterByElementTypeMap.get(ElementTypeContext) === true;
|
||||
return hideElementsWithTypes.has(ElementTypeContext);
|
||||
case SUSPENSE_NUMBER:
|
||||
case SUSPENSE_SYMBOL_STRING:
|
||||
case DEPRECATED_PLACEHOLDER_SYMBOL_STRING:
|
||||
return filterByElementTypeMap.get(ElementTypeSuspense) === true;
|
||||
return hideElementsWithTypes.has(ElementTypeSuspense);
|
||||
case PROFILER_NUMBER:
|
||||
case PROFILER_SYMBOL_STRING:
|
||||
return filterByElementTypeMap.get(ElementTypeProfiler) === true;
|
||||
return hideElementsWithTypes.has(ElementTypeProfiler);
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -5,7 +5,8 @@ export const TREE_OPERATION_REMOVE = 2;
|
|||
export const TREE_OPERATION_REORDER_CHILDREN = 3;
|
||||
export const TREE_OPERATION_UPDATE_TREE_BASE_DURATION = 4;
|
||||
|
||||
export const LOCAL_STORAGE_FILTERS_KEY = 'React::DevTools::filters';
|
||||
export const LOCAL_STORAGE_FILTER_PREFERENCES_KEY =
|
||||
'React::DevTools::filterPreferences';
|
||||
|
||||
export const LOCAL_STORAGE_RELOAD_AND_PROFILE_KEY =
|
||||
'React::DevTools::reloadAndProfile';
|
||||
|
|
|
@ -4,10 +4,12 @@ import React from 'react';
|
|||
import styles from './ButtonIcon.css';
|
||||
|
||||
export type IconType =
|
||||
| 'add'
|
||||
| 'cancel'
|
||||
| 'close'
|
||||
| 'collapsed'
|
||||
| 'copy'
|
||||
| 'delete'
|
||||
| 'down'
|
||||
| 'expanded'
|
||||
| 'export'
|
||||
|
@ -26,12 +28,16 @@ export type IconType =
|
|||
| 'view-source';
|
||||
|
||||
type Props = {|
|
||||
className?: string,
|
||||
type: IconType,
|
||||
|};
|
||||
|
||||
export default function ButtonIcon({ type }: Props) {
|
||||
export default function ButtonIcon({ className = '', type }: Props) {
|
||||
let pathData = null;
|
||||
switch (type) {
|
||||
case 'add':
|
||||
pathData = PATH_ADD;
|
||||
break;
|
||||
case 'cancel':
|
||||
pathData = PATH_CANCEL;
|
||||
break;
|
||||
|
@ -44,6 +50,9 @@ export default function ButtonIcon({ type }: Props) {
|
|||
case 'copy':
|
||||
pathData = PATH_COPY;
|
||||
break;
|
||||
case 'delete':
|
||||
pathData = PATH_DELETE;
|
||||
break;
|
||||
case 'down':
|
||||
pathData = PATH_DOWN;
|
||||
break;
|
||||
|
@ -100,7 +109,7 @@ export default function ButtonIcon({ type }: Props) {
|
|||
return (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
className={styles.ButtonIcon}
|
||||
className={`${styles.ButtonIcon} ${className}`}
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
|
@ -111,6 +120,8 @@ export default function ButtonIcon({ type }: Props) {
|
|||
);
|
||||
}
|
||||
|
||||
const PATH_ADD = 'M19 13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z';
|
||||
|
||||
const PATH_CANCEL = `
|
||||
M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zM4 12c0-4.42 3.58-8 8-8 1.85 0 3.55.63 4.9 1.69L5.69
|
||||
16.9C4.63 15.55 4 13.85 4 12zm8 8c-1.85 0-3.55-.63-4.9-1.69L18.31 7.1C19.37 8.45 20 10.15 20 12c0 4.42-3.58 8-8 8z
|
||||
|
@ -126,6 +137,11 @@ const PATH_COPY = `
|
|||
2v10a2 2 0 0 0 2 2h10c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm0 12H9V5h10v10zm-8 6h2v-2h-2v2zm-4 0h2v-2H7v2z
|
||||
`;
|
||||
|
||||
const PATH_DELETE = `
|
||||
M6 19c0 1.1.9 2 2 2h8c1.1 0 2-.9 2-2V7H6v12zm2.46-7.12l1.41-1.41L12 12.59l2.12-2.12 1.41 1.41L13.41 14l2.12
|
||||
2.12-1.41 1.41L12 15.41l-2.12 2.12-1.41-1.41L10.59 14l-2.13-2.12zM15.5 4l-1-1h-5l-1 1H5v2h14V4z
|
||||
`;
|
||||
|
||||
const PATH_DOWN = 'M7.41 8.59L12 13.17l4.59-4.58L18 10l-6 6-6-6 1.41-1.41z';
|
||||
|
||||
const PATH_EXPANDED = 'M7 10l5 5 5-5z';
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
.Filter {
|
||||
display: block;
|
||||
padding: 0.5rem 0;
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
// @flow
|
||||
|
||||
import React, { Fragment, useCallback, useState } from 'react';
|
||||
import { getSavedFilterPreferences, saveFilterPreferences } from 'src/utils';
|
||||
import { ElementTypeHostComponent } from 'src/types';
|
||||
|
||||
import styles from './FilterList.css';
|
||||
|
||||
export default function FilterList(_: {||}) {
|
||||
const [filterPreferences, setFilterPreferences] = useState(
|
||||
getSavedFilterPreferences
|
||||
);
|
||||
const updateFilterPreferences = useCallback(() => {
|
||||
const clonedFilterPreferences = { ...filterPreferences };
|
||||
setFilterPreferences(clonedFilterPreferences);
|
||||
saveFilterPreferences(clonedFilterPreferences);
|
||||
}, [filterPreferences]);
|
||||
|
||||
const { hideElementsWithTypes } = filterPreferences;
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
<label className={styles.Filter}>
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={hideElementsWithTypes.has(ElementTypeHostComponent)}
|
||||
onChange={() => {
|
||||
if (hideElementsWithTypes.has(ElementTypeHostComponent)) {
|
||||
hideElementsWithTypes.delete(ElementTypeHostComponent);
|
||||
} else {
|
||||
hideElementsWithTypes.add(ElementTypeHostComponent);
|
||||
}
|
||||
updateFilterPreferences();
|
||||
}}
|
||||
/>{' '}
|
||||
Hide host components (e.g. <code><div></code>)
|
||||
</label>
|
||||
</Fragment>
|
||||
);
|
||||
}
|
|
@ -71,6 +71,7 @@
|
|||
}
|
||||
|
||||
.ScreenshotThrottling {
|
||||
display: inline-block;
|
||||
background-color: var(--color-background-hover);
|
||||
padding: 0.25rem 0.5rem;
|
||||
border-radius: 0.25rem;
|
||||
|
|
|
@ -5,6 +5,7 @@ import { useSubscription } from '../hooks';
|
|||
import { StoreContext } from '../context';
|
||||
import { SettingsContext } from './SettingsContext';
|
||||
import Store from 'src/devtools/store';
|
||||
import FilterList from './FilterList';
|
||||
import portaledContent from '../portaledContent';
|
||||
|
||||
import styles from './Settings.css';
|
||||
|
@ -134,6 +135,7 @@ function Settings(_: {||}) {
|
|||
|
||||
<div className={styles.Section}>
|
||||
<div className={styles.Header}>Components tree</div>
|
||||
|
||||
<label className={styles.CheckboxOption}>
|
||||
<input
|
||||
type="checkbox"
|
||||
|
@ -142,6 +144,8 @@ function Settings(_: {||}) {
|
|||
/>{' '}
|
||||
Collapse newly added components by default
|
||||
</label>
|
||||
|
||||
<FilterList />
|
||||
</div>
|
||||
|
||||
{store.supportsCaptureScreenshots && (
|
||||
|
|
|
@ -38,14 +38,20 @@ export function useIsOverflowing(
|
|||
// Forked from https://usehooks.com/useLocalStorage/
|
||||
export function useLocalStorage<T>(
|
||||
key: string,
|
||||
initialValue: T
|
||||
initialValue: T | (() => T)
|
||||
): [T, (value: T | (() => T)) => void] {
|
||||
const getValueFromLocalStorage = useCallback(() => {
|
||||
try {
|
||||
const item = window.localStorage.getItem(key);
|
||||
return item ? JSON.parse(item) : initialValue;
|
||||
if (item != null) {
|
||||
return JSON.parse(item);
|
||||
}
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
if (typeof initialValue === 'function') {
|
||||
return (initialValue: any)();
|
||||
} else {
|
||||
return initialValue;
|
||||
}
|
||||
}, [initialValue, key]);
|
||||
|
|
27
src/types.js
27
src/types.js
|
@ -12,6 +12,10 @@ export type Wall = {|
|
|||
send: (event: string, payload: any, transferable?: Array<any>) => void,
|
||||
|};
|
||||
|
||||
// WARNING
|
||||
// The values below are referenced by FilterPreferences (which is saved via localStorage).
|
||||
// Do not change them or it will break previously saved user customizations.
|
||||
// If new element types are added, use new numbers rather than re-ordering existing ones.
|
||||
export const ElementTypeClass = 1;
|
||||
export const ElementTypeContext = 2;
|
||||
export const ElementTypeEventComponent = 3;
|
||||
|
@ -30,16 +34,15 @@ export const ElementTypeSuspense = 12;
|
|||
// or to enable/disable certain functionality.
|
||||
export type ElementType = 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12;
|
||||
|
||||
export const FilterByElementType = 1;
|
||||
export const FilterByName = 2;
|
||||
export const FilterByPath = 3;
|
||||
export type FilterPreferences = {|
|
||||
// Hide all elements of types in this Set.
|
||||
// We hide host components only by default.
|
||||
hideElementsWithTypes: Set<ElementType>,
|
||||
|
||||
export type Filter =
|
||||
| {|
|
||||
type: 1,
|
||||
value: ElementType,
|
||||
|}
|
||||
| {|
|
||||
type: 2 | 3,
|
||||
value: RegExp,
|
||||
|};
|
||||
// Hide all elements with displayNames matching one or more of the RegExps in this Set.
|
||||
hideElementsWithDisplayNames: Set<RegExp>,
|
||||
|
||||
// Hide all elements within paths matching one or more of the RegExps in this Set.
|
||||
// This filter is only used for elements that include debug source location.
|
||||
hideElementsWithPaths: Set<RegExp>,
|
||||
|};
|
||||
|
|
46
src/utils.js
46
src/utils.js
|
@ -1,10 +1,10 @@
|
|||
// @flow
|
||||
|
||||
import LRU from 'lru-cache';
|
||||
import { LOCAL_STORAGE_FILTERS_KEY } from './constants';
|
||||
import { LOCAL_STORAGE_FILTER_PREFERENCES_KEY } from './constants';
|
||||
import { ElementTypeHostComponent } from './types';
|
||||
|
||||
import type { Filter } from './types';
|
||||
import type { FilterPreferences } from './types';
|
||||
|
||||
const FB_MODULE_RE = /^(.*) \[from (.*)\]$/;
|
||||
const cachedDisplayNames: WeakMap<Function, string> = new WeakMap();
|
||||
|
@ -81,11 +81,43 @@ function toCodePoint(string: string) {
|
|||
return string.codePointAt(0);
|
||||
}
|
||||
|
||||
export function getSavedFilters(): Array<Filter> {
|
||||
const filters = localStorage.getItem(LOCAL_STORAGE_FILTERS_KEY);
|
||||
if (filters != null) {
|
||||
return ((JSON.parse(filters): any): Array<Filter>);
|
||||
export function getDefaultFilterPreferences(): FilterPreferences {
|
||||
return {
|
||||
hideElementsWithTypes: new Set([ElementTypeHostComponent]),
|
||||
hideElementsWithDisplayNames: new Set(),
|
||||
hideElementsWithPaths: new Set(),
|
||||
};
|
||||
}
|
||||
|
||||
export function getSavedFilterPreferences(): FilterPreferences {
|
||||
const raw = localStorage.getItem(LOCAL_STORAGE_FILTER_PREFERENCES_KEY);
|
||||
if (raw != null) {
|
||||
const json = JSON.parse(raw);
|
||||
return {
|
||||
hideElementsWithTypes: new Set(json.hideElementsWithTypes),
|
||||
hideElementsWithDisplayNames: new Set(json.hideElementsWithDisplayNames),
|
||||
hideElementsWithPaths: new Set(json.hideElementsWithPaths),
|
||||
};
|
||||
} else {
|
||||
return [{ type: 1, value: ElementTypeHostComponent }];
|
||||
return getDefaultFilterPreferences();
|
||||
}
|
||||
}
|
||||
|
||||
export function saveFilterPreferences(
|
||||
filterPreferences: FilterPreferences
|
||||
): void {
|
||||
localStorage.setItem(
|
||||
LOCAL_STORAGE_FILTER_PREFERENCES_KEY,
|
||||
JSON.stringify({
|
||||
hideElementsWithTypes: Array.from(
|
||||
filterPreferences.hideElementsWithTypes
|
||||
),
|
||||
hideElementsWithDisplayNames: Array.from(
|
||||
filterPreferences.hideElementsWithDisplayNames
|
||||
),
|
||||
hideElementsWithPaths: Array.from(
|
||||
filterPreferences.hideElementsWithPaths
|
||||
),
|
||||
})
|
||||
);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue