@liguelead/design-system 0.0.30 → 0.0.32
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/components/Alert/Alert.stories.tsx +94 -18
- package/components/Badge/Badge.stories.tsx +114 -0
- package/components/Badge/Badge.styles.ts +36 -0
- package/components/Badge/Badge.tsx +23 -0
- package/components/Badge/Badge.types.ts +11 -0
- package/components/Badge/index.ts +2 -0
- package/components/Button/Button.appearance.ts +1 -1
- package/components/Button/Button.stories.tsx +99 -18
- package/components/Checkbox/Checkbox.stories.tsx +107 -7
- package/components/DatePicker/DatePicker.styles.ts +1 -0
- package/components/DatePicker/DatePicker.tsx +9 -10
- package/components/IconButton/IconButton.sizes.ts +7 -7
- package/components/IconButton/IconButton.tsx +0 -1
- package/components/InputOpt/InputOpt.stories.tsx +30 -44
- package/components/Select/Select.stories.tsx +80 -19
- package/components/Select/Select.tsx +7 -9
- package/components/Table/Datatable.stories.tsx +186 -0
- package/components/Table/Table.stories.tsx +127 -46
- package/components/Table/Table.styles.ts +83 -8
- package/components/Table/Table.tsx +292 -142
- package/components/Table/Table.types.ts +104 -12
- package/components/Table/components/ColumnVisibility/ColumnVisibility.style.ts +46 -0
- package/components/Table/components/ColumnVisibility/ColumnVisibility.tsx +55 -0
- package/components/Table/components/DatatableColumnFilterMenu/DatatableColumnFilterMenu.styles.ts +120 -0
- package/components/Table/components/DatatableColumnFilterMenu/DatatableColumnFilterMenu.tsx +228 -0
- package/components/Table/components/DatatableColumnFilterMenu/index.ts +1 -0
- package/components/Table/components/DatatableTopBar/DatatableTopBar.styles.ts +25 -0
- package/components/Table/components/DatatableTopBar/DatatableTopBar.tsx +89 -0
- package/components/Table/components/DatatableTopBar/index.ts +1 -0
- package/components/Table/components/SearchInput/SearchInput.tsx +30 -0
- package/components/Table/components/TableHeader/TableHeader.tsx +98 -0
- package/components/Table/components/TablePagination/TablePagination.tsx +78 -0
- package/components/Table/components/index.ts +6 -0
- package/components/Table/hooks/useDatatableFilters.ts +88 -0
- package/components/Table/stories.fixtures.ts +100 -0
- package/components/Table/tanstack-table.d.ts +10 -0
- package/components/Table/utils/dateRangeFilterFn.ts +33 -0
- package/components/Table/utils/index.ts +2 -1
- package/components/Tabs/Tabs.stories.tsx +152 -0
- package/components/Tabs/Tabs.styles.ts +12 -0
- package/components/Tabs/Tabs.tsx +34 -0
- package/components/Tabs/Tabs.types.ts +15 -0
- package/components/Tabs/index.ts +2 -0
- package/components/TextField/TextField.stories.tsx +135 -12
- package/components/index.ts +3 -0
- package/package.json +3 -2
|
@@ -1,157 +1,307 @@
|
|
|
1
|
-
import { useState } from 'react'
|
|
1
|
+
import { useState, useEffect, useMemo } from 'react'
|
|
2
2
|
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
3
|
+
flexRender,
|
|
4
|
+
getCoreRowModel,
|
|
5
|
+
getFilteredRowModel,
|
|
6
|
+
getPaginationRowModel,
|
|
7
|
+
getSortedRowModel,
|
|
8
|
+
SortingState,
|
|
9
|
+
useReactTable,
|
|
10
|
+
VisibilityState,
|
|
11
|
+
FilterFn,
|
|
7
12
|
} from '@tanstack/react-table'
|
|
8
13
|
|
|
9
|
-
import {
|
|
10
|
-
CaretLeftIcon,
|
|
11
|
-
CaretRightIcon,
|
|
12
|
-
DotsThreeIcon,
|
|
13
|
-
} from '@phosphor-icons/react'
|
|
14
|
+
import { DownloadSimpleIcon, FunnelSimpleXIcon } from '@phosphor-icons/react'
|
|
14
15
|
|
|
15
16
|
import {
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
17
|
+
EmptyMessage,
|
|
18
|
+
FooterText,
|
|
19
|
+
StyledTable,
|
|
20
|
+
TableFooter,
|
|
21
|
+
TableScrollContainer,
|
|
22
|
+
TableToolbar,
|
|
23
|
+
TableWrapper,
|
|
24
|
+
Td,
|
|
25
|
+
Tr,
|
|
26
|
+
ToolbarLeft,
|
|
27
|
+
ToolbarRight,
|
|
27
28
|
} from './Table.styles'
|
|
28
|
-
import { TTableProps } from './Table.types'
|
|
29
|
-
import { getPageNumbers } from './utils'
|
|
30
|
-
import IconButton from '../IconButton'
|
|
31
|
-
import Button from '../Button'
|
|
32
29
|
|
|
30
|
+
import { TableQueryState, TTableProps } from './Table.types'
|
|
31
|
+
import { dateRangeFilterFn } from './utils'
|
|
32
|
+
import { useDatatableFilters } from './hooks/useDatatableFilters'
|
|
33
|
+
import Button from '../Button'
|
|
34
|
+
import {
|
|
35
|
+
ColumnVisibility,
|
|
36
|
+
DatatableTopBar,
|
|
37
|
+
SearchInput,
|
|
38
|
+
TableHeader,
|
|
39
|
+
TablePagination,
|
|
40
|
+
} from './components'
|
|
33
41
|
|
|
34
42
|
function Table<TData>({
|
|
35
|
-
data,
|
|
36
|
-
columns,
|
|
37
|
-
emptyTitle = 'Nenhum registro encontrado.',
|
|
38
|
-
footerText,
|
|
39
|
-
pageSize = 10,
|
|
40
|
-
showPagination = true,
|
|
41
|
-
}: TTableProps<TData>) {
|
|
42
|
-
const [pagination, setPagination] = useState({
|
|
43
|
-
pageIndex: 0,
|
|
44
|
-
pageSize,
|
|
45
|
-
});
|
|
46
|
-
|
|
47
|
-
const table = useReactTable({
|
|
48
43
|
data,
|
|
49
44
|
columns,
|
|
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
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
45
|
+
mode = 'client',
|
|
46
|
+
variant = 'table',
|
|
47
|
+
emptyTitle = 'Nenhum registro encontrado.',
|
|
48
|
+
footerText,
|
|
49
|
+
pageSize = 10,
|
|
50
|
+
showPagination = true,
|
|
51
|
+
enableGlobalSearch = false,
|
|
52
|
+
enableColumnVisibility = false,
|
|
53
|
+
enableClearFilters = false,
|
|
54
|
+
renderToolbarLeft,
|
|
55
|
+
renderToolbarRight,
|
|
56
|
+
searchPlaceholder = 'Pesquisar',
|
|
57
|
+
searchWidth = '373px',
|
|
58
|
+
clearFiltersLabel = 'Limpar filtros',
|
|
59
|
+
onExport,
|
|
60
|
+
getExportUrl,
|
|
61
|
+
exportButton = {},
|
|
62
|
+
ariaLabel = 'Tabela de dados',
|
|
63
|
+
paginationAriaLabels = {},
|
|
64
|
+
initialSorting = [],
|
|
65
|
+
initialColumnVisibility = {},
|
|
66
|
+
initialGlobalFilter = '',
|
|
67
|
+
datatableColumnFiltersValue,
|
|
68
|
+
onDatatableColumnFiltersChange,
|
|
69
|
+
datatableFilterDropdownLabels = {},
|
|
70
|
+
datatableEnableGlobalSearch = true,
|
|
71
|
+
datatableEnableColumnVisibility = true,
|
|
72
|
+
datatableEnableClearFilters = true,
|
|
73
|
+
...serverProps
|
|
74
|
+
}: TTableProps<TData>) {
|
|
75
|
+
const isDatatable = variant === 'datatable'
|
|
76
|
+
const isServerMode = mode === 'server'
|
|
77
|
+
|
|
78
|
+
const serverPageCount = isServerMode && 'pageCount' in serverProps ? serverProps.pageCount : undefined
|
|
79
|
+
const serverTotalRows = isServerMode && 'totalRows' in serverProps ? serverProps.totalRows : undefined
|
|
80
|
+
const serverFilteredRows = isServerMode && 'filteredRows' in serverProps ? serverProps.filteredRows : undefined
|
|
81
|
+
const onQueryChange = isServerMode && 'onQueryChange' in serverProps ? serverProps.onQueryChange : undefined
|
|
82
|
+
|
|
83
|
+
const [pagination, setPagination] = useState({ pageIndex: 0, pageSize })
|
|
84
|
+
const [columnVisibility, setColumnVisibility] = useState<VisibilityState>(initialColumnVisibility)
|
|
85
|
+
const [globalFilter, setGlobalFilter] = useState(initialGlobalFilter)
|
|
86
|
+
const [sorting, setSorting] = useState<SortingState>(initialSorting)
|
|
87
|
+
|
|
88
|
+
const {
|
|
89
|
+
filters: datatableFilters,
|
|
90
|
+
columnFilters,
|
|
91
|
+
setColumnFilters,
|
|
92
|
+
openFilterColumnId,
|
|
93
|
+
handleFilterValueChange,
|
|
94
|
+
handleClearColumnFilter,
|
|
95
|
+
handleClearAllFilters,
|
|
96
|
+
handlePopoverOpenChange,
|
|
97
|
+
} = useDatatableFilters({
|
|
98
|
+
isServerMode,
|
|
99
|
+
controlled: datatableColumnFiltersValue,
|
|
100
|
+
onChange: onDatatableColumnFiltersChange,
|
|
101
|
+
})
|
|
102
|
+
|
|
103
|
+
const queryState: TableQueryState = useMemo(() => ({
|
|
104
|
+
pageIndex: pagination.pageIndex,
|
|
105
|
+
pageSize: pagination.pageSize,
|
|
106
|
+
sorting,
|
|
107
|
+
globalFilter,
|
|
108
|
+
columnVisibility,
|
|
109
|
+
datatableFilters,
|
|
110
|
+
}), [pagination, sorting, globalFilter, columnVisibility, datatableFilters])
|
|
111
|
+
|
|
112
|
+
useEffect(() => {
|
|
113
|
+
if (isServerMode && onQueryChange) onQueryChange(queryState)
|
|
114
|
+
}, [queryState, isServerMode, onQueryChange])
|
|
115
|
+
|
|
116
|
+
const selectEqualsFilterFn = useMemo(
|
|
117
|
+
() =>
|
|
118
|
+
((row, columnId, filterValue) => {
|
|
119
|
+
if (filterValue == null || filterValue === '') return true
|
|
120
|
+
return row.getValue(columnId) === filterValue
|
|
121
|
+
}) as FilterFn<TData>,
|
|
122
|
+
[]
|
|
123
|
+
)
|
|
124
|
+
|
|
125
|
+
const resolvedColumns = useMemo(
|
|
126
|
+
() =>
|
|
127
|
+
columns.map((col) =>
|
|
128
|
+
(col.meta as { filterType?: string } | undefined)?.filterType === 'select' &&
|
|
129
|
+
!col.filterFn
|
|
130
|
+
? { ...col, filterFn: selectEqualsFilterFn }
|
|
131
|
+
: col
|
|
132
|
+
),
|
|
133
|
+
[columns, selectEqualsFilterFn]
|
|
134
|
+
)
|
|
135
|
+
|
|
136
|
+
const table = useReactTable<TData>({
|
|
137
|
+
data,
|
|
138
|
+
columns: resolvedColumns,
|
|
139
|
+
state: { pagination, columnVisibility, globalFilter, sorting, columnFilters },
|
|
140
|
+
onSortingChange: setSorting,
|
|
141
|
+
onColumnVisibilityChange: setColumnVisibility,
|
|
142
|
+
onGlobalFilterChange: setGlobalFilter,
|
|
143
|
+
onPaginationChange: setPagination,
|
|
144
|
+
onColumnFiltersChange: setColumnFilters,
|
|
145
|
+
getCoreRowModel: getCoreRowModel(),
|
|
146
|
+
filterFns: {
|
|
147
|
+
dateRange: dateRangeFilterFn as FilterFn<TData>
|
|
148
|
+
},
|
|
149
|
+
...(isServerMode
|
|
150
|
+
? { manualPagination: true, manualSorting: true, manualFiltering: true, pageCount: serverPageCount }
|
|
151
|
+
: {
|
|
152
|
+
getFilteredRowModel: getFilteredRowModel(),
|
|
153
|
+
getSortedRowModel: getSortedRowModel(),
|
|
154
|
+
getPaginationRowModel: getPaginationRowModel(),
|
|
155
|
+
globalFilterFn: 'includesString',
|
|
156
|
+
}
|
|
157
|
+
),
|
|
158
|
+
})
|
|
159
|
+
|
|
160
|
+
const total = isServerMode ? (serverTotalRows ?? data.length) : data.length
|
|
161
|
+
const filtered = isServerMode ? (serverFilteredRows ?? data.length) : table.getFilteredRowModel().rows.length
|
|
162
|
+
const pageCount = isServerMode ? (serverPageCount ?? 1) : table.getPageCount()
|
|
163
|
+
const hasPagination = showPagination && pageCount > 1
|
|
164
|
+
const currentPage = pagination.pageIndex + 1
|
|
165
|
+
|
|
166
|
+
const hasActiveFilters = globalFilter !== '' || sorting.length > 0
|
|
167
|
+
const hasDatatableActiveFilters = globalFilter !== '' || columnFilters.length > 0
|
|
168
|
+
|
|
169
|
+
const handleClearFilters = () => {
|
|
170
|
+
setGlobalFilter('')
|
|
171
|
+
setSorting([])
|
|
172
|
+
setPagination((prev) => ({ ...prev, pageIndex: 0 }))
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
const handleExport = async () => {
|
|
176
|
+
if (onExport) {
|
|
177
|
+
await onExport(queryState)
|
|
178
|
+
} else if (getExportUrl) {
|
|
179
|
+
const url = getExportUrl(queryState)
|
|
180
|
+
const link = document.createElement('a')
|
|
181
|
+
link.href = url
|
|
182
|
+
link.download = ''
|
|
183
|
+
document.body.appendChild(link)
|
|
184
|
+
link.click()
|
|
185
|
+
document.body.removeChild(link)
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
const showExportButton = Boolean(onExport || getExportUrl)
|
|
190
|
+
const toolbarContext = { globalFilter, setGlobalFilter, queryState }
|
|
191
|
+
|
|
192
|
+
const hasToolbar =
|
|
193
|
+
enableGlobalSearch || enableColumnVisibility || enableClearFilters ||
|
|
194
|
+
renderToolbarLeft || renderToolbarRight || showExportButton
|
|
195
|
+
|
|
196
|
+
return (
|
|
197
|
+
<TableWrapper>
|
|
198
|
+
{isDatatable && (
|
|
199
|
+
<DatatableTopBar
|
|
200
|
+
table={table}
|
|
201
|
+
globalFilter={globalFilter}
|
|
202
|
+
setGlobalFilter={setGlobalFilter}
|
|
203
|
+
enableGlobalSearch={datatableEnableGlobalSearch}
|
|
204
|
+
enableColumnVisibility={datatableEnableColumnVisibility}
|
|
205
|
+
enableClearFilters={datatableEnableClearFilters}
|
|
206
|
+
hasActiveFilters={hasDatatableActiveFilters}
|
|
207
|
+
onClearAll={() => handleClearAllFilters(() => setGlobalFilter(''))}
|
|
208
|
+
searchPlaceholder={searchPlaceholder}
|
|
209
|
+
searchWidth={searchWidth}
|
|
210
|
+
clearFiltersLabel={clearFiltersLabel}
|
|
211
|
+
showExportButton={showExportButton}
|
|
212
|
+
onExport={handleExport}
|
|
213
|
+
exportButton={exportButton}
|
|
214
|
+
/>
|
|
215
|
+
)}
|
|
216
|
+
|
|
217
|
+
{!isDatatable && hasToolbar && (
|
|
218
|
+
<TableToolbar>
|
|
219
|
+
<ToolbarLeft>
|
|
220
|
+
{renderToolbarLeft
|
|
221
|
+
? renderToolbarLeft(toolbarContext)
|
|
222
|
+
: enableGlobalSearch && (
|
|
223
|
+
<SearchInput
|
|
224
|
+
globalFilter={globalFilter}
|
|
225
|
+
setGlobalFilter={setGlobalFilter}
|
|
226
|
+
placeholder={searchPlaceholder}
|
|
227
|
+
width={searchWidth}
|
|
228
|
+
/>
|
|
229
|
+
)
|
|
134
230
|
}
|
|
135
|
-
|
|
136
|
-
>
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
231
|
+
</ToolbarLeft>
|
|
232
|
+
<ToolbarRight>
|
|
233
|
+
{renderToolbarRight?.(toolbarContext)}
|
|
234
|
+
{enableClearFilters && hasActiveFilters && (
|
|
235
|
+
<Button variant="solid" color="danger200" onClick={handleClearFilters} aria-label="Limpar filtros">
|
|
236
|
+
<FunnelSimpleXIcon />
|
|
237
|
+
{clearFiltersLabel}
|
|
238
|
+
</Button>
|
|
239
|
+
)}
|
|
240
|
+
{showExportButton && (
|
|
241
|
+
<Button
|
|
242
|
+
variant={exportButton.variant ?? 'neutralOutline'}
|
|
243
|
+
onClick={handleExport}
|
|
244
|
+
disabled={exportButton.disabled}
|
|
245
|
+
aria-label={exportButton.ariaLabel ?? 'Exportar'}
|
|
246
|
+
>
|
|
247
|
+
<DownloadSimpleIcon size={16} />
|
|
248
|
+
{exportButton.label ?? 'Exportar'}
|
|
249
|
+
</Button>
|
|
250
|
+
)}
|
|
251
|
+
{enableColumnVisibility && <ColumnVisibility table={table} />}
|
|
252
|
+
</ToolbarRight>
|
|
253
|
+
</TableToolbar>
|
|
254
|
+
)}
|
|
255
|
+
|
|
256
|
+
<TableScrollContainer>
|
|
257
|
+
<StyledTable role="table" aria-label={ariaLabel}>
|
|
258
|
+
<TableHeader
|
|
259
|
+
headerGroups={table.getHeaderGroups()}
|
|
260
|
+
isDatatable={isDatatable}
|
|
261
|
+
datatableFilters={datatableFilters}
|
|
262
|
+
openFilterColumnId={openFilterColumnId}
|
|
263
|
+
onFilterPopoverOpenChange={handlePopoverOpenChange}
|
|
264
|
+
onFilterValueChange={handleFilterValueChange}
|
|
265
|
+
onClearFilter={handleClearColumnFilter}
|
|
266
|
+
labels={datatableFilterDropdownLabels}
|
|
267
|
+
/>
|
|
268
|
+
<tbody>
|
|
269
|
+
{table.getRowModel().rows.length === 0 ? (
|
|
270
|
+
<tr>
|
|
271
|
+
<EmptyMessage colSpan={columns.length}>{emptyTitle}</EmptyMessage>
|
|
272
|
+
</tr>
|
|
273
|
+
) : (
|
|
274
|
+
table.getRowModel().rows.map((row) => (
|
|
275
|
+
<Tr key={row.id}>
|
|
276
|
+
{row.getVisibleCells().map((cell) => (
|
|
277
|
+
<Td key={cell.id}>
|
|
278
|
+
{flexRender(cell.column.columnDef.cell, cell.getContext())}
|
|
279
|
+
</Td>
|
|
280
|
+
))}
|
|
281
|
+
</Tr>
|
|
282
|
+
))
|
|
283
|
+
)}
|
|
284
|
+
</tbody>
|
|
285
|
+
</StyledTable>
|
|
286
|
+
</TableScrollContainer>
|
|
287
|
+
|
|
288
|
+
{(footerText || hasPagination) && filtered > 0 && (
|
|
289
|
+
<TableFooter $hasPagination={hasPagination}>
|
|
290
|
+
{footerText && (hasActiveFilters || hasDatatableActiveFilters) && (
|
|
291
|
+
<FooterText>{footerText({ total, filtered })}</FooterText>
|
|
292
|
+
)}
|
|
293
|
+
{hasPagination && (
|
|
294
|
+
<TablePagination
|
|
295
|
+
table={table}
|
|
296
|
+
currentPage={currentPage}
|
|
297
|
+
pageCount={pageCount}
|
|
298
|
+
ariaLabels={paginationAriaLabels}
|
|
299
|
+
/>
|
|
300
|
+
)}
|
|
301
|
+
</TableFooter>
|
|
302
|
+
)}
|
|
303
|
+
</TableWrapper>
|
|
304
|
+
)
|
|
155
305
|
}
|
|
156
306
|
|
|
157
307
|
export default Table
|
|
@@ -1,16 +1,108 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { ReactNode } from 'react'
|
|
2
|
+
import { ColumnDef, SortingState, VisibilityState } from '@tanstack/react-table'
|
|
2
3
|
|
|
3
|
-
export type
|
|
4
|
-
|
|
5
|
-
columns: ColumnDef<TData, unknown>[]
|
|
4
|
+
export type TableMode = 'client' | 'server'
|
|
5
|
+
export type TableVariant = 'table' | 'datatable'
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
7
|
+
export type ColumnFilterValue =
|
|
8
|
+
| { type: 'text'; value: string }
|
|
9
|
+
| { type: 'dateRange'; value: { from?: Date; to?: Date } }
|
|
10
|
+
| { type: 'select'; value: string | number | null }
|
|
10
11
|
|
|
11
|
-
|
|
12
|
-
total: number
|
|
13
|
-
filtered: number
|
|
14
|
-
}) => string
|
|
12
|
+
export type DatatableColumnFilters = Record<string, ColumnFilterValue | undefined>
|
|
15
13
|
|
|
16
|
-
|
|
14
|
+
export interface TableQueryState {
|
|
15
|
+
pageIndex: number
|
|
16
|
+
pageSize: number
|
|
17
|
+
sorting: SortingState
|
|
18
|
+
globalFilter: string
|
|
19
|
+
columnVisibility: VisibilityState
|
|
20
|
+
datatableFilters: DatatableColumnFilters
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export interface TableToolbarContext<_TData = unknown> {
|
|
24
|
+
globalFilter: string
|
|
25
|
+
setGlobalFilter: (value: string) => void
|
|
26
|
+
queryState: TableQueryState
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export interface ExportButtonConfig {
|
|
30
|
+
label?: string
|
|
31
|
+
variant?: 'solid' | 'outline' | 'ghost' | 'neutralOutline' | 'neutralGhost'
|
|
32
|
+
disabled?: boolean
|
|
33
|
+
ariaLabel?: string
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export type DatatableFilterType = 'text' | 'dateRange' | 'select'
|
|
37
|
+
|
|
38
|
+
export interface DatatableColumnMeta {
|
|
39
|
+
filterType?: DatatableFilterType
|
|
40
|
+
filterLabel?: string
|
|
41
|
+
filterPlaceholder?: string
|
|
42
|
+
selectOptions?: Array<{ label: string; value: string | number }>
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export interface DatatableFilterDropdownLabels {
|
|
46
|
+
filterButtonAriaLabel?: string
|
|
47
|
+
clearFiltersLabel?: string
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export interface TableBaseProps<TData> {
|
|
51
|
+
data: TData[]
|
|
52
|
+
columns: ColumnDef<TData, unknown>[]
|
|
53
|
+
mode?: TableMode
|
|
54
|
+
variant?: TableVariant
|
|
55
|
+
pageSize?: number
|
|
56
|
+
showPagination?: boolean
|
|
57
|
+
emptyTitle?: string
|
|
58
|
+
footerText?: (info: { total: number; filtered: number }) => string
|
|
59
|
+
ariaLabel?: string
|
|
60
|
+
paginationAriaLabels?: {
|
|
61
|
+
previous?: string
|
|
62
|
+
next?: string
|
|
63
|
+
page?: string
|
|
64
|
+
}
|
|
65
|
+
initialSorting?: SortingState
|
|
66
|
+
initialColumnVisibility?: VisibilityState
|
|
67
|
+
initialGlobalFilter?: string
|
|
68
|
+
datatableColumnFiltersValue?: DatatableColumnFilters
|
|
69
|
+
onDatatableColumnFiltersChange?: (next: DatatableColumnFilters) => void
|
|
70
|
+
datatableFilterDropdownLabels?: DatatableFilterDropdownLabels
|
|
71
|
+
datatableEnableGlobalSearch?: boolean
|
|
72
|
+
datatableEnableColumnVisibility?: boolean
|
|
73
|
+
datatableEnableClearFilters?: boolean
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export interface TableClientProps<TData> extends TableBaseProps<TData> {
|
|
77
|
+
mode?: 'client'
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export interface TableServerProps<TData> extends TableBaseProps<TData> {
|
|
81
|
+
mode: 'server'
|
|
82
|
+
pageCount: number
|
|
83
|
+
totalRows?: number
|
|
84
|
+
filteredRows?: number
|
|
85
|
+
onQueryChange?: (state: TableQueryState) => void | Promise<void>
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export interface TableToolbarProps<TData> {
|
|
89
|
+
enableGlobalSearch?: boolean
|
|
90
|
+
enableColumnVisibility?: boolean
|
|
91
|
+
enableClearFilters?: boolean
|
|
92
|
+
renderToolbarLeft?: (ctx: TableToolbarContext<TData>) => ReactNode
|
|
93
|
+
renderToolbarRight?: (ctx: TableToolbarContext<TData>) => ReactNode
|
|
94
|
+
searchPlaceholder?: string
|
|
95
|
+
searchWidth?: string
|
|
96
|
+
clearFiltersLabel?: string
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export interface TableExportProps {
|
|
100
|
+
onExport?: (state: TableQueryState) => void | Promise<void>
|
|
101
|
+
getExportUrl?: (state: TableQueryState) => string
|
|
102
|
+
exportButton?: ExportButtonConfig
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export type TTableProps<TData> =
|
|
106
|
+
(TableClientProps<TData> | TableServerProps<TData>)
|
|
107
|
+
& TableToolbarProps<TData>
|
|
108
|
+
& TableExportProps
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import style from "styled-components";
|
|
2
|
+
import { radius, spacing } from "@liguelead/foundation";
|
|
3
|
+
import { CaretDownIcon, CaretUpIcon } from "@phosphor-icons/react";
|
|
4
|
+
import { parseColor } from "../../../../utils";
|
|
5
|
+
|
|
6
|
+
type TIconProps = {
|
|
7
|
+
$selected?: 'asc' | 'desc' | false;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export const PopoverWrapper = style.div`
|
|
11
|
+
display: flex;
|
|
12
|
+
margin-top: 8px;
|
|
13
|
+
flex-direction: column;
|
|
14
|
+
gap: 6px;
|
|
15
|
+
justify-content: center;
|
|
16
|
+
background-color: white;
|
|
17
|
+
border: 1px solid ${({ theme }) => parseColor(theme.colors.neutral200)};
|
|
18
|
+
border-radius: 4px;
|
|
19
|
+
padding: 8px;
|
|
20
|
+
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.10), 0 2px 4px -2px rgba(0, 0, 0, 0.10);
|
|
21
|
+
`;
|
|
22
|
+
|
|
23
|
+
export const PopoverLabel = style.label`
|
|
24
|
+
display: flex;
|
|
25
|
+
align-items: center;
|
|
26
|
+
gap: ${spacing.spacing2}px;
|
|
27
|
+
padding: ${spacing.spacing2}px;
|
|
28
|
+
border-radius: ${radius.radius2}px;
|
|
29
|
+
cursor: pointer;
|
|
30
|
+
`
|
|
31
|
+
|
|
32
|
+
export const ArrowUpIcon = style(CaretUpIcon).attrs<TIconProps>({})`
|
|
33
|
+
fill: ${({ theme, $selected }) => {
|
|
34
|
+
if ($selected === 'asc') return parseColor(theme.colors.primary);
|
|
35
|
+
if ($selected === 'desc') return parseColor(theme.colors.neutral400);
|
|
36
|
+
return parseColor(theme.colors.neutral700);
|
|
37
|
+
}};
|
|
38
|
+
`;
|
|
39
|
+
|
|
40
|
+
export const ArrowDownIcon = style(CaretDownIcon).attrs<TIconProps>({})`
|
|
41
|
+
fill: ${({ theme, $selected }) => {
|
|
42
|
+
if ($selected === 'desc') return parseColor(theme.colors.primary);
|
|
43
|
+
if ($selected === 'asc') return parseColor(theme.colors.neutral400);
|
|
44
|
+
return parseColor(theme.colors.neutral700);
|
|
45
|
+
}};
|
|
46
|
+
`;
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { Table } from "@tanstack/react-table";
|
|
2
|
+
import { SlidersHorizontalIcon } from "@phosphor-icons/react";
|
|
3
|
+
import * as PopoverPrimitive from "@radix-ui/react-popover";
|
|
4
|
+
import Checkbox from "../../../Checkbox";
|
|
5
|
+
import IconButton from "../../../IconButton";
|
|
6
|
+
import { PopoverLabel, PopoverWrapper } from "./ColumnVisibility.style";
|
|
7
|
+
|
|
8
|
+
interface ColumnVisibilityProps<TData> {
|
|
9
|
+
table: Table<TData>;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const ColumnVisibility = <TData,>({ table }: ColumnVisibilityProps<TData>) => {
|
|
13
|
+
return (
|
|
14
|
+
<PopoverPrimitive.Root>
|
|
15
|
+
<PopoverPrimitive.Trigger asChild>
|
|
16
|
+
<IconButton
|
|
17
|
+
variant="neutralOutline"
|
|
18
|
+
aria-label="Alternar visibilidade de colunas"
|
|
19
|
+
>
|
|
20
|
+
<SlidersHorizontalIcon weight="light" />
|
|
21
|
+
</IconButton>
|
|
22
|
+
</PopoverPrimitive.Trigger>
|
|
23
|
+
<PopoverPrimitive.Portal>
|
|
24
|
+
<PopoverPrimitive.Content
|
|
25
|
+
side="bottom"
|
|
26
|
+
align="end"
|
|
27
|
+
sideOffset={-7}
|
|
28
|
+
>
|
|
29
|
+
<PopoverWrapper>
|
|
30
|
+
{table
|
|
31
|
+
.getAllColumns()
|
|
32
|
+
.filter(
|
|
33
|
+
(col) =>
|
|
34
|
+
typeof col.accessorFn !== "undefined"
|
|
35
|
+
&& col.getCanHide()
|
|
36
|
+
)
|
|
37
|
+
.map((col) => (
|
|
38
|
+
<PopoverLabel key={col.id}>
|
|
39
|
+
<Checkbox
|
|
40
|
+
checked={col.getIsVisible()}
|
|
41
|
+
onChange={(e: React.ChangeEvent<HTMLInputElement>) =>
|
|
42
|
+
col.toggleVisibility(e.target.checked)
|
|
43
|
+
}
|
|
44
|
+
/>
|
|
45
|
+
{typeof col.columnDef.header === 'string' ? col.columnDef.header : col.id}
|
|
46
|
+
</PopoverLabel>
|
|
47
|
+
))}
|
|
48
|
+
</PopoverWrapper>
|
|
49
|
+
</PopoverPrimitive.Content>
|
|
50
|
+
</PopoverPrimitive.Portal>
|
|
51
|
+
</PopoverPrimitive.Root>
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export default ColumnVisibility;
|