Files
flipper/desktop/app/src/sandy-chrome/appinspect/BookmarkSection.tsx
Michel Weststrate 11eb19da4c Introduce column filters
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
2021-03-16 15:03:44 -07:00

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;
}