Summary: Beyond a search across all columns, it is now possible to specific columns for specific values: * for a row to be visible, all active column filters need to be matched (e.g. both a filter on time and app has to be satisfied) * if multiple values within a column are filtered for, these are -or-ed. * if no value at all within a column is checked, even when they are defined, the column won't take part in filtering * if there is a general search and column filters, a row has to satisfy both Filters can be preconfigured, pre-configured filters cannot be removed. Reseting will reset the filters back to their original Move `useMemoize` to flipper-plugin Merged the `ui/utils` and `utils` folder inside `flipper-plugin` Reviewed By: nikoant Differential Revision: D26450260 fbshipit-source-id: 11693d5d140cea03cad91c1e0f3438d7b129cf29
151 lines
4.0 KiB
TypeScript
151 lines
4.0 KiB
TypeScript
/**
|
|
* Copyright (c) Facebook, Inc. and its affiliates.
|
|
*
|
|
* This source code is licensed under the MIT license found in the
|
|
* LICENSE file in the root directory of this source tree.
|
|
*
|
|
* @format
|
|
*/
|
|
|
|
import React, {useMemo} from 'react';
|
|
import {AutoComplete, Input, Typography} from 'antd';
|
|
import {StarFilled, StarOutlined} from '@ant-design/icons';
|
|
import {useStore} from '../../utils/useStore';
|
|
import {
|
|
Layout,
|
|
NUX,
|
|
TrackingScope,
|
|
useTrackedCallback,
|
|
useValue,
|
|
} from 'flipper-plugin';
|
|
import {State} from '../../reducers';
|
|
|
|
// eslint-disable-next-line flipper/no-relative-imports-across-packages
|
|
import type {NavigationPlugin} from '../../../../plugins/navigation/index';
|
|
import {useMemoize} from 'flipper-plugin';
|
|
import styled from '@emotion/styled';
|
|
|
|
const {Text} = Typography;
|
|
|
|
export function BookmarkSection() {
|
|
const navPlugin = useStore(navPluginStateSelector);
|
|
|
|
return navPlugin ? (
|
|
<TrackingScope scope="bookmarks">
|
|
<NUX
|
|
title="Use bookmarks to directly navigate to a location in the app."
|
|
placement="right">
|
|
<BookmarkSectionInput navPlugin={navPlugin} />
|
|
</NUX>
|
|
</TrackingScope>
|
|
) : null;
|
|
}
|
|
|
|
function BookmarkSectionInput({navPlugin}: {navPlugin: NavigationPlugin}) {
|
|
const currentURI = useValue(navPlugin.currentURI);
|
|
const bookmarks = useValue(navPlugin.bookmarks);
|
|
const patterns = useValue(navPlugin.appMatchPatterns);
|
|
|
|
const isBookmarked = useMemo(() => bookmarks.has(currentURI), [
|
|
bookmarks,
|
|
currentURI,
|
|
]);
|
|
|
|
const autoCompleteItems = useMemoize(
|
|
navPlugin.getAutoCompleteAppMatchPatterns,
|
|
[currentURI, bookmarks, patterns, 20],
|
|
);
|
|
|
|
const handleBookmarkClick = useTrackedCallback(
|
|
'bookmark',
|
|
() => {
|
|
if (isBookmarked) {
|
|
navPlugin.removeBookmark(currentURI);
|
|
} else if (currentURI) {
|
|
navPlugin.addBookmark({
|
|
uri: currentURI,
|
|
commonName: null,
|
|
});
|
|
}
|
|
},
|
|
[navPlugin, currentURI, isBookmarked],
|
|
);
|
|
|
|
const navigate = useTrackedCallback('navigate', navPlugin.navigateTo, []);
|
|
|
|
const bookmarkButton = isBookmarked ? (
|
|
<StarFilled onClick={handleBookmarkClick} />
|
|
) : (
|
|
<StarOutlined onClick={handleBookmarkClick} />
|
|
);
|
|
|
|
return (
|
|
<StyledAutoComplete
|
|
dropdownMatchSelectWidth={500}
|
|
value={currentURI}
|
|
onSelect={navigate}
|
|
style={{flex: 1}}
|
|
options={[
|
|
{
|
|
label: <Text strong>Bookmarks</Text>,
|
|
options: Array.from(bookmarks.values()).map((bookmark) => ({
|
|
value: bookmark.uri,
|
|
label: (
|
|
<NavigationEntry label={bookmark.commonName} uri={bookmark.uri} />
|
|
),
|
|
})),
|
|
},
|
|
{
|
|
label: <Text strong>Entry points</Text>,
|
|
options: autoCompleteItems.map((value) => ({
|
|
value: value.pattern,
|
|
label: (
|
|
<NavigationEntry label={value.className} uri={value.pattern} />
|
|
),
|
|
})),
|
|
},
|
|
]}>
|
|
<Input
|
|
addonAfter={bookmarkButton}
|
|
defaultValue="<select a bookmark>"
|
|
value={currentURI}
|
|
onChange={(e) => {
|
|
navPlugin.currentURI.set(e.target.value);
|
|
}}
|
|
onPressEnter={() => {
|
|
navigate(currentURI);
|
|
}}
|
|
/>
|
|
</StyledAutoComplete>
|
|
);
|
|
}
|
|
|
|
function NavigationEntry({label, uri}: {label: string | null; uri: string}) {
|
|
return (
|
|
<Layout.Container>
|
|
<Text>{label ?? uri}</Text>
|
|
<Text type="secondary">{uri}</Text>
|
|
</Layout.Container>
|
|
);
|
|
}
|
|
|
|
const StyledAutoComplete = styled(AutoComplete)({
|
|
display: 'flex',
|
|
flex: 1,
|
|
'.ant-select-selector': {
|
|
flex: 1,
|
|
},
|
|
});
|
|
|
|
const NAVIGATION_PLUGIN_ID = 'Navigation';
|
|
|
|
function navPluginStateSelector(state: State) {
|
|
const {selectedApp, clients} = state.connections;
|
|
if (!selectedApp) return undefined;
|
|
const client = clients.find((client) => client.id === selectedApp);
|
|
if (!client) return undefined;
|
|
return client.sandyPluginStates.get(NAVIGATION_PLUGIN_ID)?.instanceApi as
|
|
| undefined
|
|
| NavigationPlugin;
|
|
}
|