@lminii/material-react-table-mcp 4.0.1
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/README.md +68 -0
- package/data/MIGRATION.md +136 -0
- package/data/api.json +5379 -0
- package/data/examples/advanced.js +215 -0
- package/data/examples/advanced.tsx +309 -0
- package/data/examples/aggregation-and-grouping.js +116 -0
- package/data/examples/aggregation-and-grouping.tsx +153 -0
- package/data/examples/aggregation-multi.js +89 -0
- package/data/examples/aggregation-multi.tsx +105 -0
- package/data/examples/alternate-column-filtering.js +38 -0
- package/data/examples/alternate-column-filtering.tsx +49 -0
- package/data/examples/alternate-detail-panel.js +65 -0
- package/data/examples/alternate-detail-panel.tsx +81 -0
- package/data/examples/alternate-pagination.js +17 -0
- package/data/examples/alternate-pagination.tsx +23 -0
- package/data/examples/basic.js +86 -0
- package/data/examples/basic.tsx +109 -0
- package/data/examples/chart-detail-panel.js +69 -0
- package/data/examples/chart-detail-panel.tsx +89 -0
- package/data/examples/column-actions-space.js +34 -0
- package/data/examples/column-actions-space.tsx +44 -0
- package/data/examples/column-alignment.js +58 -0
- package/data/examples/column-alignment.tsx +70 -0
- package/data/examples/custom-column-actions.js +74 -0
- package/data/examples/custom-column-actions.tsx +109 -0
- package/data/examples/custom-column-filtering-ui.js +59 -0
- package/data/examples/custom-column-filtering-ui.tsx +89 -0
- package/data/examples/custom-headless.js +85 -0
- package/data/examples/custom-headless.tsx +125 -0
- package/data/examples/custom-top-toolbar.js +60 -0
- package/data/examples/custom-top-toolbar.tsx +88 -0
- package/data/examples/customize-display-columns.js +72 -0
- package/data/examples/customize-display-columns.tsx +85 -0
- package/data/examples/customize-filter-components.js +78 -0
- package/data/examples/customize-filter-components.tsx +101 -0
- package/data/examples/customize-filter-modes.js +59 -0
- package/data/examples/customize-filter-modes.tsx +73 -0
- package/data/examples/customize-filter-variants.js +98 -0
- package/data/examples/customize-filter-variants.tsx +116 -0
- package/data/examples/customize-global-filter-component.js +37 -0
- package/data/examples/customize-global-filter-component.tsx +52 -0
- package/data/examples/customize-remove-column-grouping.js +75 -0
- package/data/examples/customize-remove-column-grouping.tsx +94 -0
- package/data/examples/customize-row-selection.js +43 -0
- package/data/examples/customize-row-selection.tsx +54 -0
- package/data/examples/customize-table-styles.js +81 -0
- package/data/examples/customize-table-styles.tsx +100 -0
- package/data/examples/disable-column-actions.js +26 -0
- package/data/examples/disable-column-actions.tsx +37 -0
- package/data/examples/disable-column-hiding.js +74 -0
- package/data/examples/disable-column-hiding.tsx +90 -0
- package/data/examples/disable-density-toggle.js +37 -0
- package/data/examples/disable-density-toggle.tsx +49 -0
- package/data/examples/dynamic-columns.js +124 -0
- package/data/examples/dynamic-columns.tsx +189 -0
- package/data/examples/editing-crud-cell.js +288 -0
- package/data/examples/editing-crud-cell.tsx +377 -0
- package/data/examples/editing-crud-modal.js +275 -0
- package/data/examples/editing-crud-modal.tsx +370 -0
- package/data/examples/editing-crud-row.js +255 -0
- package/data/examples/editing-crud-row.tsx +333 -0
- package/data/examples/editing-crud-table.js +280 -0
- package/data/examples/editing-crud-table.tsx +369 -0
- package/data/examples/editing-crud-tree.js +328 -0
- package/data/examples/editing-crud-tree.tsx +416 -0
- package/data/examples/enable-cell-actions.js +52 -0
- package/data/examples/enable-cell-actions.tsx +76 -0
- package/data/examples/enable-click-to-copy.js +33 -0
- package/data/examples/enable-click-to-copy.tsx +39 -0
- package/data/examples/enable-column-grouping.js +69 -0
- package/data/examples/enable-column-grouping.tsx +120 -0
- package/data/examples/enable-column-ordering.js +37 -0
- package/data/examples/enable-column-ordering.tsx +48 -0
- package/data/examples/enable-column-pinning.js +58 -0
- package/data/examples/enable-column-pinning.tsx +69 -0
- package/data/examples/enable-column-resizing.js +45 -0
- package/data/examples/enable-column-resizing.tsx +56 -0
- package/data/examples/enable-column-virtualization.js +19 -0
- package/data/examples/enable-column-virtualization.tsx +27 -0
- package/data/examples/enable-detail-panel-conditionally.js +61 -0
- package/data/examples/enable-detail-panel-conditionally.tsx +78 -0
- package/data/examples/enable-detail-panel-virtualized.js +64 -0
- package/data/examples/enable-detail-panel-virtualized.tsx +80 -0
- package/data/examples/enable-expanding-tree.js +87 -0
- package/data/examples/enable-expanding-tree.tsx +110 -0
- package/data/examples/enable-filter-facet-values.js +52 -0
- package/data/examples/enable-filter-facet-values.tsx +65 -0
- package/data/examples/enable-row-dragging.js +102 -0
- package/data/examples/enable-row-dragging.tsx +137 -0
- package/data/examples/enable-row-numbers-original.js +37 -0
- package/data/examples/enable-row-numbers-original.tsx +48 -0
- package/data/examples/enable-row-numbers-static.js +37 -0
- package/data/examples/enable-row-numbers-static.tsx +48 -0
- package/data/examples/enable-row-ordering.js +40 -0
- package/data/examples/enable-row-ordering.tsx +57 -0
- package/data/examples/enable-row-pinning-select.js +64 -0
- package/data/examples/enable-row-pinning-select.tsx +76 -0
- package/data/examples/enable-row-pinning-static.js +39 -0
- package/data/examples/enable-row-pinning-static.tsx +50 -0
- package/data/examples/enable-row-pinning-sticky.js +57 -0
- package/data/examples/enable-row-pinning-sticky.tsx +69 -0
- package/data/examples/enable-row-selection.js +71 -0
- package/data/examples/enable-row-selection.tsx +96 -0
- package/data/examples/enable-row-virtualization.js +85 -0
- package/data/examples/enable-row-virtualization.tsx +101 -0
- package/data/examples/enable-sticky-header.js +47 -0
- package/data/examples/enable-sticky-header.tsx +59 -0
- package/data/examples/expanding-tree-expanded.js +98 -0
- package/data/examples/expanding-tree-expanded.tsx +121 -0
- package/data/examples/expanding-tree-flat-parse.js +102 -0
- package/data/examples/expanding-tree-flat-parse.tsx +125 -0
- package/data/examples/expanding-tree-root-expanded.js +120 -0
- package/data/examples/expanding-tree-root-expanded.tsx +151 -0
- package/data/examples/export-to-csv.js +84 -0
- package/data/examples/export-to-csv.tsx +114 -0
- package/data/examples/export-to-pdf.js +76 -0
- package/data/examples/export-to-pdf.tsx +104 -0
- package/data/examples/external-toolbar.js +82 -0
- package/data/examples/external-toolbar.tsx +108 -0
- package/data/examples/font-awesome-icons.js +62 -0
- package/data/examples/font-awesome-icons.tsx +106 -0
- package/data/examples/infinite-scrolling.js +137 -0
- package/data/examples/infinite-scrolling.tsx +208 -0
- package/data/examples/lazy-detail-panel.js +164 -0
- package/data/examples/lazy-detail-panel.tsx +262 -0
- package/data/examples/lazy-sub-rows.js +118 -0
- package/data/examples/lazy-sub-rows.tsx +190 -0
- package/data/examples/linear-progress.js +48 -0
- package/data/examples/linear-progress.tsx +66 -0
- package/data/examples/loading.js +34 -0
- package/data/examples/loading.tsx +50 -0
- package/data/examples/localization-i18n-ar.js +40 -0
- package/data/examples/localization-i18n-ar.tsx +65 -0
- package/data/examples/localization-i18n-az.js +38 -0
- package/data/examples/localization-i18n-az.tsx +60 -0
- package/data/examples/localization-i18n-bg.js +38 -0
- package/data/examples/localization-i18n-bg.tsx +60 -0
- package/data/examples/localization-i18n-cs.js +38 -0
- package/data/examples/localization-i18n-cs.tsx +60 -0
- package/data/examples/localization-i18n-da.js +38 -0
- package/data/examples/localization-i18n-da.tsx +60 -0
- package/data/examples/localization-i18n-de.js +38 -0
- package/data/examples/localization-i18n-de.tsx +60 -0
- package/data/examples/localization-i18n-el.js +38 -0
- package/data/examples/localization-i18n-el.tsx +60 -0
- package/data/examples/localization-i18n-en.js +38 -0
- package/data/examples/localization-i18n-en.tsx +60 -0
- package/data/examples/localization-i18n-es.js +38 -0
- package/data/examples/localization-i18n-es.tsx +60 -0
- package/data/examples/localization-i18n-et.js +38 -0
- package/data/examples/localization-i18n-et.tsx +60 -0
- package/data/examples/localization-i18n-fa.js +40 -0
- package/data/examples/localization-i18n-fa.tsx +66 -0
- package/data/examples/localization-i18n-fi.js +38 -0
- package/data/examples/localization-i18n-fi.tsx +60 -0
- package/data/examples/localization-i18n-fr.js +38 -0
- package/data/examples/localization-i18n-fr.tsx +60 -0
- package/data/examples/localization-i18n-he.js +40 -0
- package/data/examples/localization-i18n-he.tsx +66 -0
- package/data/examples/localization-i18n-hr.js +38 -0
- package/data/examples/localization-i18n-hr.tsx +60 -0
- package/data/examples/localization-i18n-hu.js +38 -0
- package/data/examples/localization-i18n-hu.tsx +60 -0
- package/data/examples/localization-i18n-hy.js +40 -0
- package/data/examples/localization-i18n-hy.tsx +65 -0
- package/data/examples/localization-i18n-id.js +38 -0
- package/data/examples/localization-i18n-id.tsx +60 -0
- package/data/examples/localization-i18n-it.js +38 -0
- package/data/examples/localization-i18n-it.tsx +60 -0
- package/data/examples/localization-i18n-ja.js +38 -0
- package/data/examples/localization-i18n-ja.tsx +60 -0
- package/data/examples/localization-i18n-ko.js +38 -0
- package/data/examples/localization-i18n-ko.tsx +60 -0
- package/data/examples/localization-i18n-nl.js +38 -0
- package/data/examples/localization-i18n-nl.tsx +60 -0
- package/data/examples/localization-i18n-no.js +38 -0
- package/data/examples/localization-i18n-no.tsx +60 -0
- package/data/examples/localization-i18n-np.js +38 -0
- package/data/examples/localization-i18n-np.tsx +60 -0
- package/data/examples/localization-i18n-pl.js +38 -0
- package/data/examples/localization-i18n-pl.tsx +60 -0
- package/data/examples/localization-i18n-pt-BR.js +38 -0
- package/data/examples/localization-i18n-pt-BR.tsx +60 -0
- package/data/examples/localization-i18n-pt.js +38 -0
- package/data/examples/localization-i18n-pt.tsx +60 -0
- package/data/examples/localization-i18n-ro.js +38 -0
- package/data/examples/localization-i18n-ro.tsx +60 -0
- package/data/examples/localization-i18n-ru.js +38 -0
- package/data/examples/localization-i18n-ru.tsx +60 -0
- package/data/examples/localization-i18n-sk.js +38 -0
- package/data/examples/localization-i18n-sk.tsx +60 -0
- package/data/examples/localization-i18n-sr-Cyrl-RS.js +38 -0
- package/data/examples/localization-i18n-sr-Cyrl-RS.tsx +60 -0
- package/data/examples/localization-i18n-sr-Latn-RS.js +38 -0
- package/data/examples/localization-i18n-sr-Latn-RS.tsx +60 -0
- package/data/examples/localization-i18n-sv.js +38 -0
- package/data/examples/localization-i18n-sv.tsx +60 -0
- package/data/examples/localization-i18n-tr.js +38 -0
- package/data/examples/localization-i18n-tr.tsx +60 -0
- package/data/examples/localization-i18n-uk.js +38 -0
- package/data/examples/localization-i18n-uk.tsx +60 -0
- package/data/examples/localization-i18n-vi.js +38 -0
- package/data/examples/localization-i18n-vi.tsx +60 -0
- package/data/examples/localization-i18n-zh-hans.js +38 -0
- package/data/examples/localization-i18n-zh-hans.tsx +60 -0
- package/data/examples/localization-i18n-zh-hant.js +38 -0
- package/data/examples/localization-i18n-zh-hant.tsx +60 -0
- package/data/examples/manual-selection.js +81 -0
- package/data/examples/manual-selection.tsx +105 -0
- package/data/examples/minimal.js +66 -0
- package/data/examples/minimal.tsx +78 -0
- package/data/examples/mui-theme.js +109 -0
- package/data/examples/mui-theme.tsx +136 -0
- package/data/examples/multi-sorting.js +48 -0
- package/data/examples/multi-sorting.tsx +58 -0
- package/data/examples/persistent-state.js +126 -0
- package/data/examples/persistent-state.tsx +184 -0
- package/data/examples/react-query.js +122 -0
- package/data/examples/react-query.tsx +182 -0
- package/data/examples/remote.js +113 -0
- package/data/examples/remote.tsx +150 -0
- package/data/examples/row-actions-buttons.js +54 -0
- package/data/examples/row-actions-buttons.tsx +89 -0
- package/data/examples/row-actions-menu-items.js +36 -0
- package/data/examples/row-actions-menu-items.tsx +64 -0
- package/data/examples/single-row-selection.js +74 -0
- package/data/examples/single-row-selection.tsx +98 -0
- package/data/examples/virtualized.js +122 -0
- package/data/examples/virtualized.tsx +138 -0
- package/data/guides/accessibility.md +119 -0
- package/data/guides/aggregation.md +210 -0
- package/data/guides/async-loading.md +132 -0
- package/data/guides/best-practices.md +363 -0
- package/data/guides/cell-actions.md +139 -0
- package/data/guides/click-to-copy.md +67 -0
- package/data/guides/column-actions.md +69 -0
- package/data/guides/column-filtering.md +366 -0
- package/data/guides/column-grouping.md +192 -0
- package/data/guides/column-hiding.md +133 -0
- package/data/guides/column-ordering-dnd.md +99 -0
- package/data/guides/column-pinning.md +72 -0
- package/data/guides/column-resizing.md +157 -0
- package/data/guides/column-size.md +162 -0
- package/data/guides/customize-components.md +395 -0
- package/data/guides/customize-icons.md +35 -0
- package/data/guides/data-columns.md +292 -0
- package/data/guides/density-toggle.md +46 -0
- package/data/guides/detail-panel.md +195 -0
- package/data/guides/display-columns.md +135 -0
- package/data/guides/editing.md +367 -0
- package/data/guides/expanding-sub-rows.md +194 -0
- package/data/guides/full-screen-toggle.md +63 -0
- package/data/guides/global-filtering.md +237 -0
- package/data/guides/localization.md +57 -0
- package/data/guides/memoization.md +157 -0
- package/data/guides/pagination.md +171 -0
- package/data/guides/row-actions.md +116 -0
- package/data/guides/row-numbers.md +24 -0
- package/data/guides/row-ordering-dnd.md +51 -0
- package/data/guides/row-pinning.md +96 -0
- package/data/guides/row-selection.md +237 -0
- package/data/guides/sorting.md +190 -0
- package/data/guides/state-management.md +165 -0
- package/data/guides/sticky-header.md +41 -0
- package/data/guides/table-event-listeners.md +103 -0
- package/data/guides/toolbar-customization.md +251 -0
- package/data/guides/virtualization.md +183 -0
- package/data/index.json +1930 -0
- package/data/reference/mrt-components.md +183 -0
- package/data/reference/mrt-hooks.md +125 -0
- package/data/skills/README.md +81 -0
- package/data/skills/composable-components/SKILL.md +189 -0
- package/data/skills/customization/SKILL.md +190 -0
- package/data/skills/drag-and-drop-ordering/SKILL.md +227 -0
- package/data/skills/editing/SKILL.md +243 -0
- package/data/skills/filtering/SKILL.md +213 -0
- package/data/skills/getting-started/SKILL.md +213 -0
- package/data/skills/localization/SKILL.md +182 -0
- package/data/skills/migrate-v3-to-v4/SKILL.md +161 -0
- package/data/skills/state-and-server-data/SKILL.md +200 -0
- package/data/skills/virtualization/SKILL.md +203 -0
- package/dist/data.js +159 -0
- package/dist/index.js +244 -0
- package/package.json +50 -0
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
import {
|
|
2
|
+
lazy,
|
|
3
|
+
Suspense,
|
|
4
|
+
type UIEvent,
|
|
5
|
+
useCallback,
|
|
6
|
+
useEffect,
|
|
7
|
+
useMemo,
|
|
8
|
+
useRef,
|
|
9
|
+
useState,
|
|
10
|
+
} from 'react';
|
|
11
|
+
import {
|
|
12
|
+
MaterialReactTable,
|
|
13
|
+
useMaterialReactTable,
|
|
14
|
+
type MRT_ColumnDef,
|
|
15
|
+
type MRT_ColumnFiltersState,
|
|
16
|
+
type MRT_SortingState,
|
|
17
|
+
type MRT_RowVirtualizer,
|
|
18
|
+
} from 'material-react-table';
|
|
19
|
+
import { Typography } from '@mui/material';
|
|
20
|
+
import {
|
|
21
|
+
QueryClient,
|
|
22
|
+
QueryClientProvider,
|
|
23
|
+
useInfiniteQuery,
|
|
24
|
+
} from '@tanstack/react-query'; //Note: this is TanStack React Query V5
|
|
25
|
+
|
|
26
|
+
//Your API response shape will probably be different. Knowing a total row count is important though.
|
|
27
|
+
type UserApiResponse = {
|
|
28
|
+
data: Array<User>;
|
|
29
|
+
meta: {
|
|
30
|
+
totalRowCount: number;
|
|
31
|
+
};
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
type User = {
|
|
35
|
+
firstName: string;
|
|
36
|
+
lastName: string;
|
|
37
|
+
address: string;
|
|
38
|
+
state: string;
|
|
39
|
+
phoneNumber: string;
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
const columns: MRT_ColumnDef<User>[] = [
|
|
43
|
+
{
|
|
44
|
+
accessorKey: 'firstName',
|
|
45
|
+
header: 'First Name',
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
accessorKey: 'lastName',
|
|
49
|
+
header: 'Last Name',
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
accessorKey: 'address',
|
|
53
|
+
header: 'Address',
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
accessorKey: 'state',
|
|
57
|
+
header: 'State',
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
accessorKey: 'phoneNumber',
|
|
61
|
+
header: 'Phone Number',
|
|
62
|
+
},
|
|
63
|
+
];
|
|
64
|
+
|
|
65
|
+
const fetchSize = 25;
|
|
66
|
+
|
|
67
|
+
const Example = () => {
|
|
68
|
+
const tableContainerRef = useRef<HTMLDivElement>(null); //we can get access to the underlying TableContainer element and react to its scroll events
|
|
69
|
+
const rowVirtualizerInstanceRef = useRef<MRT_RowVirtualizer>(null); //we can get access to the underlying Virtualizer instance and call its scrollToIndex method
|
|
70
|
+
|
|
71
|
+
const [columnFilters, setColumnFilters] = useState<MRT_ColumnFiltersState>(
|
|
72
|
+
[],
|
|
73
|
+
);
|
|
74
|
+
const [globalFilter, setGlobalFilter] = useState<string>();
|
|
75
|
+
const [sorting, setSorting] = useState<MRT_SortingState>([]);
|
|
76
|
+
|
|
77
|
+
const { data, fetchNextPage, isError, isFetching, isLoading } =
|
|
78
|
+
useInfiniteQuery<UserApiResponse>({
|
|
79
|
+
queryKey: [
|
|
80
|
+
'users-list',
|
|
81
|
+
{
|
|
82
|
+
columnFilters, //refetch when columnFilters changes
|
|
83
|
+
globalFilter, //refetch when globalFilter changes
|
|
84
|
+
sorting, //refetch when sorting changes
|
|
85
|
+
},
|
|
86
|
+
],
|
|
87
|
+
queryFn: async ({ pageParam }) => {
|
|
88
|
+
const url = new URL('/api/data', location.origin); // nextjs api route
|
|
89
|
+
url.searchParams.set('start', `${(pageParam as number) * fetchSize}`);
|
|
90
|
+
url.searchParams.set('size', `${fetchSize}`);
|
|
91
|
+
url.searchParams.set('filters', JSON.stringify(columnFilters ?? []));
|
|
92
|
+
url.searchParams.set('globalFilter', globalFilter ?? '');
|
|
93
|
+
url.searchParams.set('sorting', JSON.stringify(sorting ?? []));
|
|
94
|
+
|
|
95
|
+
const response = await fetch(url.href);
|
|
96
|
+
const json = (await response.json()) as UserApiResponse;
|
|
97
|
+
return json;
|
|
98
|
+
},
|
|
99
|
+
initialPageParam: 0,
|
|
100
|
+
getNextPageParam: (_lastGroup, groups) => groups.length,
|
|
101
|
+
refetchOnWindowFocus: false,
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
const flatData = useMemo(
|
|
105
|
+
() => data?.pages.flatMap((page) => page.data) ?? [],
|
|
106
|
+
[data],
|
|
107
|
+
);
|
|
108
|
+
|
|
109
|
+
const totalDBRowCount = data?.pages?.[0]?.meta?.totalRowCount ?? 0;
|
|
110
|
+
const totalFetched = flatData.length;
|
|
111
|
+
|
|
112
|
+
//called on scroll and possibly on mount to fetch more data as the user scrolls and reaches bottom of table
|
|
113
|
+
const fetchMoreOnBottomReached = useCallback(
|
|
114
|
+
(containerRefElement?: HTMLDivElement | null) => {
|
|
115
|
+
if (containerRefElement) {
|
|
116
|
+
const { scrollHeight, scrollTop, clientHeight } = containerRefElement;
|
|
117
|
+
//once the user has scrolled within 400px of the bottom of the table, fetch more data if we can
|
|
118
|
+
if (
|
|
119
|
+
scrollHeight - scrollTop - clientHeight < 400 &&
|
|
120
|
+
!isFetching &&
|
|
121
|
+
totalFetched < totalDBRowCount
|
|
122
|
+
) {
|
|
123
|
+
fetchNextPage();
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
},
|
|
127
|
+
[fetchNextPage, isFetching, totalFetched, totalDBRowCount],
|
|
128
|
+
);
|
|
129
|
+
|
|
130
|
+
//scroll to top of table when sorting or filters change
|
|
131
|
+
useEffect(() => {
|
|
132
|
+
//scroll to the top of the table when the sorting changes
|
|
133
|
+
try {
|
|
134
|
+
rowVirtualizerInstanceRef.current?.scrollToIndex?.(0);
|
|
135
|
+
} catch (error) {
|
|
136
|
+
console.error(error);
|
|
137
|
+
}
|
|
138
|
+
}, [sorting, columnFilters, globalFilter]);
|
|
139
|
+
|
|
140
|
+
//a check on mount to see if the table is already scrolled to the bottom and immediately needs to fetch more data
|
|
141
|
+
useEffect(() => {
|
|
142
|
+
fetchMoreOnBottomReached(tableContainerRef.current);
|
|
143
|
+
}, [fetchMoreOnBottomReached]);
|
|
144
|
+
|
|
145
|
+
const table = useMaterialReactTable({
|
|
146
|
+
columns,
|
|
147
|
+
data: flatData,
|
|
148
|
+
enablePagination: false,
|
|
149
|
+
enableRowNumbers: true,
|
|
150
|
+
enableRowVirtualization: true,
|
|
151
|
+
manualFiltering: true,
|
|
152
|
+
manualSorting: true,
|
|
153
|
+
muiTableContainerProps: {
|
|
154
|
+
ref: tableContainerRef, //get access to the table container element
|
|
155
|
+
sx: { maxHeight: '600px' }, //give the table a max height
|
|
156
|
+
onScroll: (event: UIEvent<HTMLDivElement>) =>
|
|
157
|
+
fetchMoreOnBottomReached(event.target as HTMLDivElement), //add an event listener to the table container element
|
|
158
|
+
},
|
|
159
|
+
muiToolbarAlertBannerProps: isError
|
|
160
|
+
? {
|
|
161
|
+
color: 'error',
|
|
162
|
+
children: 'Error loading data',
|
|
163
|
+
}
|
|
164
|
+
: undefined,
|
|
165
|
+
onColumnFiltersChange: setColumnFilters,
|
|
166
|
+
onGlobalFilterChange: setGlobalFilter,
|
|
167
|
+
onSortingChange: setSorting,
|
|
168
|
+
renderBottomToolbarCustomActions: () => (
|
|
169
|
+
<Typography>
|
|
170
|
+
Fetched {totalFetched} of {totalDBRowCount} total rows.
|
|
171
|
+
</Typography>
|
|
172
|
+
),
|
|
173
|
+
state: {
|
|
174
|
+
columnFilters,
|
|
175
|
+
globalFilter,
|
|
176
|
+
isLoading,
|
|
177
|
+
showAlertBanner: isError,
|
|
178
|
+
showProgressBars: isFetching,
|
|
179
|
+
sorting,
|
|
180
|
+
},
|
|
181
|
+
rowVirtualizerInstanceRef, //get access to the virtualizer instance
|
|
182
|
+
rowVirtualizerOptions: { overscan: 4 },
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
return <MaterialReactTable table={table} />;
|
|
186
|
+
};
|
|
187
|
+
|
|
188
|
+
//react query setup in App.tsx
|
|
189
|
+
const ReactQueryDevtoolsProduction = lazy(() =>
|
|
190
|
+
import('@tanstack/react-query-devtools/build/modern/production.js').then(
|
|
191
|
+
(d) => ({
|
|
192
|
+
default: d.ReactQueryDevtools,
|
|
193
|
+
}),
|
|
194
|
+
),
|
|
195
|
+
);
|
|
196
|
+
|
|
197
|
+
const queryClient = new QueryClient();
|
|
198
|
+
|
|
199
|
+
export default function App() {
|
|
200
|
+
return (
|
|
201
|
+
<QueryClientProvider client={queryClient}>
|
|
202
|
+
<Example />
|
|
203
|
+
<Suspense fallback={null}>
|
|
204
|
+
<ReactQueryDevtoolsProduction />
|
|
205
|
+
</Suspense>
|
|
206
|
+
</QueryClientProvider>
|
|
207
|
+
);
|
|
208
|
+
}
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
import { lazy, Suspense, useMemo, useState } from 'react';
|
|
2
|
+
import { MaterialReactTable, useMaterialReactTable, } from 'material-react-table';
|
|
3
|
+
import { Alert, CircularProgress, Stack } from '@mui/material';
|
|
4
|
+
import AddIcon from '@mui/icons-material/Add';
|
|
5
|
+
import MinusIcon from '@mui/icons-material/Remove';
|
|
6
|
+
import { QueryClient, QueryClientProvider, keepPreviousData, useQuery, } from '@tanstack/react-query'; //note: this is TanStack React Query V5
|
|
7
|
+
const DetailPanel = ({ row }) => {
|
|
8
|
+
const { data: userInfo, isLoading, isError, } = useFetchUserInfo({
|
|
9
|
+
phoneNumber: row.id, //the row id is set to the user's phone number
|
|
10
|
+
}, {
|
|
11
|
+
enabled: row.getIsExpanded(),
|
|
12
|
+
});
|
|
13
|
+
if (isLoading)
|
|
14
|
+
return <CircularProgress />;
|
|
15
|
+
if (isError)
|
|
16
|
+
return <Alert severity="error">Error Loading User Info</Alert>;
|
|
17
|
+
const { favoriteMusic, favoriteSong, quote } = userInfo ?? {};
|
|
18
|
+
return (<Stack sx={{
|
|
19
|
+
gap: '0.5rem',
|
|
20
|
+
minHeight: '00px',
|
|
21
|
+
}}>
|
|
22
|
+
<div>
|
|
23
|
+
<b>Favorite Music:</b> {favoriteMusic}
|
|
24
|
+
</div>
|
|
25
|
+
<div>
|
|
26
|
+
<b>Favorite Song:</b> {favoriteSong}
|
|
27
|
+
</div>
|
|
28
|
+
<div>
|
|
29
|
+
<b>Quote:</b> {quote}
|
|
30
|
+
</div>
|
|
31
|
+
</Stack>);
|
|
32
|
+
};
|
|
33
|
+
const Example = () => {
|
|
34
|
+
//manage our own state for stuff we want to pass to the API
|
|
35
|
+
const [columnFilters, setColumnFilters] = useState([]);
|
|
36
|
+
const [globalFilter, setGlobalFilter] = useState('');
|
|
37
|
+
const [sorting, setSorting] = useState([]);
|
|
38
|
+
const [pagination, setPagination] = useState({
|
|
39
|
+
pageIndex: 0,
|
|
40
|
+
pageSize: 5,
|
|
41
|
+
});
|
|
42
|
+
const { data: { data = [], meta } = {}, isError, isRefetching, isLoading, } = useFetchUsers({
|
|
43
|
+
columnFilters,
|
|
44
|
+
globalFilter,
|
|
45
|
+
pagination,
|
|
46
|
+
sorting,
|
|
47
|
+
});
|
|
48
|
+
const columns = useMemo(
|
|
49
|
+
//column definitions...
|
|
50
|
+
() => [
|
|
51
|
+
{
|
|
52
|
+
accessorKey: 'firstName',
|
|
53
|
+
header: 'First Name',
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
accessorKey: 'lastName',
|
|
57
|
+
header: 'Last Name',
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
accessorKey: 'address',
|
|
61
|
+
header: 'Address',
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
accessorKey: 'state',
|
|
65
|
+
header: 'State',
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
accessorKey: 'phoneNumber',
|
|
69
|
+
header: 'Phone Number',
|
|
70
|
+
},
|
|
71
|
+
], []);
|
|
72
|
+
const table = useMaterialReactTable({
|
|
73
|
+
columns,
|
|
74
|
+
data,
|
|
75
|
+
getRowId: (row) => row.phoneNumber,
|
|
76
|
+
manualFiltering: true, //turn off built-in client-side filtering
|
|
77
|
+
manualPagination: true, //turn off built-in client-side pagination
|
|
78
|
+
manualSorting: true, //turn off built-in client-side sorting
|
|
79
|
+
muiExpandButtonProps: ({ row }) => ({
|
|
80
|
+
children: row.getIsExpanded() ? <MinusIcon /> : <AddIcon />,
|
|
81
|
+
}),
|
|
82
|
+
muiToolbarAlertBannerProps: isError
|
|
83
|
+
? {
|
|
84
|
+
color: 'error',
|
|
85
|
+
children: 'Error loading data',
|
|
86
|
+
}
|
|
87
|
+
: undefined,
|
|
88
|
+
onColumnFiltersChange: setColumnFilters,
|
|
89
|
+
onGlobalFilterChange: setGlobalFilter,
|
|
90
|
+
onPaginationChange: setPagination,
|
|
91
|
+
onSortingChange: setSorting,
|
|
92
|
+
renderDetailPanel: ({ row }) => <DetailPanel row={row}/>,
|
|
93
|
+
rowCount: meta?.totalRowCount ?? 0,
|
|
94
|
+
state: {
|
|
95
|
+
columnFilters,
|
|
96
|
+
globalFilter,
|
|
97
|
+
isLoading,
|
|
98
|
+
pagination,
|
|
99
|
+
showAlertBanner: isError,
|
|
100
|
+
showProgressBars: isRefetching,
|
|
101
|
+
sorting,
|
|
102
|
+
},
|
|
103
|
+
});
|
|
104
|
+
return <MaterialReactTable table={table}/>;
|
|
105
|
+
};
|
|
106
|
+
//react query setup in App.tsx
|
|
107
|
+
const ReactQueryDevtoolsProduction = lazy(() => import('@tanstack/react-query-devtools/build/modern/production.js').then((d) => ({
|
|
108
|
+
default: d.ReactQueryDevtools,
|
|
109
|
+
})));
|
|
110
|
+
const queryClient = new QueryClient();
|
|
111
|
+
export default function App() {
|
|
112
|
+
return (<QueryClientProvider client={queryClient}>
|
|
113
|
+
<Example />
|
|
114
|
+
<Suspense fallback={null}>
|
|
115
|
+
<ReactQueryDevtoolsProduction />
|
|
116
|
+
</Suspense>
|
|
117
|
+
</QueryClientProvider>);
|
|
118
|
+
}
|
|
119
|
+
//fetch user hook
|
|
120
|
+
const useFetchUsers = ({ columnFilters, globalFilter, pagination, sorting, }) => {
|
|
121
|
+
return useQuery({
|
|
122
|
+
queryKey: [
|
|
123
|
+
'users', //give a unique key for this query
|
|
124
|
+
columnFilters, //refetch when columnFilters changes
|
|
125
|
+
globalFilter, //refetch when globalFilter changes
|
|
126
|
+
pagination.pageIndex, //refetch when pagination.pageIndex changes
|
|
127
|
+
pagination.pageSize, //refetch when pagination.pageSize changes
|
|
128
|
+
sorting, //refetch when sorting changes
|
|
129
|
+
],
|
|
130
|
+
queryFn: async () => {
|
|
131
|
+
const fetchURL = new URL('/api/data', location.origin);
|
|
132
|
+
//read our state and pass it to the API as query params
|
|
133
|
+
fetchURL.searchParams.set('start', `${pagination.pageIndex * pagination.pageSize}`);
|
|
134
|
+
fetchURL.searchParams.set('size', `${pagination.pageSize}`);
|
|
135
|
+
fetchURL.searchParams.set('filters', JSON.stringify(columnFilters ?? []));
|
|
136
|
+
fetchURL.searchParams.set('globalFilter', globalFilter ?? '');
|
|
137
|
+
fetchURL.searchParams.set('sorting', JSON.stringify(sorting ?? []));
|
|
138
|
+
//use whatever fetch library you want, fetch, axios, etc
|
|
139
|
+
const response = await fetch(fetchURL.href);
|
|
140
|
+
const json = (await response.json());
|
|
141
|
+
return json;
|
|
142
|
+
},
|
|
143
|
+
placeholderData: keepPreviousData, //don't go to 0 rows when refetching or paginating to next page
|
|
144
|
+
});
|
|
145
|
+
};
|
|
146
|
+
//fetch more user info hook
|
|
147
|
+
const useFetchUserInfo = (params, options) => {
|
|
148
|
+
return useQuery({
|
|
149
|
+
enabled: options.enabled, //only fetch when the detail panel is opened
|
|
150
|
+
staleTime: 60 * 1000, //don't refetch for 60 seconds
|
|
151
|
+
queryKey: ['user', params.phoneNumber], //give a unique key for this query for each user fetch
|
|
152
|
+
queryFn: async () => {
|
|
153
|
+
const fetchURL = new URL(`/api/moredata/${params.phoneNumber
|
|
154
|
+
.replaceAll('-', '')
|
|
155
|
+
.replaceAll('.', '')
|
|
156
|
+
.replaceAll('(', '')
|
|
157
|
+
.replaceAll(')', '')}`, location.origin);
|
|
158
|
+
//use whatever fetch library you want, fetch, axios, etc
|
|
159
|
+
const response = await fetch(fetchURL.href);
|
|
160
|
+
const json = (await response.json());
|
|
161
|
+
return json;
|
|
162
|
+
},
|
|
163
|
+
});
|
|
164
|
+
};
|
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
import { lazy, Suspense, useMemo, useState } from 'react';
|
|
2
|
+
import {
|
|
3
|
+
MaterialReactTable,
|
|
4
|
+
useMaterialReactTable,
|
|
5
|
+
type MRT_ColumnDef,
|
|
6
|
+
type MRT_ColumnFiltersState,
|
|
7
|
+
type MRT_PaginationState,
|
|
8
|
+
type MRT_SortingState,
|
|
9
|
+
type MRT_Row,
|
|
10
|
+
} from 'material-react-table';
|
|
11
|
+
import { Alert, CircularProgress, Stack } from '@mui/material';
|
|
12
|
+
import AddIcon from '@mui/icons-material/Add';
|
|
13
|
+
import MinusIcon from '@mui/icons-material/Remove';
|
|
14
|
+
import {
|
|
15
|
+
QueryClient,
|
|
16
|
+
QueryClientProvider,
|
|
17
|
+
keepPreviousData,
|
|
18
|
+
useQuery,
|
|
19
|
+
} from '@tanstack/react-query'; //note: this is TanStack React Query V5
|
|
20
|
+
|
|
21
|
+
//Your API response shape will probably be different. Knowing a total row count is important though.
|
|
22
|
+
type UserApiResponse = {
|
|
23
|
+
data: Array<User>;
|
|
24
|
+
meta: {
|
|
25
|
+
totalRowCount: number;
|
|
26
|
+
};
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
type User = {
|
|
30
|
+
firstName: string;
|
|
31
|
+
lastName: string;
|
|
32
|
+
address: string;
|
|
33
|
+
state: string;
|
|
34
|
+
phoneNumber: string;
|
|
35
|
+
lastLogin: Date;
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
type FullUserInfoApiResponse = FullUserInfo;
|
|
39
|
+
|
|
40
|
+
type FullUserInfo = User & {
|
|
41
|
+
favoriteMusic: string;
|
|
42
|
+
favoriteSong: string;
|
|
43
|
+
quote: string;
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
const DetailPanel = ({ row }: { row: MRT_Row<User> }) => {
|
|
47
|
+
const {
|
|
48
|
+
data: userInfo,
|
|
49
|
+
isLoading,
|
|
50
|
+
isError,
|
|
51
|
+
} = useFetchUserInfo(
|
|
52
|
+
{
|
|
53
|
+
phoneNumber: row.id, //the row id is set to the user's phone number
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
enabled: row.getIsExpanded(),
|
|
57
|
+
},
|
|
58
|
+
);
|
|
59
|
+
if (isLoading) return <CircularProgress />;
|
|
60
|
+
if (isError) return <Alert severity="error">Error Loading User Info</Alert>;
|
|
61
|
+
|
|
62
|
+
const { favoriteMusic, favoriteSong, quote } = userInfo ?? {};
|
|
63
|
+
|
|
64
|
+
return (
|
|
65
|
+
<Stack
|
|
66
|
+
sx={{
|
|
67
|
+
gap: '0.5rem',
|
|
68
|
+
minHeight: '00px',
|
|
69
|
+
}}
|
|
70
|
+
>
|
|
71
|
+
<div>
|
|
72
|
+
<b>Favorite Music:</b> {favoriteMusic}
|
|
73
|
+
</div>
|
|
74
|
+
<div>
|
|
75
|
+
<b>Favorite Song:</b> {favoriteSong}
|
|
76
|
+
</div>
|
|
77
|
+
<div>
|
|
78
|
+
<b>Quote:</b> {quote}
|
|
79
|
+
</div>
|
|
80
|
+
</Stack>
|
|
81
|
+
);
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
const Example = () => {
|
|
85
|
+
//manage our own state for stuff we want to pass to the API
|
|
86
|
+
const [columnFilters, setColumnFilters] = useState<MRT_ColumnFiltersState>(
|
|
87
|
+
[],
|
|
88
|
+
);
|
|
89
|
+
const [globalFilter, setGlobalFilter] = useState('');
|
|
90
|
+
const [sorting, setSorting] = useState<MRT_SortingState>([]);
|
|
91
|
+
const [pagination, setPagination] = useState<MRT_PaginationState>({
|
|
92
|
+
pageIndex: 0,
|
|
93
|
+
pageSize: 5,
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
const {
|
|
97
|
+
data: { data = [], meta } = {},
|
|
98
|
+
isError,
|
|
99
|
+
isRefetching,
|
|
100
|
+
isLoading,
|
|
101
|
+
} = useFetchUsers({
|
|
102
|
+
columnFilters,
|
|
103
|
+
globalFilter,
|
|
104
|
+
pagination,
|
|
105
|
+
sorting,
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
const columns = useMemo<MRT_ColumnDef<User>[]>(
|
|
109
|
+
//column definitions...
|
|
110
|
+
() => [
|
|
111
|
+
{
|
|
112
|
+
accessorKey: 'firstName',
|
|
113
|
+
header: 'First Name',
|
|
114
|
+
},
|
|
115
|
+
{
|
|
116
|
+
accessorKey: 'lastName',
|
|
117
|
+
header: 'Last Name',
|
|
118
|
+
},
|
|
119
|
+
{
|
|
120
|
+
accessorKey: 'address',
|
|
121
|
+
header: 'Address',
|
|
122
|
+
},
|
|
123
|
+
{
|
|
124
|
+
accessorKey: 'state',
|
|
125
|
+
header: 'State',
|
|
126
|
+
},
|
|
127
|
+
{
|
|
128
|
+
accessorKey: 'phoneNumber',
|
|
129
|
+
header: 'Phone Number',
|
|
130
|
+
},
|
|
131
|
+
],
|
|
132
|
+
[],
|
|
133
|
+
//end
|
|
134
|
+
);
|
|
135
|
+
|
|
136
|
+
const table = useMaterialReactTable({
|
|
137
|
+
columns,
|
|
138
|
+
data,
|
|
139
|
+
getRowId: (row) => row.phoneNumber,
|
|
140
|
+
manualFiltering: true, //turn off built-in client-side filtering
|
|
141
|
+
manualPagination: true, //turn off built-in client-side pagination
|
|
142
|
+
manualSorting: true, //turn off built-in client-side sorting
|
|
143
|
+
muiExpandButtonProps: ({ row }) => ({
|
|
144
|
+
children: row.getIsExpanded() ? <MinusIcon /> : <AddIcon />,
|
|
145
|
+
}),
|
|
146
|
+
muiToolbarAlertBannerProps: isError
|
|
147
|
+
? {
|
|
148
|
+
color: 'error',
|
|
149
|
+
children: 'Error loading data',
|
|
150
|
+
}
|
|
151
|
+
: undefined,
|
|
152
|
+
onColumnFiltersChange: setColumnFilters,
|
|
153
|
+
onGlobalFilterChange: setGlobalFilter,
|
|
154
|
+
onPaginationChange: setPagination,
|
|
155
|
+
onSortingChange: setSorting,
|
|
156
|
+
renderDetailPanel: ({ row }) => <DetailPanel row={row} />,
|
|
157
|
+
rowCount: meta?.totalRowCount ?? 0,
|
|
158
|
+
state: {
|
|
159
|
+
columnFilters,
|
|
160
|
+
globalFilter,
|
|
161
|
+
isLoading,
|
|
162
|
+
pagination,
|
|
163
|
+
showAlertBanner: isError,
|
|
164
|
+
showProgressBars: isRefetching,
|
|
165
|
+
sorting,
|
|
166
|
+
},
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
return <MaterialReactTable table={table} />;
|
|
170
|
+
};
|
|
171
|
+
|
|
172
|
+
//react query setup in App.tsx
|
|
173
|
+
const ReactQueryDevtoolsProduction = lazy(() =>
|
|
174
|
+
import('@tanstack/react-query-devtools/build/modern/production.js').then(
|
|
175
|
+
(d) => ({
|
|
176
|
+
default: d.ReactQueryDevtools,
|
|
177
|
+
}),
|
|
178
|
+
),
|
|
179
|
+
);
|
|
180
|
+
|
|
181
|
+
const queryClient = new QueryClient();
|
|
182
|
+
|
|
183
|
+
export default function App() {
|
|
184
|
+
return (
|
|
185
|
+
<QueryClientProvider client={queryClient}>
|
|
186
|
+
<Example />
|
|
187
|
+
<Suspense fallback={null}>
|
|
188
|
+
<ReactQueryDevtoolsProduction />
|
|
189
|
+
</Suspense>
|
|
190
|
+
</QueryClientProvider>
|
|
191
|
+
);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
//fetch user hook
|
|
195
|
+
const useFetchUsers = ({
|
|
196
|
+
columnFilters,
|
|
197
|
+
globalFilter,
|
|
198
|
+
pagination,
|
|
199
|
+
sorting,
|
|
200
|
+
}: {
|
|
201
|
+
columnFilters: MRT_ColumnFiltersState;
|
|
202
|
+
globalFilter: string;
|
|
203
|
+
pagination: MRT_PaginationState;
|
|
204
|
+
sorting: MRT_SortingState;
|
|
205
|
+
}) => {
|
|
206
|
+
return useQuery<UserApiResponse>({
|
|
207
|
+
queryKey: [
|
|
208
|
+
'users', //give a unique key for this query
|
|
209
|
+
columnFilters, //refetch when columnFilters changes
|
|
210
|
+
globalFilter, //refetch when globalFilter changes
|
|
211
|
+
pagination.pageIndex, //refetch when pagination.pageIndex changes
|
|
212
|
+
pagination.pageSize, //refetch when pagination.pageSize changes
|
|
213
|
+
sorting, //refetch when sorting changes
|
|
214
|
+
],
|
|
215
|
+
queryFn: async () => {
|
|
216
|
+
const fetchURL = new URL('/api/data', location.origin);
|
|
217
|
+
|
|
218
|
+
//read our state and pass it to the API as query params
|
|
219
|
+
fetchURL.searchParams.set(
|
|
220
|
+
'start',
|
|
221
|
+
`${pagination.pageIndex * pagination.pageSize}`,
|
|
222
|
+
);
|
|
223
|
+
fetchURL.searchParams.set('size', `${pagination.pageSize}`);
|
|
224
|
+
fetchURL.searchParams.set('filters', JSON.stringify(columnFilters ?? []));
|
|
225
|
+
fetchURL.searchParams.set('globalFilter', globalFilter ?? '');
|
|
226
|
+
fetchURL.searchParams.set('sorting', JSON.stringify(sorting ?? []));
|
|
227
|
+
|
|
228
|
+
//use whatever fetch library you want, fetch, axios, etc
|
|
229
|
+
const response = await fetch(fetchURL.href);
|
|
230
|
+
const json = (await response.json()) as UserApiResponse;
|
|
231
|
+
return json;
|
|
232
|
+
},
|
|
233
|
+
placeholderData: keepPreviousData, //don't go to 0 rows when refetching or paginating to next page
|
|
234
|
+
});
|
|
235
|
+
};
|
|
236
|
+
|
|
237
|
+
//fetch more user info hook
|
|
238
|
+
const useFetchUserInfo = (
|
|
239
|
+
params: { phoneNumber: string },
|
|
240
|
+
options: { enabled: boolean },
|
|
241
|
+
) => {
|
|
242
|
+
return useQuery<FullUserInfoApiResponse>({
|
|
243
|
+
enabled: options.enabled, //only fetch when the detail panel is opened
|
|
244
|
+
staleTime: 60 * 1000, //don't refetch for 60 seconds
|
|
245
|
+
queryKey: ['user', params.phoneNumber], //give a unique key for this query for each user fetch
|
|
246
|
+
queryFn: async () => {
|
|
247
|
+
const fetchURL = new URL(
|
|
248
|
+
`/api/moredata/${params.phoneNumber
|
|
249
|
+
.replaceAll('-', '')
|
|
250
|
+
.replaceAll('.', '')
|
|
251
|
+
.replaceAll('(', '')
|
|
252
|
+
.replaceAll(')', '')}`,
|
|
253
|
+
location.origin,
|
|
254
|
+
);
|
|
255
|
+
|
|
256
|
+
//use whatever fetch library you want, fetch, axios, etc
|
|
257
|
+
const response = await fetch(fetchURL.href);
|
|
258
|
+
const json = (await response.json()) as FullUserInfoApiResponse;
|
|
259
|
+
return json;
|
|
260
|
+
},
|
|
261
|
+
});
|
|
262
|
+
};
|