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:
committed by
Facebook GitHub Bot
parent
f95cddd5ef
commit
c89d18fd68
@@ -31,6 +31,7 @@ test('Correct top level API exposed', () => {
|
||||
"DataDescription",
|
||||
"DataFormatter",
|
||||
"DataInspector",
|
||||
"DataList",
|
||||
"DataSource",
|
||||
"DataTable",
|
||||
"DetailSidebar",
|
||||
|
||||
@@ -84,6 +84,7 @@ export {createDataSource, DataSource} from './state/DataSource';
|
||||
|
||||
export {DataTable, DataTableColumn} from './ui/data-table/DataTable';
|
||||
export {DataTableManager} from './ui/data-table/DataTableManager';
|
||||
export {DataList} from './ui/DataList';
|
||||
|
||||
export {
|
||||
Interactive as _Interactive,
|
||||
|
||||
@@ -94,7 +94,10 @@ export class DataSource<
|
||||
private nextId = 0;
|
||||
private _records: Entry<T>[] = [];
|
||||
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();
|
||||
|
||||
// if we shift the window, we increase shiftOffset to correct idToIndex results, rather than remapping all values
|
||||
|
||||
152
desktop/flipper-plugin/src/ui/DataList.tsx
Normal file
152
desktop/flipper-plugin/src/ui/DataList.tsx
Normal 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>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
},
|
||||
);
|
||||
@@ -88,9 +88,10 @@ const Container = styled.div<ContainerProps>(
|
||||
}),
|
||||
);
|
||||
|
||||
const Horizontal = styled(Container)({
|
||||
const Horizontal = styled(Container)<{wrap?: boolean}>(({wrap}) => ({
|
||||
flexDirection: 'row',
|
||||
});
|
||||
flexWrap: wrap ? 'wrap' : undefined,
|
||||
}));
|
||||
|
||||
const ScrollParent = styled.div<{axis?: ScrollAxis}>(({axis}) => ({
|
||||
flex: 1,
|
||||
|
||||
@@ -809,6 +809,19 @@ See `View > Flipper Style Guide` inside the Flipper application for more details
|
||||
|
||||
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
|
||||
|
||||
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.
|
||||
|
||||
Reference in New Issue
Block a user