@databiosphere/findable-ui 44.0.0 → 45.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.release-please-manifest.json +1 -1
- package/CHANGELOG.md +20 -0
- package/lib/components/DataDictionary/components/Table/options/core/constants.js +2 -1
- package/lib/components/DataDictionary/components/Table/options/hook.js +2 -0
- package/lib/components/Detail/components/Table/table.js +4 -1
- package/lib/components/Index/table/hook.js +4 -1
- package/lib/components/Table/common/columnDef.js +2 -0
- package/lib/components/Table/common/utils.d.ts +0 -6
- package/lib/components/Table/common/utils.js +0 -63
- package/lib/components/Table/components/TableFeatures/TableDownload/tableDownload.d.ts +3 -0
- package/lib/components/Table/components/TableFeatures/TableDownload/tableDownload.js +9 -0
- package/lib/components/Table/components/{DownloadEntityResults/downloadEntityResults.styles.js → TableFeatures/TableDownload/tableDownload.styles.js} +1 -1
- package/lib/components/Table/components/TableFeatures/TableDownload/types.d.ts +5 -0
- package/lib/components/Table/components/TableFeatures/TableDownload/types.js +1 -0
- package/lib/components/Table/components/TableToolbar/tableToolbar.js +3 -3
- package/lib/components/Table/components/TableToolbar2/tableToolbar2.d.ts +3 -0
- package/lib/components/Table/components/TableToolbar2/tableToolbar2.js +8 -0
- package/lib/components/Table/components/TableToolbar2/tableToolbar2.styles.d.ts +10 -0
- package/lib/components/Table/components/TableToolbar2/tableToolbar2.styles.js +16 -0
- package/lib/components/Table/components/TableToolbar2/types.d.ts +4 -0
- package/lib/components/Table/components/TableToolbar2/types.js +1 -0
- package/lib/components/Table/featureOptions/tableDownload/constants.d.ts +3 -0
- package/lib/components/Table/featureOptions/tableDownload/constants.js +3 -0
- package/lib/components/Table/features/TableDownload/constants.d.ts +2 -0
- package/lib/components/Table/features/TableDownload/constants.js +10 -0
- package/lib/components/Table/features/TableDownload/onDownload/utils.d.ts +14 -0
- package/lib/components/Table/features/TableDownload/onDownload/utils.js +63 -0
- package/lib/components/Table/features/TableDownload/types.d.ts +15 -0
- package/lib/components/Table/features/TableDownload/types.js +1 -0
- package/lib/components/Table/features/TableDownload/utils.d.ts +19 -0
- package/lib/components/Table/features/TableDownload/utils.js +46 -0
- package/lib/components/Table/features/entities.d.ts +8 -3
- package/lib/config/entities.d.ts +0 -1
- package/package.json +1 -1
- package/src/components/DataDictionary/components/Table/options/core/constants.ts +2 -1
- package/src/components/DataDictionary/components/Table/options/hook.ts +2 -0
- package/src/components/Detail/components/Table/table.tsx +4 -1
- package/src/components/Index/table/hook.ts +4 -1
- package/src/components/Table/common/columnDef.ts +2 -0
- package/src/components/Table/common/utils.ts +0 -75
- package/src/components/Table/components/{DownloadEntityResults/downloadEntityResults.styles.ts → TableFeatures/TableDownload/tableDownload.styles.ts} +1 -1
- package/src/components/Table/components/TableFeatures/TableDownload/tableDownload.tsx +23 -0
- package/src/components/Table/components/TableFeatures/TableDownload/types.ts +7 -0
- package/src/components/Table/components/TableToolbar/tableToolbar.tsx +3 -8
- package/src/components/Table/components/TableToolbar2/tableToolbar2.styles.ts +18 -0
- package/src/components/Table/components/TableToolbar2/tableToolbar2.tsx +17 -0
- package/src/components/Table/components/TableToolbar2/types.ts +5 -0
- package/src/components/Table/featureOptions/tableDownload/constants.ts +9 -0
- package/src/components/Table/features/TableDownload/constants.ts +24 -0
- package/src/components/Table/features/TableDownload/onDownload/utils.ts +76 -0
- package/src/components/Table/features/TableDownload/types.ts +19 -0
- package/src/components/Table/features/TableDownload/utils.ts +60 -0
- package/src/components/Table/features/entities.ts +19 -3
- package/src/config/entities.ts +0 -1
- package/types/data-explorer-ui.d.ts +16 -2
- package/lib/components/Table/components/DownloadEntityResults/downloadEntityResults.d.ts +0 -6
- package/lib/components/Table/components/DownloadEntityResults/downloadEntityResults.js +0 -18
- package/lib/components/common/Button/components/FileDownloadButton/fileDownloadButton.d.ts +0 -5
- package/lib/components/common/Button/components/FileDownloadButton/fileDownloadButton.js +0 -14
- package/src/components/Table/components/DownloadEntityResults/downloadEntityResults.tsx +0 -39
- package/src/components/common/Button/components/FileDownloadButton/fileDownloadButton.tsx +0 -27
- /package/lib/components/Table/components/{DownloadEntityResults/downloadEntityResults.styles.d.ts → TableFeatures/TableDownload/tableDownload.styles.d.ts} +0 -0
|
@@ -34,9 +34,11 @@ import {
|
|
|
34
34
|
sortingFn,
|
|
35
35
|
} from "../../Table/common/utils";
|
|
36
36
|
import { getFacetedMinMaxValues } from "../../Table/featureOptions/facetedColumn/getFacetedMinMaxValues";
|
|
37
|
+
import { TABLE_DOWNLOAD_OPTIONS } from "../../Table/featureOptions/tableDownload/constants";
|
|
37
38
|
import { ROW_POSITION } from "../../Table/features/RowPosition/constants";
|
|
38
39
|
import { ROW_PREVIEW } from "../../Table/features/RowPreview/constants";
|
|
39
40
|
import { RowPreviewState } from "../../Table/features/RowPreview/entities";
|
|
41
|
+
import { TABLE_DOWNLOAD } from "../../Table/features/TableDownload/constants";
|
|
40
42
|
import { buildBaseColumnDef } from "../../TableCreator/common/utils";
|
|
41
43
|
import { useTableOptions } from "../../TableCreator/options/hook";
|
|
42
44
|
import { createCell } from "./coreOptions/columns/cellFactory";
|
|
@@ -162,7 +164,7 @@ UseTableProps): UseTable<T> => {
|
|
|
162
164
|
* - This will simplify the configuration structure and centralize table state definitions, reducing redundancy and improving clarity.
|
|
163
165
|
*/
|
|
164
166
|
const table = useReactTable<T>({
|
|
165
|
-
_features: [ROW_POSITION, ROW_PREVIEW],
|
|
167
|
+
_features: [ROW_POSITION, ROW_PREVIEW, TABLE_DOWNLOAD],
|
|
166
168
|
columns: columnDefs,
|
|
167
169
|
data: listItems || [],
|
|
168
170
|
enableColumnFilters: true, // client-side filtering.
|
|
@@ -187,6 +189,7 @@ UseTableProps): UseTable<T> => {
|
|
|
187
189
|
onRowSelectionChange,
|
|
188
190
|
onSortingChange,
|
|
189
191
|
pageCount,
|
|
192
|
+
...TABLE_DOWNLOAD_OPTIONS,
|
|
190
193
|
state,
|
|
191
194
|
...tableOptions,
|
|
192
195
|
});
|
|
@@ -12,6 +12,7 @@ export const COLUMN_DEF: Record<string, ColumnDef<RowData>> = {
|
|
|
12
12
|
enableGrouping: false,
|
|
13
13
|
enableHiding: false,
|
|
14
14
|
enableSorting: false,
|
|
15
|
+
enableTableDownload: false,
|
|
15
16
|
header: "",
|
|
16
17
|
id: COLUMN_IDENTIFIER.ROW_POSITION,
|
|
17
18
|
meta: {
|
|
@@ -27,6 +28,7 @@ export const COLUMN_DEF: Record<string, ColumnDef<RowData>> = {
|
|
|
27
28
|
enableGrouping: false,
|
|
28
29
|
enableHiding: false,
|
|
29
30
|
enableSorting: false,
|
|
31
|
+
enableTableDownload: false,
|
|
30
32
|
header: HeadSelectionCell,
|
|
31
33
|
id: COLUMN_IDENTIFIER.ROW_SELECTION,
|
|
32
34
|
meta: {
|
|
@@ -11,7 +11,6 @@ import {
|
|
|
11
11
|
} from "@tanstack/react-table";
|
|
12
12
|
import { Category } from "../../../common/categories/models/types";
|
|
13
13
|
import { EXPLORE_MODE, ExploreMode } from "../../../hooks/useExploreMode/types";
|
|
14
|
-
import { COLUMN_IDENTIFIER } from "./columnIdentifier";
|
|
15
14
|
|
|
16
15
|
/**
|
|
17
16
|
* Internal model of a category term count keyed by category term.
|
|
@@ -24,11 +23,6 @@ type CountByTerms = Map<any, number>;
|
|
|
24
23
|
*/
|
|
25
24
|
type PinnedCell<T extends RowData> = [Cell<T, unknown>, number];
|
|
26
25
|
|
|
27
|
-
/**
|
|
28
|
-
* Model of possible table data values.
|
|
29
|
-
*/
|
|
30
|
-
type TableData = number | string | string[];
|
|
31
|
-
|
|
32
26
|
/**
|
|
33
27
|
* Build view-specific models from react table faceted values function.
|
|
34
28
|
* @param columns - Table columns.
|
|
@@ -100,49 +94,6 @@ export function getColumnHeader<T extends RowData>(column: Column<T>): string {
|
|
|
100
94
|
return meta?.header || id;
|
|
101
95
|
}
|
|
102
96
|
|
|
103
|
-
/**
|
|
104
|
-
* Format data to TSV string.
|
|
105
|
-
* @param data - Table data.
|
|
106
|
-
* @returns table data formatted into a TSV string.
|
|
107
|
-
*/
|
|
108
|
-
function formatDataToTSV(data: TableData[][]): string {
|
|
109
|
-
return data
|
|
110
|
-
.map((row) => {
|
|
111
|
-
return row
|
|
112
|
-
.map((data) => {
|
|
113
|
-
// Use empty string in place of undefined and null.
|
|
114
|
-
if (data === undefined || data === null) return "";
|
|
115
|
-
// Convert to string.
|
|
116
|
-
const dataString = Array.isArray(data)
|
|
117
|
-
? data.join(", ")
|
|
118
|
-
: String(data);
|
|
119
|
-
// Quote if necessary.
|
|
120
|
-
return /[\t\r\n"]/.test(dataString)
|
|
121
|
-
? `"${dataString.replaceAll('"', '""')}"`
|
|
122
|
-
: dataString;
|
|
123
|
-
})
|
|
124
|
-
.join("\t");
|
|
125
|
-
})
|
|
126
|
-
.join("\n");
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
/**
|
|
130
|
-
* Returns filtered entity results as a blob.
|
|
131
|
-
* @param rows - Table rows.
|
|
132
|
-
* @returns filtered entity results as a blob.
|
|
133
|
-
*/
|
|
134
|
-
export function generateDownloadBlob<T extends RowData>(
|
|
135
|
-
rows: Row<T>[]
|
|
136
|
-
): Blob | undefined {
|
|
137
|
-
if (rows.length === 0) {
|
|
138
|
-
return;
|
|
139
|
-
}
|
|
140
|
-
const tableHeaders = getHeadersTableData(rows);
|
|
141
|
-
const tableData = getRowsTableData(rows);
|
|
142
|
-
const tsv = formatDataToTSV([tableHeaders, ...tableData]);
|
|
143
|
-
return new Blob([tsv], { type: "text/tab-separated-values" });
|
|
144
|
-
}
|
|
145
|
-
|
|
146
97
|
/**
|
|
147
98
|
* Returns unique category term counts keyed by category terms.
|
|
148
99
|
* Custom function based off react table function getFacetedUniqueValues, see
|
|
@@ -225,32 +176,6 @@ export function getTableStatePagination(
|
|
|
225
176
|
};
|
|
226
177
|
}
|
|
227
178
|
|
|
228
|
-
/**
|
|
229
|
-
* Returns the list of table headers, excluding "rowSelection" column.
|
|
230
|
-
* @param rows - Table rows.
|
|
231
|
-
* @returns list of headers.
|
|
232
|
-
*/
|
|
233
|
-
function getHeadersTableData<T extends RowData>(rows: Row<T>[]): TableData[] {
|
|
234
|
-
return rows[0]
|
|
235
|
-
.getAllCells()
|
|
236
|
-
.filter((cell) => cell.column.id !== COLUMN_IDENTIFIER.ROW_SELECTION)
|
|
237
|
-
.map((cell) => cell.column.columnDef.header as TableData);
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
/**
|
|
241
|
-
* Returns the list of table data, excluding "rowSelection" column.
|
|
242
|
-
* @param rows - Table rows.
|
|
243
|
-
* @returns list of data.
|
|
244
|
-
*/
|
|
245
|
-
function getRowsTableData<T extends RowData>(rows: Row<T>[]): TableData[][] {
|
|
246
|
-
return rows.map((row) =>
|
|
247
|
-
row
|
|
248
|
-
.getAllCells()
|
|
249
|
-
.filter((cell) => cell.column.id !== COLUMN_IDENTIFIER.ROW_SELECTION)
|
|
250
|
-
.map((cell) => cell.getValue() as TableData)
|
|
251
|
-
);
|
|
252
|
-
}
|
|
253
|
-
|
|
254
179
|
/**
|
|
255
180
|
* Returns true if any or all table rows are selected.
|
|
256
181
|
* @param table - Table.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import styled from "@emotion/styled";
|
|
2
2
|
import { Button } from "@mui/material";
|
|
3
|
-
import { bpDownSm } from "
|
|
3
|
+
import { bpDownSm } from "../../../../../styles/common/mixins/breakpoints";
|
|
4
4
|
|
|
5
5
|
export const StyledButton = styled(Button)`
|
|
6
6
|
${bpDownSm} {
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { RowData } from "@tanstack/react-table";
|
|
2
|
+
import React from "react";
|
|
3
|
+
import { BUTTON_PROPS } from "../../../../common/Button/constants";
|
|
4
|
+
import { DownloadIcon } from "../../../../common/CustomIcon/components/DownloadIcon/downloadIcon";
|
|
5
|
+
import { StyledButton } from "./tableDownload.styles";
|
|
6
|
+
import { TableDownloadProps } from "./types";
|
|
7
|
+
|
|
8
|
+
export const TableDownload = <T extends RowData>({
|
|
9
|
+
className,
|
|
10
|
+
table,
|
|
11
|
+
}: TableDownloadProps<T>): JSX.Element | null => {
|
|
12
|
+
if (!table.options.enableTableDownload) return null;
|
|
13
|
+
return (
|
|
14
|
+
<StyledButton
|
|
15
|
+
{...BUTTON_PROPS.SECONDARY_CONTAINED}
|
|
16
|
+
className={className}
|
|
17
|
+
onClick={() => table.downloadData()}
|
|
18
|
+
startIcon={<DownloadIcon />}
|
|
19
|
+
>
|
|
20
|
+
Download TSV
|
|
21
|
+
</StyledButton>
|
|
22
|
+
);
|
|
23
|
+
};
|
|
@@ -5,8 +5,8 @@ import { ListViewConfig } from "../../../../config/entities";
|
|
|
5
5
|
import { useExploreState } from "../../../../hooks/useExploreState";
|
|
6
6
|
import { ViewToggle } from "../../../Index/components/EntityView/components/controls/ViewToggle/viewToggle";
|
|
7
7
|
import { isAnyRowSelected } from "../../common/utils";
|
|
8
|
-
import { DownloadEntityResults } from "../DownloadEntityResults/downloadEntityResults";
|
|
9
8
|
import { PaginationSummary } from "../PaginationSummary/paginationSummary";
|
|
9
|
+
import { TableDownload } from "../TableFeatures/TableDownload/tableDownload";
|
|
10
10
|
import { ColumnGrouping } from "./components/ColumnGrouping/columnGrouping";
|
|
11
11
|
import { ColumnVisibility } from "./components/ColumnVisibility/columnVisibility";
|
|
12
12
|
import { RowPreview } from "./components/RowPreview/rowPreview";
|
|
@@ -27,7 +27,7 @@ export const TableToolbar = <T extends RowData>({
|
|
|
27
27
|
const { paginationState } = exploreState;
|
|
28
28
|
const { currentPage, pages, pageSize, rows } = paginationState;
|
|
29
29
|
const { getSelectedRowModel } = tableInstance;
|
|
30
|
-
const {
|
|
30
|
+
const { rowPreviewView } = listView || {};
|
|
31
31
|
return (
|
|
32
32
|
<Fragment>
|
|
33
33
|
<StyledToolbar>
|
|
@@ -47,12 +47,7 @@ export const TableToolbar = <T extends RowData>({
|
|
|
47
47
|
)}
|
|
48
48
|
</Grid>
|
|
49
49
|
<Grid {...GRID_PROPS}>
|
|
50
|
-
{
|
|
51
|
-
<DownloadEntityResults
|
|
52
|
-
entityName={exploreState.tabValue}
|
|
53
|
-
rows={tableInstance.getFilteredRowModel().rows}
|
|
54
|
-
/>
|
|
55
|
-
)}
|
|
50
|
+
<TableDownload table={tableInstance} />
|
|
56
51
|
<ColumnGrouping tableInstance={tableInstance} />
|
|
57
52
|
<ColumnVisibility tableInstance={tableInstance} />
|
|
58
53
|
</Grid>
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import styled from "@emotion/styled";
|
|
2
|
+
import { Toolbar as MToolbar, Stack } from "@mui/material";
|
|
3
|
+
import { PALETTE } from "../../../../styles/common/constants/palette";
|
|
4
|
+
|
|
5
|
+
export const StyledToolbar = styled(MToolbar)`
|
|
6
|
+
align-items: center;
|
|
7
|
+
background-color: ${PALETTE.COMMON_WHITE};
|
|
8
|
+
display: flex;
|
|
9
|
+
padding: 16px;
|
|
10
|
+
`;
|
|
11
|
+
|
|
12
|
+
export const StyledStack = styled(Stack)`
|
|
13
|
+
align-items: center;
|
|
14
|
+
flex: 1;
|
|
15
|
+
flex-direction: row;
|
|
16
|
+
gap: 8px;
|
|
17
|
+
justify-content: flex-end;
|
|
18
|
+
`;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { RowData } from "@tanstack/react-table";
|
|
2
|
+
import React from "react";
|
|
3
|
+
import { TableDownload } from "../TableFeatures/TableDownload/tableDownload";
|
|
4
|
+
import { StyledStack, StyledToolbar } from "./tableToolbar2.styles";
|
|
5
|
+
import { TableToolbar2Props } from "./types";
|
|
6
|
+
|
|
7
|
+
export const TableToolbar2 = <T extends RowData>({
|
|
8
|
+
table,
|
|
9
|
+
}: TableToolbar2Props<T>): JSX.Element => {
|
|
10
|
+
return (
|
|
11
|
+
<StyledToolbar>
|
|
12
|
+
<StyledStack>
|
|
13
|
+
<TableDownload table={table} />
|
|
14
|
+
</StyledStack>
|
|
15
|
+
</StyledToolbar>
|
|
16
|
+
);
|
|
17
|
+
};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { RowData } from "@tanstack/react-table";
|
|
2
|
+
import { TableDownloadOptions } from "../../features/TableDownload/types";
|
|
3
|
+
|
|
4
|
+
export const TABLE_DOWNLOAD_OPTIONS: Pick<
|
|
5
|
+
TableDownloadOptions<RowData>,
|
|
6
|
+
"enableTableDownload"
|
|
7
|
+
> = {
|
|
8
|
+
enableTableDownload: false,
|
|
9
|
+
};
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Column,
|
|
3
|
+
RowData,
|
|
4
|
+
Table,
|
|
5
|
+
TableFeature,
|
|
6
|
+
TableOptionsResolved,
|
|
7
|
+
} from "@tanstack/react-table";
|
|
8
|
+
import { downloadData, getCanTableDownload, onTableDownload } from "./utils";
|
|
9
|
+
|
|
10
|
+
export const TABLE_DOWNLOAD: TableFeature = {
|
|
11
|
+
createColumn: <TData extends RowData, TValue>(
|
|
12
|
+
column: Column<TData, TValue>,
|
|
13
|
+
table: Table<TData>
|
|
14
|
+
): void => {
|
|
15
|
+
column.getCanTableDownload = (): boolean =>
|
|
16
|
+
getCanTableDownload(column, table);
|
|
17
|
+
},
|
|
18
|
+
createTable: <T extends RowData>(table: Table<T>): void => {
|
|
19
|
+
table.downloadData = (): void => downloadData(table);
|
|
20
|
+
},
|
|
21
|
+
getDefaultOptions: <T extends RowData>(): Partial<
|
|
22
|
+
TableOptionsResolved<T>
|
|
23
|
+
> => ({ enableTableDownload: false, onTableDownload }),
|
|
24
|
+
};
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { Column, RowData, Table } from "@tanstack/react-table";
|
|
2
|
+
import { getColumnHeader } from "../../../common/utils";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Format data to TSV string.
|
|
6
|
+
* @param data - Table data.
|
|
7
|
+
* @returns table data formatted into a TSV string.
|
|
8
|
+
*/
|
|
9
|
+
export function formatDataToTSV(data: unknown[][]): string {
|
|
10
|
+
return data
|
|
11
|
+
.map((row) => {
|
|
12
|
+
return row
|
|
13
|
+
.map((value) => {
|
|
14
|
+
// Use empty string in place of undefined and null.
|
|
15
|
+
if (value === undefined || value === null) return "";
|
|
16
|
+
|
|
17
|
+
// Convert unknown value to string.
|
|
18
|
+
let valueStr: string;
|
|
19
|
+
|
|
20
|
+
// Test if object / array.
|
|
21
|
+
if (typeof value === "object") {
|
|
22
|
+
if (Array.isArray(value)) {
|
|
23
|
+
// Handle array.
|
|
24
|
+
valueStr = value.join(", ") || "";
|
|
25
|
+
} else {
|
|
26
|
+
// Handle object.
|
|
27
|
+
valueStr = JSON.stringify(value);
|
|
28
|
+
}
|
|
29
|
+
} else {
|
|
30
|
+
// Handle primitive.
|
|
31
|
+
valueStr = String(value);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// Quote if necessary.
|
|
35
|
+
return /[\t\r\n"]/.test(valueStr)
|
|
36
|
+
? `"${valueStr.replaceAll('"', '""')}"`
|
|
37
|
+
: valueStr;
|
|
38
|
+
})
|
|
39
|
+
.join("\t");
|
|
40
|
+
})
|
|
41
|
+
.join("\n");
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export function getBlob<T extends RowData>(table: Table<T>): Blob | undefined {
|
|
45
|
+
const rows = table.getRowModel().rows;
|
|
46
|
+
|
|
47
|
+
if (rows.length === 0) return;
|
|
48
|
+
|
|
49
|
+
// Get downloadable columns based on getCanDownload property.
|
|
50
|
+
const downloadableColumns = getDownloadableColumns(table);
|
|
51
|
+
|
|
52
|
+
// Extract column IDs for filtering.
|
|
53
|
+
const downloadableColumnIds = downloadableColumns.map((column) => column.id);
|
|
54
|
+
|
|
55
|
+
// Get table headers from downloadable columns.
|
|
56
|
+
const tableHeaders = downloadableColumns.map(getColumnHeader);
|
|
57
|
+
|
|
58
|
+
// Get row data but only for downloadable columns.
|
|
59
|
+
const tableData = rows.map((row) =>
|
|
60
|
+
downloadableColumnIds.map((columnId) => row.getValue(columnId))
|
|
61
|
+
);
|
|
62
|
+
|
|
63
|
+
const tsv = formatDataToTSV([tableHeaders, ...tableData]);
|
|
64
|
+
return new Blob([tsv], { type: "text/tab-separated-values" });
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Filters table columns from column.getCanDownload API.
|
|
69
|
+
* @param table - Table.
|
|
70
|
+
* @returns Columns configured to be downloadable.
|
|
71
|
+
*/
|
|
72
|
+
export function getDownloadableColumns<T extends RowData>(
|
|
73
|
+
table: Table<T>
|
|
74
|
+
): Column<T>[] {
|
|
75
|
+
return table.getAllColumns().filter((column) => column.getCanTableDownload());
|
|
76
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { RowData, Table } from "@tanstack/react-table";
|
|
2
|
+
|
|
3
|
+
export interface TableDownloadColumn {
|
|
4
|
+
getCanTableDownload: () => boolean;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export interface TableDownloadColumnDef {
|
|
8
|
+
enableTableDownload?: boolean;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export interface TableDownloadInstance {
|
|
12
|
+
downloadData: () => void;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export interface TableDownloadOptions<T extends RowData> {
|
|
16
|
+
downloadFilename?: string;
|
|
17
|
+
enableTableDownload?: boolean;
|
|
18
|
+
onTableDownload?: (table: Table<T>) => void;
|
|
19
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { Column, RowData, Table } from "@tanstack/react-table";
|
|
2
|
+
import { getBlob } from "./onDownload/utils";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Function that downloads the table data as a TSV file.
|
|
6
|
+
* Uses the onDownload callback, or a custom download function if provided.
|
|
7
|
+
* @param table - Table.
|
|
8
|
+
*/
|
|
9
|
+
export function downloadData<T extends RowData>(table: Table<T>): void {
|
|
10
|
+
table.options.onTableDownload?.(table);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Returns true if column can be downloaded.
|
|
15
|
+
* @param column - Column.
|
|
16
|
+
* @param table - Table.
|
|
17
|
+
* @returns True if column can be downloaded.
|
|
18
|
+
*/
|
|
19
|
+
export function getCanTableDownload<T extends RowData, TValue>(
|
|
20
|
+
column: Column<T, TValue>,
|
|
21
|
+
table: Table<T>
|
|
22
|
+
): boolean {
|
|
23
|
+
return (
|
|
24
|
+
(column.columnDef.enableTableDownload ?? true) &&
|
|
25
|
+
(table.options.enableTableDownload ?? false)
|
|
26
|
+
);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Default download function that downloads the table data as a TSV file.
|
|
31
|
+
* @param table - Table.
|
|
32
|
+
*/
|
|
33
|
+
export function onTableDownload<T extends RowData>(table: Table<T>): void {
|
|
34
|
+
// Check if download is enabled.
|
|
35
|
+
if (!(table.options.enableTableDownload ?? false)) return;
|
|
36
|
+
|
|
37
|
+
// Generate the blob.
|
|
38
|
+
const blob = getBlob(table);
|
|
39
|
+
if (!blob) return;
|
|
40
|
+
|
|
41
|
+
// Create an object URL for the blob.
|
|
42
|
+
const url = URL.createObjectURL(blob);
|
|
43
|
+
|
|
44
|
+
// Create a download link.
|
|
45
|
+
const anchorEl = document.createElement("a");
|
|
46
|
+
anchorEl.href = url;
|
|
47
|
+
anchorEl.download = table.options.downloadFilename || "table-data.tsv";
|
|
48
|
+
|
|
49
|
+
// Append to the document body for better cross-browser support.
|
|
50
|
+
document.body.appendChild(anchorEl);
|
|
51
|
+
|
|
52
|
+
// Trigger the download.
|
|
53
|
+
anchorEl.click();
|
|
54
|
+
|
|
55
|
+
// Remove the element after triggering download.
|
|
56
|
+
document.body.removeChild(anchorEl);
|
|
57
|
+
|
|
58
|
+
// Clean up.
|
|
59
|
+
URL.revokeObjectURL(url);
|
|
60
|
+
}
|
|
@@ -6,9 +6,25 @@ import {
|
|
|
6
6
|
RowPreviewRow,
|
|
7
7
|
RowPreviewTableState,
|
|
8
8
|
} from "./RowPreview/entities";
|
|
9
|
+
import {
|
|
10
|
+
TableDownloadColumn,
|
|
11
|
+
TableDownloadInstance,
|
|
12
|
+
TableDownloadOptions,
|
|
13
|
+
} from "./TableDownload/types";
|
|
14
|
+
|
|
15
|
+
export type CustomFeatureColumn = TableDownloadColumn;
|
|
16
|
+
|
|
17
|
+
export interface CustomFeatureInstance<T extends RowData>
|
|
18
|
+
extends TableDownloadInstance,
|
|
19
|
+
RowPreviewInstance<T> {}
|
|
9
20
|
|
|
10
21
|
export type CustomFeatureInitialTableState = Partial<RowPreviewTableState>;
|
|
11
|
-
|
|
12
|
-
export
|
|
13
|
-
|
|
22
|
+
|
|
23
|
+
export interface CustomFeatureOptions<T extends RowData>
|
|
24
|
+
extends TableDownloadOptions<T>,
|
|
25
|
+
RowPositionOptions,
|
|
26
|
+
RowPreviewOptions {}
|
|
27
|
+
|
|
28
|
+
export interface CustomFeatureRow extends RowPositionRow, RowPreviewRow {}
|
|
29
|
+
|
|
14
30
|
export type CustomFeatureTableState = RowPreviewTableState;
|
package/src/config/entities.ts
CHANGED
|
@@ -277,7 +277,6 @@ export interface ListConfig<T extends RowData> {
|
|
|
277
277
|
*/
|
|
278
278
|
export interface ListViewConfig {
|
|
279
279
|
disablePagination?: boolean;
|
|
280
|
-
enableDownload?: boolean;
|
|
281
280
|
rowPreviewView?: ComponentsConfig; // Row preview view is expected to be a modal or drawer or similar.
|
|
282
281
|
rowSelectionView?: ComponentsConfig;
|
|
283
282
|
}
|
|
@@ -23,6 +23,7 @@ import { Components } from "rehype-react";
|
|
|
23
23
|
import { DataLayer } from "../src/common/analytics/entities";
|
|
24
24
|
import { DataDictionaryAnnotation } from "../src/common/entities";
|
|
25
25
|
import {
|
|
26
|
+
CustomFeatureColumn,
|
|
26
27
|
CustomFeatureInitialTableState,
|
|
27
28
|
CustomFeatureInstance,
|
|
28
29
|
CustomFeatureOptions,
|
|
@@ -244,6 +245,14 @@ declare module "@emotion/react" {
|
|
|
244
245
|
}
|
|
245
246
|
|
|
246
247
|
declare module "@tanstack/react-table" {
|
|
248
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars -- TData and TValue are unused variables.
|
|
249
|
+
interface Column<TData extends RowData, TValue> extends CustomFeatureColumn {}
|
|
250
|
+
|
|
251
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars -- TData and TValue are unused variables.
|
|
252
|
+
interface ColumnDefBase<TData extends RowData, TValue> {
|
|
253
|
+
enableTableDownload?: boolean;
|
|
254
|
+
}
|
|
255
|
+
|
|
247
256
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars -- TData and TValue are unused variables.
|
|
248
257
|
interface ColumnMeta<TData extends RowData, TValue> {
|
|
249
258
|
align?: TableCellProps["align"];
|
|
@@ -253,10 +262,13 @@ declare module "@tanstack/react-table" {
|
|
|
253
262
|
header?: string;
|
|
254
263
|
width?: GridTrackSize;
|
|
255
264
|
}
|
|
265
|
+
|
|
256
266
|
// eslint-disable-next-line @typescript-eslint/no-empty-interface -- empty interface is needed for extending.
|
|
257
267
|
interface InitialTableState extends CustomFeatureInitialTableState {}
|
|
268
|
+
|
|
258
269
|
// eslint-disable-next-line @typescript-eslint/no-empty-interface, @typescript-eslint/no-unused-vars -- empty interface is needed for extending, TData is an unused variable.
|
|
259
270
|
interface Row<TData extends RowData> extends CustomFeatureRow {}
|
|
271
|
+
|
|
260
272
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars -- TData is an unused variables.
|
|
261
273
|
interface SortingOptions<TData extends RowData> {
|
|
262
274
|
/**
|
|
@@ -265,11 +277,13 @@ declare module "@tanstack/react-table" {
|
|
|
265
277
|
*/
|
|
266
278
|
enableSortingInteraction?: boolean;
|
|
267
279
|
}
|
|
280
|
+
|
|
268
281
|
// eslint-disable-next-line @typescript-eslint/no-empty-interface -- empty interface is needed for extending.
|
|
269
282
|
interface Table<TData extends RowData> extends CustomFeatureInstance<TData> {}
|
|
270
|
-
|
|
283
|
+
|
|
271
284
|
interface TableOptionsResolved<TData extends RowData>
|
|
272
|
-
extends CustomFeatureOptions {}
|
|
285
|
+
extends CustomFeatureOptions<TData> {}
|
|
286
|
+
|
|
273
287
|
// eslint-disable-next-line @typescript-eslint/no-empty-interface -- empty interface is needed for extending.
|
|
274
288
|
interface TableState extends CustomFeatureTableState {}
|
|
275
289
|
}
|
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
import { Row, RowData } from "@tanstack/react-table";
|
|
2
|
-
export interface DownloadEntityResultsProps<T extends RowData> {
|
|
3
|
-
entityName: string;
|
|
4
|
-
rows: Row<T>[];
|
|
5
|
-
}
|
|
6
|
-
export declare const DownloadEntityResults: <T extends RowData>({ entityName, rows, }: DownloadEntityResultsProps<T>) => JSX.Element;
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
import React from "react";
|
|
2
|
-
import { FileDownloadButton } from "../../../common/Button/components/FileDownloadButton/fileDownloadButton";
|
|
3
|
-
import { BUTTON_PROPS } from "../../../common/Button/constants";
|
|
4
|
-
import { DownloadIcon } from "../../../common/CustomIcon/components/DownloadIcon/downloadIcon";
|
|
5
|
-
import { generateDownloadBlob } from "../../common/utils";
|
|
6
|
-
import { StyledButton } from "./downloadEntityResults.styles";
|
|
7
|
-
export const DownloadEntityResults = ({ entityName, rows, }) => {
|
|
8
|
-
const [fileUrl, setFileUrl] = React.useState(undefined);
|
|
9
|
-
const onDownload = () => {
|
|
10
|
-
const blob = generateDownloadBlob(rows);
|
|
11
|
-
if (blob) {
|
|
12
|
-
setFileUrl(window.URL.createObjectURL(blob));
|
|
13
|
-
}
|
|
14
|
-
};
|
|
15
|
-
return (React.createElement(React.Fragment, null,
|
|
16
|
-
React.createElement(StyledButton, { ...BUTTON_PROPS.SECONDARY_CONTAINED, onClick: () => onDownload(), startIcon: React.createElement(DownloadIcon, null) }, "Download TSV"),
|
|
17
|
-
React.createElement(FileDownloadButton, { fileName: `${entityName}.tsv`, fileUrl: fileUrl })));
|
|
18
|
-
};
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
import { Box } from "@mui/material";
|
|
2
|
-
import React, { useEffect, useRef } from "react";
|
|
3
|
-
export const FileDownloadButton = ({ fileName, fileUrl, }) => {
|
|
4
|
-
const downloadRef = useRef(null);
|
|
5
|
-
// Initiates file download when file url request is successful.
|
|
6
|
-
useEffect(() => {
|
|
7
|
-
if (downloadRef.current && fileName && fileUrl) {
|
|
8
|
-
downloadRef.current.setAttribute("href", fileUrl);
|
|
9
|
-
downloadRef.current.setAttribute("download", fileName);
|
|
10
|
-
downloadRef.current.click();
|
|
11
|
-
}
|
|
12
|
-
}, [fileName, fileUrl]);
|
|
13
|
-
return (React.createElement(Box, { component: "a", download: true, ref: downloadRef, sx: { display: "none" } }));
|
|
14
|
-
};
|
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
import { Row, RowData } from "@tanstack/react-table";
|
|
2
|
-
import React from "react";
|
|
3
|
-
import { FileDownloadButton } from "../../../common/Button/components/FileDownloadButton/fileDownloadButton";
|
|
4
|
-
import { BUTTON_PROPS } from "../../../common/Button/constants";
|
|
5
|
-
import { DownloadIcon } from "../../../common/CustomIcon/components/DownloadIcon/downloadIcon";
|
|
6
|
-
import { generateDownloadBlob } from "../../common/utils";
|
|
7
|
-
import { StyledButton } from "./downloadEntityResults.styles";
|
|
8
|
-
|
|
9
|
-
export interface DownloadEntityResultsProps<T extends RowData> {
|
|
10
|
-
entityName: string;
|
|
11
|
-
rows: Row<T>[];
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
export const DownloadEntityResults = <T extends RowData>({
|
|
15
|
-
entityName,
|
|
16
|
-
rows,
|
|
17
|
-
}: DownloadEntityResultsProps<T>): JSX.Element => {
|
|
18
|
-
const [fileUrl, setFileUrl] = React.useState<string | undefined>(undefined);
|
|
19
|
-
|
|
20
|
-
const onDownload = (): void => {
|
|
21
|
-
const blob = generateDownloadBlob(rows);
|
|
22
|
-
if (blob) {
|
|
23
|
-
setFileUrl(window.URL.createObjectURL(blob));
|
|
24
|
-
}
|
|
25
|
-
};
|
|
26
|
-
|
|
27
|
-
return (
|
|
28
|
-
<>
|
|
29
|
-
<StyledButton
|
|
30
|
-
{...BUTTON_PROPS.SECONDARY_CONTAINED}
|
|
31
|
-
onClick={(): void => onDownload()}
|
|
32
|
-
startIcon={<DownloadIcon />}
|
|
33
|
-
>
|
|
34
|
-
Download TSV
|
|
35
|
-
</StyledButton>
|
|
36
|
-
<FileDownloadButton fileName={`${entityName}.tsv`} fileUrl={fileUrl} />
|
|
37
|
-
</>
|
|
38
|
-
);
|
|
39
|
-
};
|