1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
|
import type { UseQueryResult } from '@tanstack/react-query';
import { Column, Row, SearchField } from '@umami/react-zen';
import {
cloneElement,
isValidElement,
type ReactElement,
type ReactNode,
useCallback,
useState,
} from 'react';
import { Empty } from '@/components/common/Empty';
import { LoadingPanel } from '@/components/common/LoadingPanel';
import { Pager } from '@/components/common/Pager';
import { useMessages, useMobile, useNavigation } from '@/components/hooks';
import type { PageResult } from '@/lib/types';
const DEFAULT_SEARCH_DELAY = 600;
export interface DataGridProps {
query: UseQueryResult<PageResult<any>, any>;
searchDelay?: number;
allowSearch?: boolean;
allowPaging?: boolean;
autoFocus?: boolean;
renderActions?: () => ReactNode;
renderEmpty?: () => ReactNode;
children: ReactNode | ((data: any) => ReactNode);
}
export function DataGrid({
query,
searchDelay = 600,
allowSearch,
allowPaging = true,
autoFocus,
renderActions,
renderEmpty = () => <Empty />,
children,
}: DataGridProps) {
const { formatMessage, labels } = useMessages();
const { data, error, isLoading, isFetching } = query;
const { router, updateParams, query: queryParams } = useNavigation();
const [search, setSearch] = useState(queryParams?.search || data?.search || '');
const showPager = allowPaging && data && data.count > data.pageSize;
const { isMobile } = useMobile();
const displayMode = isMobile ? 'cards' : undefined;
const handleSearch = (value: string) => {
if (value !== search) {
setSearch(value);
router.push(updateParams({ search: value, page: 1 }));
}
};
const handlePageChange = useCallback(
(page: number) => {
router.push(updateParams({ search, page }));
},
[search],
);
const child = data ? (typeof children === 'function' ? children(data) : children) : null;
return (
<Column gap="4" minHeight="300px">
{allowSearch && (
<Row alignItems="center" justifyContent="space-between" wrap="wrap" gap>
<SearchField
value={search}
onSearch={handleSearch}
delay={searchDelay || DEFAULT_SEARCH_DELAY}
autoFocus={autoFocus}
placeholder={formatMessage(labels.search)}
/>
{renderActions?.()}
</Row>
)}
<LoadingPanel
data={data}
isLoading={isLoading}
isFetching={isFetching}
error={error}
renderEmpty={renderEmpty}
>
{data && (
<>
<Column>
{isValidElement(child)
? cloneElement(child as ReactElement<any>, { displayMode })
: child}
</Column>
{showPager && (
<Row marginTop="6">
<Pager
page={data.page}
pageSize={data.pageSize}
count={data.count}
onPageChange={handlePageChange}
/>
</Row>
)}
</>
)}
</LoadingPanel>
</Column>
);
}
|