Skip to content

Commit

Permalink
feat: add table filter ref and example (#407)
Browse files Browse the repository at this point in the history
* chore: add table filter ref

* chore: add usage of tablefilter ref

* fix: ux

* chore: minor change and comments
  • Loading branch information
islxyqwe authored Aug 9, 2024
1 parent 0083263 commit c93ca77
Show file tree
Hide file tree
Showing 4 changed files with 75 additions and 7 deletions.
1 change: 1 addition & 0 deletions packages/graphic-walker/src/Table.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ export const TableApp = observer(function VizApp(props: BaseTableProps) {
<div className={`${darkMode === 'dark' ? 'dark' : ''} App font-sans bg-background text-foreground h-full m-0 p-0`}>
<div className="bg-background text-foreground h-full">
<DatasetTable
ref={props.tableFilterRef}
onMetaChange={vizStore.onMetaChange ? (fid, fIndex, diffMeta) => {
vizStore.updateCurrentDatasetMetas(fid, diffMeta);
} : undefined}
Expand Down
21 changes: 17 additions & 4 deletions packages/graphic-walker/src/components/dataTable/index.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React, { useMemo, useState, useRef, useEffect, useCallback } from 'react';
import React, { useMemo, useState, useRef, useEffect, useCallback, forwardRef, useImperativeHandle, ForwardedRef } from 'react';
import styled from 'styled-components';
import type { IMutField, IRow, IComputationFunction, IFilterFiledSimple, IFilterRule, IFilterField, IFilterWorkflowStep, IField } from '../../interfaces';
import type { IMutField, IRow, IComputationFunction, IFilterFiledSimple, IFilterRule, IFilterField, IFilterWorkflowStep, IField, IVisFilter } from '../../interfaces';
import { useTranslation } from 'react-i18next';
import LoadingLayer from '../loadingLayer';
import { dataReadRaw } from '../../computation';
Expand Down Expand Up @@ -232,7 +232,13 @@ function TruncateDector(props: { value: string }) {
);
}

const DataTable: React.FC<DataTableProps> = (props) => {
const DataTable = forwardRef(
(
props: DataTableProps,
ref: ForwardedRef<{
getFilters: () => IVisFilter[];
}>
) => {
const { size = 10, onMetaChange, metas, computation, disableFilter, displayOffset, hidePaginationAtOnepage, hideProfiling } = props;
const [pageIndex, setPageIndex] = useState(0);
const { t } = useTranslation();
Expand All @@ -253,6 +259,13 @@ const DataTable: React.FC<DataTableProps> = (props) => {

const { filters, editingFilterIdx, onClose, onDeleteFilter, onSelectFilter, onWriteFilter, options } = useFilters(metas);

const filtersRef = useRef(filters);
filtersRef.current = filters;

useImperativeHandle(ref, () => ({
getFilters: () => filtersRef.current.filter(x => x.rule) as IVisFilter[],
}));

const [total, setTotal] = useState(0);
const [statLoading, setStatLoading] = useState(false);

Expand Down Expand Up @@ -541,7 +554,7 @@ const DataTable: React.FC<DataTableProps> = (props) => {
)}
</Container>
);
};
});

export default DataTable;

Expand Down
3 changes: 3 additions & 0 deletions packages/graphic-walker/src/interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -986,6 +986,9 @@ export interface ITableSpecProps {
/** @deprecated use vizThemeConfig instead */
themeConfig?: GWGlobalConfig;
vizThemeConfig?: IThemeKey | GWGlobalConfig;
tableFilterRef?: React.Ref<{
getFilters: () => IVisFilter[];
}>;
}

export interface IVizAppProps extends IAppI18nProps, IVizProps, IThemeProps, IErrorHandlerProps, IVizStoreProps, ISpecProps {}
Expand Down
57 changes: 54 additions & 3 deletions packages/playground/src/examples/pages/table.stories.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,62 @@
import { useContext } from 'react';
import { TableWalker } from '@kanaries/graphic-walker';
import { useContext, useRef } from 'react';
import { getComputation, IVisFilter, TableWalker } from '@kanaries/graphic-walker';
import { themeContext } from '../context';
import { useFetch, IDataSource } from '../util';

export default function GraphicWalkerComponent() {
const { theme } = useContext(themeContext);
const { dataSource, fields } = useFetch<IDataSource>('https://pub-2422ed4100b443659f588f2382cfc7b1.r2.dev/datasets/ds-students-service.json');
const tableRef = useRef<{ getFilters: () => IVisFilter[] }>(null);

return <TableWalker fields={fields} data={dataSource} appearance={theme} pageSize={50} />;
const downloadCSV = async () => {
const filters = tableRef.current?.getFilters() ?? [];

// or use a remote computation service
// const computation = async (workflow) => fetch(endPoint, { body: JSON.stringify(workflow) }).then(resp => resp.json())
const computation = getComputation(dataSource);

const result = await computation({
workflow: [
{ type: 'filter', filters },
{
type: 'view',
query: [
{
op: 'raw',
fields: fields.map((x) => x.fid),
},
],
},
],
});

const header = fields.map((x) => x.name).join(',');
const data = result
.map((row) =>
fields
.map((x) => row[x.fid] ?? '')
.map((x) => (typeof x === 'string' ? `"${x}"` : `${x}`))
.join(',')
)
.join('\n');
const blob = new Blob([header + '\n' + data], { type: 'text/csv' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = `Student.csv`;
a.click();
URL.revokeObjectURL(url);
};

return (
<div className="flex flex-col gap-2">
<button
onClick={downloadCSV}
className="h-9 px-4 py-2 w-fit m-2 bg-zinc-950 text-white shadow hover:bg-primary/90 inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50"
>
Export CSV
</button>
<TableWalker tableFilterRef={tableRef} fields={fields} data={dataSource} appearance={theme} pageSize={50} vizThemeConfig="g2" />
</div>
);
}

0 comments on commit c93ca77

Please sign in to comment.