diff --git a/packages/graphic-walker/src/Table.tsx b/packages/graphic-walker/src/Table.tsx index 1d0ab289..822b0276 100644 --- a/packages/graphic-walker/src/Table.tsx +++ b/packages/graphic-walker/src/Table.tsx @@ -85,6 +85,7 @@ export const TableApp = observer(function VizApp(props: BaseTableProps) {
{ vizStore.updateCurrentDatasetMetas(fid, diffMeta); } : undefined} diff --git a/packages/graphic-walker/src/components/dataTable/index.tsx b/packages/graphic-walker/src/components/dataTable/index.tsx index 1f16fd28..8745a842 100644 --- a/packages/graphic-walker/src/components/dataTable/index.tsx +++ b/packages/graphic-walker/src/components/dataTable/index.tsx @@ -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'; @@ -232,7 +232,13 @@ function TruncateDector(props: { value: string }) { ); } -const DataTable: React.FC = (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(); @@ -253,6 +259,13 @@ const DataTable: React.FC = (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); @@ -541,7 +554,7 @@ const DataTable: React.FC = (props) => { )} ); -}; +}); export default DataTable; diff --git a/packages/graphic-walker/src/interfaces.ts b/packages/graphic-walker/src/interfaces.ts index c1fae193..1d28d77a 100644 --- a/packages/graphic-walker/src/interfaces.ts +++ b/packages/graphic-walker/src/interfaces.ts @@ -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 {} diff --git a/packages/playground/src/examples/pages/table.stories.tsx b/packages/playground/src/examples/pages/table.stories.tsx index a6acd6de..6289917a 100644 --- a/packages/playground/src/examples/pages/table.stories.tsx +++ b/packages/playground/src/examples/pages/table.stories.tsx @@ -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('https://pub-2422ed4100b443659f588f2382cfc7b1.r2.dev/datasets/ds-students-service.json'); + const tableRef = useRef<{ getFilters: () => IVisFilter[] }>(null); - return ; + 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 ( +
+ + +
+ ); }