@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.
Files changed (46) hide show
  1. package/components/Alert/Alert.stories.tsx +94 -18
  2. package/components/Badge/Badge.stories.tsx +114 -0
  3. package/components/Badge/Badge.styles.ts +36 -0
  4. package/components/Badge/Badge.tsx +23 -0
  5. package/components/Badge/Badge.types.ts +11 -0
  6. package/components/Badge/index.ts +2 -0
  7. package/components/Button/Button.appearance.ts +1 -1
  8. package/components/Button/Button.stories.tsx +99 -18
  9. package/components/Checkbox/Checkbox.stories.tsx +107 -7
  10. package/components/DatePicker/DatePicker.styles.ts +1 -0
  11. package/components/DatePicker/DatePicker.tsx +9 -10
  12. package/components/IconButton/IconButton.sizes.ts +7 -7
  13. package/components/IconButton/IconButton.tsx +0 -1
  14. package/components/InputOpt/InputOpt.stories.tsx +30 -44
  15. package/components/Select/Select.stories.tsx +80 -19
  16. package/components/Select/Select.tsx +7 -9
  17. package/components/Table/Datatable.stories.tsx +186 -0
  18. package/components/Table/Table.stories.tsx +127 -46
  19. package/components/Table/Table.styles.ts +83 -8
  20. package/components/Table/Table.tsx +292 -142
  21. package/components/Table/Table.types.ts +104 -12
  22. package/components/Table/components/ColumnVisibility/ColumnVisibility.style.ts +46 -0
  23. package/components/Table/components/ColumnVisibility/ColumnVisibility.tsx +55 -0
  24. package/components/Table/components/DatatableColumnFilterMenu/DatatableColumnFilterMenu.styles.ts +120 -0
  25. package/components/Table/components/DatatableColumnFilterMenu/DatatableColumnFilterMenu.tsx +228 -0
  26. package/components/Table/components/DatatableColumnFilterMenu/index.ts +1 -0
  27. package/components/Table/components/DatatableTopBar/DatatableTopBar.styles.ts +25 -0
  28. package/components/Table/components/DatatableTopBar/DatatableTopBar.tsx +89 -0
  29. package/components/Table/components/DatatableTopBar/index.ts +1 -0
  30. package/components/Table/components/SearchInput/SearchInput.tsx +30 -0
  31. package/components/Table/components/TableHeader/TableHeader.tsx +98 -0
  32. package/components/Table/components/TablePagination/TablePagination.tsx +78 -0
  33. package/components/Table/components/index.ts +6 -0
  34. package/components/Table/hooks/useDatatableFilters.ts +88 -0
  35. package/components/Table/stories.fixtures.ts +100 -0
  36. package/components/Table/tanstack-table.d.ts +10 -0
  37. package/components/Table/utils/dateRangeFilterFn.ts +33 -0
  38. package/components/Table/utils/index.ts +2 -1
  39. package/components/Tabs/Tabs.stories.tsx +152 -0
  40. package/components/Tabs/Tabs.styles.ts +12 -0
  41. package/components/Tabs/Tabs.tsx +34 -0
  42. package/components/Tabs/Tabs.types.ts +15 -0
  43. package/components/Tabs/index.ts +2 -0
  44. package/components/TextField/TextField.stories.tsx +135 -12
  45. package/components/index.ts +3 -0
  46. 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
- flexRender,
4
- getCoreRowModel,
5
- getPaginationRowModel,
6
- useReactTable,
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
- EmptyMessage,
17
- FooterText,
18
- PaginationContainer,
19
- PaginationControls,
20
- PaginationElipsis,
21
- StyledTable,
22
- TableFooter,
23
- TableWrapper,
24
- Td,
25
- Th,
26
- Tr,
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
- state: {
51
- pagination,
52
- },
53
- onPaginationChange: setPagination,
54
- getCoreRowModel: getCoreRowModel(),
55
- getPaginationRowModel: getPaginationRowModel(),
56
- });
57
-
58
- const total = table.getFilteredRowModel().rows.length;
59
- const pageCount = table.getPageCount();
60
- const hasPagination = showPagination && pageCount > 1;
61
- const currentPage = pagination.pageIndex + 1;
62
- const pageNumbersArray = getPageNumbers({ currentPage, pageCount });
63
-
64
- return (
65
- <TableWrapper>
66
- <StyledTable>
67
- <thead>
68
- {table.getHeaderGroups().map((hg) => (
69
- <tr key={hg.id}>
70
- {hg.headers.map((header) => (
71
- <Th key={header.id}>
72
- {flexRender(
73
- header.column.columnDef.header,
74
- header.getContext()
75
- )}
76
- </Th>
77
- ))}
78
- </tr>
79
- ))}
80
- </thead>
81
-
82
- <tbody>
83
- {table.getRowModel().rows.length === 0 ? (
84
- <tr>
85
- <EmptyMessage colSpan={columns.length}>{emptyTitle}</EmptyMessage>
86
- </tr>
87
- ) : (
88
- table.getRowModel().rows.map((row) => (
89
- <Tr key={row.id}>
90
- {row.getVisibleCells().map((cell) => (
91
- <Td key={cell.id}>
92
- {flexRender(cell.column.columnDef.cell, cell.getContext())}
93
- </Td>
94
- ))}
95
- </Tr>
96
- ))
97
- )}
98
- </tbody>
99
- </StyledTable>
100
-
101
- {(footerText || hasPagination) && total > 0 && (
102
- <TableFooter $hasPagination={hasPagination}>
103
- {footerText && (
104
- <FooterText>
105
- {footerText({
106
- total: data.length,
107
- filtered: total,
108
- })}
109
- </FooterText>
110
- )}
111
- {hasPagination && (
112
- <PaginationContainer>
113
- <PaginationControls>
114
- <IconButton
115
- variant="outline"
116
- onClick={() => table.previousPage()}
117
- disabled={!table.getCanPreviousPage()}
118
- >
119
- <CaretLeftIcon />
120
- </IconButton>
121
- {pageNumbersArray.map((page, index) =>
122
- page === "ellipsis" ? (
123
- <PaginationElipsis key={`ellipsis-${index}`}>
124
- <DotsThreeIcon size={16} />
125
- </PaginationElipsis>
126
- ) : (
127
- <Button
128
- key={page}
129
- size="sm"
130
- variant={page === currentPage ? "solid" : "ghost"}
131
- onClick={() => {
132
- if (typeof page === "number") {
133
- table.setPageIndex(page - 1);
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
- {page}
138
- </Button>
139
- )
140
- )}
141
- <IconButton
142
- variant="outline"
143
- onClick={() => table.nextPage()}
144
- disabled={!table.getCanNextPage()}
145
- >
146
- <CaretRightIcon />
147
- </IconButton>
148
- </PaginationControls>
149
- </PaginationContainer>
150
- )}
151
- </TableFooter>
152
- )}
153
- </TableWrapper>
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 { ColumnDef } from '@tanstack/react-table';
1
+ import { ReactNode } from 'react'
2
+ import { ColumnDef, SortingState, VisibilityState } from '@tanstack/react-table'
2
3
 
3
- export type TTableProps<TData> = {
4
- data: TData[]
5
- columns: ColumnDef<TData, unknown>[]
4
+ export type TableMode = 'client' | 'server'
5
+ export type TableVariant = 'table' | 'datatable'
6
6
 
7
- emptyTitle?: string
8
- pageSize?: number
9
- showPagination?: boolean
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
- footerText?: (info: {
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;