From cf1ec445c7ecdd740dd6f8f99a108ed42767c033 Mon Sep 17 00:00:00 2001 From: islxyqwe Date: Fri, 24 May 2024 19:46:20 +0800 Subject: [PATCH 1/4] chore: add table filter ref --- packages/graphic-walker/src/Table.tsx | 1 + .../src/components/dataTable/index.tsx | 19 ++++++++++++++++--- packages/graphic-walker/src/interfaces.ts | 3 +++ 3 files changed, 20 insertions(+), 3 deletions(-) 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..94cc1b1b 100644 --- a/packages/graphic-walker/src/components/dataTable/index.tsx +++ b/packages/graphic-walker/src/components/dataTable/index.tsx @@ -1,4 +1,4 @@ -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 { useTranslation } from 'react-i18next'; @@ -232,7 +232,13 @@ function TruncateDector(props: { value: string }) { ); } -const DataTable: React.FC = (props) => { +const DataTable = forwardRef( + ( + props: DataTableProps, + ref: ForwardedRef<{ + getFilters: () => IFilterField[]; + }> + ) => { 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, + })); + 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..7ae9cfb6 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: () => IFilterField[]; + }>; } export interface IVizAppProps extends IAppI18nProps, IVizProps, IThemeProps, IErrorHandlerProps, IVizStoreProps, ISpecProps {} From 275e7a1f567e0be41c41d6551cd830705d926e9f Mon Sep 17 00:00:00 2001 From: islxyqwe Date: Fri, 9 Aug 2024 15:07:56 +0800 Subject: [PATCH 2/4] chore: add usage of tablefilter ref --- .../src/examples/pages/table.stories.tsx | 53 +++++++++++++++++-- 1 file changed, 50 insertions(+), 3 deletions(-) diff --git a/packages/playground/src/examples/pages/table.stories.tsx b/packages/playground/src/examples/pages/table.stories.tsx index a6acd6de..81d641d8 100644 --- a/packages/playground/src/examples/pages/table.stories.tsx +++ b/packages/playground/src/examples/pages/table.stories.tsx @@ -1,11 +1,58 @@ -import { useContext } from 'react'; -import { TableWalker } from '@kanaries/graphic-walker'; +import { useContext, useRef } from 'react'; +import { getComputation, IFilterField, 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: () => IFilterField[] }>(null); - return ; + const downloadCSV = async () => { + const filters = (tableRef.current?.getFilters() ?? []).filter((x) => x.rule); + + const result = await getComputation(dataSource)({ + workflow: [ + ...(filters.length > 0 ? [{ type: 'filter' as const, filters: filters.map((x): IVisFilter => ({ ...x, rule: x.rule! })) }] : []), + { + 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 ( +
+ + +
+ ); } From 1869c2880920cd9ef966a86ef445af80108a714b Mon Sep 17 00:00:00 2001 From: islxyqwe Date: Fri, 9 Aug 2024 15:17:26 +0800 Subject: [PATCH 3/4] fix: ux --- packages/playground/src/examples/pages/table.stories.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/playground/src/examples/pages/table.stories.tsx b/packages/playground/src/examples/pages/table.stories.tsx index 81d641d8..4d7c34a0 100644 --- a/packages/playground/src/examples/pages/table.stories.tsx +++ b/packages/playground/src/examples/pages/table.stories.tsx @@ -48,11 +48,11 @@ export default function GraphicWalkerComponent() {
- +
); } From 518ec845b27fe28e0cdd7065d4c6c41cd499032b Mon Sep 17 00:00:00 2001 From: islxyqwe Date: Fri, 9 Aug 2024 15:35:03 +0800 Subject: [PATCH 4/4] chore: minor change and comments --- .../src/components/dataTable/index.tsx | 6 +++--- packages/graphic-walker/src/interfaces.ts | 2 +- .../src/examples/pages/table.stories.tsx | 14 +++++++++----- 3 files changed, 13 insertions(+), 9 deletions(-) diff --git a/packages/graphic-walker/src/components/dataTable/index.tsx b/packages/graphic-walker/src/components/dataTable/index.tsx index 94cc1b1b..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, 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'; @@ -236,7 +236,7 @@ const DataTable = forwardRef( ( props: DataTableProps, ref: ForwardedRef<{ - getFilters: () => IFilterField[]; + getFilters: () => IVisFilter[]; }> ) => { const { size = 10, onMetaChange, metas, computation, disableFilter, displayOffset, hidePaginationAtOnepage, hideProfiling } = props; @@ -263,7 +263,7 @@ const DataTable = forwardRef( filtersRef.current = filters; useImperativeHandle(ref, () => ({ - getFilters: () => filtersRef.current, + getFilters: () => filtersRef.current.filter(x => x.rule) as IVisFilter[], })); const [total, setTotal] = useState(0); diff --git a/packages/graphic-walker/src/interfaces.ts b/packages/graphic-walker/src/interfaces.ts index 7ae9cfb6..1d28d77a 100644 --- a/packages/graphic-walker/src/interfaces.ts +++ b/packages/graphic-walker/src/interfaces.ts @@ -987,7 +987,7 @@ export interface ITableSpecProps { themeConfig?: GWGlobalConfig; vizThemeConfig?: IThemeKey | GWGlobalConfig; tableFilterRef?: React.Ref<{ - getFilters: () => IFilterField[]; + getFilters: () => IVisFilter[]; }>; } diff --git a/packages/playground/src/examples/pages/table.stories.tsx b/packages/playground/src/examples/pages/table.stories.tsx index 4d7c34a0..6289917a 100644 --- a/packages/playground/src/examples/pages/table.stories.tsx +++ b/packages/playground/src/examples/pages/table.stories.tsx @@ -1,19 +1,23 @@ import { useContext, useRef } from 'react'; -import { getComputation, IFilterField, IVisFilter, TableWalker } from '@kanaries/graphic-walker'; +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: () => IFilterField[] }>(null); + const tableRef = useRef<{ getFilters: () => IVisFilter[] }>(null); const downloadCSV = async () => { - const filters = (tableRef.current?.getFilters() ?? []).filter((x) => x.rule); + const filters = tableRef.current?.getFilters() ?? []; - const result = await getComputation(dataSource)({ + // 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: [ - ...(filters.length > 0 ? [{ type: 'filter' as const, filters: filters.map((x): IVisFilter => ({ ...x, rule: x.rule! })) }] : []), + { type: 'filter', filters }, { type: 'view', query: [