Files
flipper/desktop/flipper-plugin/src/utils/createTablePlugin.tsx
Michel Weststrate f5b5e9be9f Convert rsys plugin to Sandy
Summary:
The `flipper` dependency is being deprecated for plugins, so upgraded the plugin to new APIs. This should be pretty safe, however, I'm not sure how to test it :)

Also removed react-diff-viewer dependency, as that seemed unused.

Reviewed By: passy

Differential Revision: D28091469

fbshipit-source-id: b9be007e3fc7b0689b1e1f711849ed82a6e01716
2021-05-06 06:44:22 -07:00

151 lines
4.3 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 {notification, Typography} from 'antd';
import React from 'react';
import {PluginClient} from '../plugin/Plugin';
import {usePlugin} from '../plugin/PluginContext';
import {createState} from '../state/atom';
import {createDataSource, DataSource} from '../state/DataSource';
import {DataTableColumn} from '../ui/data-table/DataTable';
import {MasterDetail} from '../ui/MasterDetail';
type PluginResult<Raw, Row> = {
plugin(
client: PluginClient<Record<string, Raw | {}>>,
): {
rows: DataSource<Row>;
};
Component(): React.ReactElement;
};
/**
* createTablePlugin creates a Plugin class which handles fetching data from the client and
* displaying in in a table. The table handles selection of items and rendering a sidebar where
* more detailed information can be presented about the selected row.
*
* The plugin expects the be able to subscribe to the `method` argument and recieve either an array
* of data objects or a single data object. Each data object represents a row in the table which is
* build by calling the `buildRow` function argument.
*
* An optional resetMethod argument can be provided which will replace the current rows with the
* data provided. This is useful when connecting to Flipper for this first time, or reconnecting to
* the client in an unknown state.
*/
export function createTablePlugin<Row extends object>(props: {
method: string;
resetMethod?: string;
columns: DataTableColumn<Row>[];
renderSidebar?: (record: Row) => any;
key?: keyof Row;
onCopyRows?(records: Row[]): string;
}): PluginResult<Row, Row>;
export function createTablePlugin<
Raw extends object,
Row extends object = Raw
>(props: {
buildRow: (record: Raw) => Row;
method: string;
resetMethod?: string;
columns: DataTableColumn<Row>[];
renderSidebar?: (record: Row) => any;
key?: keyof Raw;
onCopyRows?(records: Row[]): string;
}): PluginResult<Raw, Row>;
export function createTablePlugin<
Raw extends object,
Method extends string,
ResetMethod extends string,
Row extends object = Raw
>(props: {
method: Method;
resetMethod?: ResetMethod;
columns: DataTableColumn<Row>[];
renderSidebar?: (record: Row) => any;
buildRow?: (record: Raw) => Row;
key?: keyof Raw;
onCopyRows?(records: Row[]): string;
}) {
function plugin(
client: PluginClient<Record<Method, Raw> & Record<ResetMethod, {}>, {}>,
) {
const rows = createDataSource<Row>([], {
persist: 'rows',
key: props.key,
});
const selection = createState<undefined | Row>(undefined);
const isPaused = createState(false);
client.onMessage(props.method, (event) => {
if (isPaused.get()) {
return;
}
const record = props.buildRow
? props.buildRow(event)
: ((event as any) as Row);
if (props.key) {
rows.upsert(record);
} else {
rows.append(record);
}
});
if (props.resetMethod) {
client.onMessage(props.resetMethod, () => {
rows.clear();
});
}
// help plugin authors with finding out what the events and data shape is from the plugin
const unhandledMessagesSeen = new Set<string>();
client.onUnhandledMessage((message, params) => {
if (unhandledMessagesSeen.has(message)) {
return;
}
unhandledMessagesSeen.add(message);
notification.warn({
message: 'Unhandled message: ' + message,
description: (
<Typography.Paragraph>
<pre>{JSON.stringify(params, null, 2)}</pre>
</Typography.Paragraph>
),
});
});
return {
selection,
rows,
isPaused,
};
}
function SidebarComponent({record}: {record: Row}) {
return props.renderSidebar!(record);
}
function Component() {
const instance = usePlugin(plugin);
return (
<MasterDetail<Row>
columns={props.columns}
dataSource={instance.rows}
sidebarComponent={props.renderSidebar ? SidebarComponent : undefined}
selection={instance.selection}
isPaused={instance.isPaused}
enableMenuEntries
enableClear
onCopyRows={props.onCopyRows}
/>
);
}
return {plugin, Component};
}