Introduce DataList

Summary: Introduce the DataList component. Not feature complete yet, but core functionality is present so that people can use it during the convertathon. It is used to implement the route list in the network mock dialog

Reviewed By: priteshrnandgaonkar

Differential Revision: D27046716

fbshipit-source-id: a247ce7032b350b31bf55962ca4268e30f43471a
This commit is contained in:
Michel Weststrate
2021-04-27 01:42:52 -07:00
committed by Facebook GitHub Bot
parent f95cddd5ef
commit c89d18fd68
6 changed files with 174 additions and 3 deletions

View File

@@ -31,6 +31,7 @@ test('Correct top level API exposed', () => {
"DataDescription", "DataDescription",
"DataFormatter", "DataFormatter",
"DataInspector", "DataInspector",
"DataList",
"DataSource", "DataSource",
"DataTable", "DataTable",
"DetailSidebar", "DetailSidebar",

View File

@@ -84,6 +84,7 @@ export {createDataSource, DataSource} from './state/DataSource';
export {DataTable, DataTableColumn} from './ui/data-table/DataTable'; export {DataTable, DataTableColumn} from './ui/data-table/DataTable';
export {DataTableManager} from './ui/data-table/DataTableManager'; export {DataTableManager} from './ui/data-table/DataTableManager';
export {DataList} from './ui/DataList';
export { export {
Interactive as _Interactive, Interactive as _Interactive,

View File

@@ -94,7 +94,10 @@ export class DataSource<
private nextId = 0; private nextId = 0;
private _records: Entry<T>[] = []; private _records: Entry<T>[] = [];
private _recordsById: Map<KEY_TYPE, T> = new Map(); private _recordsById: Map<KEY_TYPE, T> = new Map();
private keyAttribute: undefined | keyof T; /**
* @readonly
*/
public keyAttribute: undefined | keyof T;
private idToIndex: Map<KEY_TYPE, number> = new Map(); private idToIndex: Map<KEY_TYPE, number> = new Map();
// if we shift the window, we increase shiftOffset to correct idToIndex results, rather than remapping all values // if we shift the window, we increase shiftOffset to correct idToIndex results, rather than remapping all values

View File

@@ -0,0 +1,152 @@
/**
* 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, {useCallback, memo} from 'react';
import {DataFormatter} from './DataFormatter';
import {Layout} from './Layout';
import {theme} from './theme';
import {Typography} from 'antd';
const {Text} = Typography;
interface Item {
id: string;
title: string;
description?: string;
}
interface DataListProps<T extends Item> {
/**
* Defines the styling of the component. By default shows a list, but alternatively the items can be displayed in a drop down
*/
type?: 'default' /* | 'compact' | 'dropdown' */;
/**
* By default the data list will take all available space and scroll if items aren't otherwise visible.
* By setting `scrollable={false}` the list will only take its natural size
*/
scrollable?: boolean;
/**
* The current selection
*/
value?: string /* | Atom<string>*/;
/**
* Handler that is fired if selection is changed
*/
onSelect?(id: string, value: T): void;
className?: string;
style?: React.CSSProperties;
/**
* Items to display. Per item at least a title and unique id should be provided
*/
items: readonly Item[];
/**
* Custom render function. By default the component will render the `title` in bold and description (if any) below it
*/
onRenderItem?: (item: T, selected: boolean) => React.ReactElement;
}
export const DataList: React.FC<DataListProps<any>> = function DataList<
T extends Item
>({
// type,
scrollable,
value,
onSelect,
className,
style,
items,
onRenderItem,
}: DataListProps<T>) {
const handleSelect = useCallback(
(key: string, item: T) => {
onSelect?.(key, item);
},
[onSelect],
);
const renderedItems = items.map((item) => (
<DataListItemWrapper
item={item}
key={item.id}
selected={item.id === value}
onRenderItem={onRenderItem as any}
onSelect={handleSelect as any}
/>
));
return scrollable ? (
<Layout.Container
style={style}
className={className}
borderTop
borderBottom
grow>
<Layout.ScrollContainer vertical>{renderedItems}</Layout.ScrollContainer>
</Layout.Container>
) : (
<Layout.Container style={style} className={className} borderTop>
{renderedItems}
</Layout.Container>
);
};
DataList.defaultProps = {
type: 'default',
scrollable: false,
onRenderItem: defaultItemRenderer,
};
function defaultItemRenderer(item: Item, _selected: boolean) {
return <DataListItem title={item.title} description={item.description} />;
}
const DataListItemWrapper = memo(
({
item,
onRenderItem,
onSelect,
selected,
}: {
item: Item;
onRenderItem: typeof defaultItemRenderer;
onSelect: (id: string, item: Item) => void;
selected: boolean;
}) => {
return (
<Layout.Container
pad
borderBottom
key={item.id}
style={{
background: selected ? theme.backgroundWash : undefined,
borderLeft: selected
? `4px solid ${theme.primaryColor}`
: `4px solid transparent`,
}}
onClick={() => {
onSelect(item.id, item);
}}>
{onRenderItem(item, selected)}
</Layout.Container>
);
},
);
const DataListItem = memo(
({title, description}: {title: string; description?: string}) => {
return (
<>
<Text strong>{DataFormatter.format(title)}</Text>
{description != null && (
<Text type="secondary">{DataFormatter.format(description)}</Text>
)}
</>
);
},
);

View File

@@ -88,9 +88,10 @@ const Container = styled.div<ContainerProps>(
}), }),
); );
const Horizontal = styled(Container)({ const Horizontal = styled(Container)<{wrap?: boolean}>(({wrap}) => ({
flexDirection: 'row', flexDirection: 'row',
}); flexWrap: wrap ? 'wrap' : undefined,
}));
const ScrollParent = styled.div<{axis?: ScrollAxis}>(({axis}) => ({ const ScrollParent = styled.div<{axis?: ScrollAxis}>(({axis}) => ({
flex: 1, flex: 1,

View File

@@ -809,6 +809,19 @@ See `View > Flipper Style Guide` inside the Flipper application for more details
Coming soon. Coming soon.
### DataList
The DataList can be used to display a set of items efficiently, and where a single item can be selected.
Properties:
* `items`: Items to display. Per item at least a title and unique id should be provided.
* `value`: The current selection
* `onSelect`
* `onRenderItem`: A custom render function. By default the component will render the `title` in bold and description (if any) below it.
* `type`: `default` or `dropdown. Defines the styling of the component. By default shows a list, but alternatively the items can be displayed in a drop down
* `scrollable`: By default the data list will take all available space and scroll if items aren't otherwise visible. By setting `scrollable={false}` the list will only take its natural size
### NUX ### NUX
An element that can be used to provide a New User eXperience: Hints that give a one time introduction to new features to the current user. An element that can be used to provide a New User eXperience: Hints that give a one time introduction to new features to the current user.