wca-designsystem 0.0.55 → 0.0.56
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/package.json +1 -1
- package/src/components/organismos/Tabela/SkeletonTable.tsx +4 -4
- package/src/components/organismos/Tabela/Tabela.stories.tsx +28 -139
- package/src/components/organismos/Tabela/body.tsx +42 -9
- package/src/components/organismos/Tabela/index.tsx +49 -11
- package/src/components/organismos/Tabela/pagination.tsx +1 -1
- package/src/components/organismos/Tabela/styles.ts +14 -0
package/package.json
CHANGED
|
@@ -14,11 +14,11 @@ function SkeletonTable<T extends { id: number | string }>({
|
|
|
14
14
|
}: SkeletonTableProps<T>) {
|
|
15
15
|
return (
|
|
16
16
|
<S.TabelaBody>
|
|
17
|
-
{
|
|
17
|
+
{data?.map(row => {
|
|
18
18
|
return (
|
|
19
|
-
<tr key={row.
|
|
20
|
-
{
|
|
21
|
-
<td key={cell.
|
|
19
|
+
<tr key={row.id}>
|
|
20
|
+
{columns.map(cell => (
|
|
21
|
+
<td key={cell.header}>
|
|
22
22
|
<Skeleton height={19} radius="xl" animate={true} />
|
|
23
23
|
</td>
|
|
24
24
|
))}
|
|
@@ -9,156 +9,41 @@ export default {
|
|
|
9
9
|
component: Tabela,
|
|
10
10
|
} as Meta;
|
|
11
11
|
|
|
12
|
-
const
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
exemplo: 'opa',
|
|
18
|
-
exemplo2: 'opa2',
|
|
19
|
-
children: [
|
|
20
|
-
{
|
|
21
|
-
id: '1-1',
|
|
22
|
-
name: 'Task 1',
|
|
23
|
-
price: '$10',
|
|
24
|
-
exemplo: 'opa',
|
|
25
|
-
exemplo2: 'opa2',
|
|
26
|
-
children: [
|
|
27
|
-
{
|
|
28
|
-
id: '1-10',
|
|
29
|
-
name: 'Task 10',
|
|
30
|
-
price: '$10',
|
|
31
|
-
exemplo: 'opa',
|
|
32
|
-
exemplo2: 'opa2',
|
|
33
|
-
},
|
|
34
|
-
],
|
|
35
|
-
},
|
|
36
|
-
{
|
|
37
|
-
id: '1-2',
|
|
38
|
-
name: 'Task 2',
|
|
39
|
-
price: '$10',
|
|
40
|
-
exemplo: 'opa',
|
|
41
|
-
exemplo2: 'opa2',
|
|
42
|
-
},
|
|
43
|
-
],
|
|
44
|
-
},
|
|
45
|
-
{
|
|
46
|
-
id: 2,
|
|
47
|
-
name: 'Produto 2',
|
|
48
|
-
price: '$15',
|
|
49
|
-
exemplo: 'opa',
|
|
50
|
-
exemplo2: 'opa2',
|
|
51
|
-
children: [
|
|
52
|
-
{
|
|
53
|
-
id: '2-1',
|
|
54
|
-
name: 'Task 1',
|
|
55
|
-
price: '$10',
|
|
56
|
-
exemplo: 'opa',
|
|
57
|
-
exemplo2: 'opa2',
|
|
58
|
-
},
|
|
59
|
-
{
|
|
60
|
-
id: '2-2',
|
|
61
|
-
name: 'Task 2',
|
|
62
|
-
price: '$10',
|
|
63
|
-
exemplo: 'opa',
|
|
64
|
-
exemplo2: 'opa2',
|
|
65
|
-
},
|
|
66
|
-
],
|
|
67
|
-
},
|
|
68
|
-
{
|
|
69
|
-
id: 3,
|
|
70
|
-
name: 'Produto 3',
|
|
71
|
-
price: '$20',
|
|
72
|
-
exemplo: 'opa',
|
|
73
|
-
exemplo2: 'opa2',
|
|
74
|
-
children: [
|
|
75
|
-
{
|
|
76
|
-
id: '3-1',
|
|
77
|
-
name: 'Task 1',
|
|
78
|
-
price: '$10',
|
|
79
|
-
exemplo: 'opa',
|
|
80
|
-
exemplo2: 'opa2',
|
|
81
|
-
},
|
|
82
|
-
{
|
|
83
|
-
id: '3-2',
|
|
84
|
-
name: 'Task 2',
|
|
85
|
-
price: '$10',
|
|
86
|
-
exemplo: 'opa',
|
|
87
|
-
exemplo2: 'opa2',
|
|
88
|
-
},
|
|
89
|
-
],
|
|
90
|
-
},
|
|
91
|
-
{
|
|
92
|
-
id: 4,
|
|
93
|
-
name: 'Produto 3',
|
|
94
|
-
price: '$20',
|
|
95
|
-
exemplo: 'opa',
|
|
96
|
-
exemplo2: 'opa2',
|
|
97
|
-
children: [
|
|
98
|
-
{
|
|
99
|
-
id: '4-1',
|
|
100
|
-
name: 'Task 1',
|
|
101
|
-
price: '$10',
|
|
102
|
-
exemplo: 'opa',
|
|
103
|
-
exemplo2: 'opa2',
|
|
104
|
-
},
|
|
105
|
-
{
|
|
106
|
-
id: '4-2',
|
|
107
|
-
name: 'Task 2',
|
|
108
|
-
price: '$10',
|
|
109
|
-
exemplo: 'opa',
|
|
110
|
-
exemplo2: 'opa2',
|
|
111
|
-
},
|
|
112
|
-
],
|
|
113
|
-
},
|
|
114
|
-
{
|
|
115
|
-
id: 5,
|
|
116
|
-
name: 'Produto 3',
|
|
117
|
-
price: '$20',
|
|
118
|
-
exemplo: 'opa',
|
|
119
|
-
exemplo2: 'opa2',
|
|
120
|
-
children: [
|
|
121
|
-
{
|
|
122
|
-
id: '5-1',
|
|
123
|
-
name: 'Task 1',
|
|
124
|
-
price: '$10',
|
|
125
|
-
exemplo: 'opa',
|
|
126
|
-
exemplo2: 'opa2',
|
|
127
|
-
},
|
|
128
|
-
{
|
|
129
|
-
id: '5-2',
|
|
130
|
-
name: 'Task 2',
|
|
131
|
-
price: '$10',
|
|
132
|
-
exemplo: 'opa',
|
|
133
|
-
exemplo2: 'opa2',
|
|
134
|
-
},
|
|
135
|
-
],
|
|
136
|
-
},
|
|
137
|
-
{
|
|
138
|
-
id: 6,
|
|
139
|
-
name: 'Produto 3',
|
|
140
|
-
price: '$20',
|
|
141
|
-
exemplo: 'opa',
|
|
142
|
-
exemplo2: 'opa2',
|
|
143
|
-
children: [
|
|
12
|
+
const generateData = () => {
|
|
13
|
+
const data = [];
|
|
14
|
+
|
|
15
|
+
for (let i = 1; i <= 101; i++) {
|
|
16
|
+
const children = [
|
|
144
17
|
{
|
|
145
|
-
id:
|
|
146
|
-
name:
|
|
18
|
+
id: `${i}-1`,
|
|
19
|
+
name: `Task ${i}-1`,
|
|
147
20
|
price: '$10',
|
|
148
21
|
exemplo: 'opa',
|
|
149
22
|
exemplo2: 'opa2',
|
|
150
23
|
},
|
|
151
24
|
{
|
|
152
|
-
id:
|
|
153
|
-
name:
|
|
25
|
+
id: `${i}-2`,
|
|
26
|
+
name: `Task ${i}-2`,
|
|
154
27
|
price: '$10',
|
|
155
28
|
exemplo: 'opa',
|
|
156
29
|
exemplo2: 'opa2',
|
|
157
30
|
},
|
|
158
|
-
]
|
|
159
|
-
},
|
|
160
|
-
];
|
|
31
|
+
];
|
|
161
32
|
|
|
33
|
+
data.push({
|
|
34
|
+
id: i,
|
|
35
|
+
name: `Produto ${i}`,
|
|
36
|
+
price: '$20',
|
|
37
|
+
exemplo: 'opa',
|
|
38
|
+
exemplo2: 'opa2',
|
|
39
|
+
children,
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
return data;
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
const data = generateData();
|
|
162
47
|
const columns: any = [
|
|
163
48
|
{
|
|
164
49
|
key: 'expand',
|
|
@@ -190,6 +75,7 @@ TabelaProps.args = {
|
|
|
190
75
|
columns: columns,
|
|
191
76
|
color: theme.colors.blue,
|
|
192
77
|
hasFiltersButtons: true,
|
|
78
|
+
|
|
193
79
|
ordenarPor: {
|
|
194
80
|
label: 'Ordenar por: Maior',
|
|
195
81
|
value: 'Maior',
|
|
@@ -201,6 +87,9 @@ TabelaProps.args = {
|
|
|
201
87
|
rowSelection: [],
|
|
202
88
|
setRowSelection: () => console.log('selecionou'),
|
|
203
89
|
setFiltrosPor: () => console.log('aqui'),
|
|
90
|
+
fetchMoreData: () => console.log('fetchMoreData'),
|
|
91
|
+
hasInfinitScrool: true,
|
|
92
|
+
hasMoreData: true,
|
|
204
93
|
setGlobalFilter: () => console.log('aqui'),
|
|
205
94
|
setPagination: () => console.log('aqui'),
|
|
206
95
|
quantidadeDeRegistros: 10,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React, { useState, useCallback } from 'react';
|
|
1
|
+
import React, { useState, useCallback, useRef, useEffect } from 'react';
|
|
2
2
|
import { Checkbox } from '@mantine/core';
|
|
3
3
|
import { Column } from '.';
|
|
4
4
|
import * as S from './styles';
|
|
@@ -18,6 +18,9 @@ export interface BodyTableProps<
|
|
|
18
18
|
hasSelect?: boolean;
|
|
19
19
|
rowSelection?: Array<T>;
|
|
20
20
|
setRowSelection?: React.Dispatch<React.SetStateAction<Array<T>>> | undefined;
|
|
21
|
+
hasInfinitScrool?: boolean;
|
|
22
|
+
hasMoreData?: boolean;
|
|
23
|
+
fetchMoreData?: () => void;
|
|
21
24
|
}
|
|
22
25
|
|
|
23
26
|
function BodyTable<T extends { id: string | number; children: Array<T> }>(
|
|
@@ -29,10 +32,15 @@ function BodyTable<T extends { id: string | number; children: Array<T> }>(
|
|
|
29
32
|
acoesChildren,
|
|
30
33
|
hasSelect,
|
|
31
34
|
rowSelection,
|
|
35
|
+
fetchMoreData,
|
|
36
|
+
hasMoreData,
|
|
37
|
+
hasInfinitScrool,
|
|
32
38
|
setRowSelection,
|
|
33
39
|
color,
|
|
34
40
|
} = props;
|
|
35
41
|
|
|
42
|
+
const tableBodyRef = useRef<HTMLTableSectionElement | null>(null);
|
|
43
|
+
|
|
36
44
|
const [expandedRows, setExpandedRows] = useState<Set<number | string>>(
|
|
37
45
|
new Set()
|
|
38
46
|
);
|
|
@@ -62,6 +70,30 @@ function BodyTable<T extends { id: string | number; children: Array<T> }>(
|
|
|
62
70
|
});
|
|
63
71
|
}, []);
|
|
64
72
|
|
|
73
|
+
useEffect(() => {
|
|
74
|
+
if (!fetchMoreData || !hasInfinitScrool || !tableBodyRef.current) return;
|
|
75
|
+
|
|
76
|
+
const handleScroll = () => {
|
|
77
|
+
debugger;
|
|
78
|
+
const element = tableBodyRef.current;
|
|
79
|
+
if (element) {
|
|
80
|
+
const atBottom =
|
|
81
|
+
element.scrollHeight - element.scrollTop === element.clientHeight;
|
|
82
|
+
if (atBottom) {
|
|
83
|
+
debugger;
|
|
84
|
+
fetchMoreData(); // Dispara a função ao atingir o final do scroll
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
const currentRef = tableBodyRef.current;
|
|
90
|
+
currentRef?.addEventListener('scroll', handleScroll);
|
|
91
|
+
|
|
92
|
+
return () => {
|
|
93
|
+
currentRef?.removeEventListener('scroll', handleScroll);
|
|
94
|
+
};
|
|
95
|
+
}, [fetchMoreData, hasInfinitScrool]);
|
|
96
|
+
|
|
65
97
|
function renderRows(
|
|
66
98
|
item: T,
|
|
67
99
|
columns: Column<T>[],
|
|
@@ -79,7 +111,6 @@ function BodyTable<T extends { id: string | number; children: Array<T> }>(
|
|
|
79
111
|
|
|
80
112
|
return [
|
|
81
113
|
<tr key={`row-${item.id}`}>
|
|
82
|
-
{/* Checkbox para seleção */}
|
|
83
114
|
{hasSelect && (
|
|
84
115
|
<td className="select">
|
|
85
116
|
<Checkbox
|
|
@@ -96,8 +127,6 @@ function BodyTable<T extends { id: string | number; children: Array<T> }>(
|
|
|
96
127
|
/>
|
|
97
128
|
</td>
|
|
98
129
|
)}
|
|
99
|
-
|
|
100
|
-
{/* Colunas dinâmicas */}
|
|
101
130
|
{columns.map((column, colIndex) => {
|
|
102
131
|
if (column.key === 'expand') {
|
|
103
132
|
return (
|
|
@@ -137,12 +166,10 @@ function BodyTable<T extends { id: string | number; children: Array<T> }>(
|
|
|
137
166
|
);
|
|
138
167
|
}
|
|
139
168
|
|
|
140
|
-
// Verifica se existe a prop 'cell'
|
|
141
169
|
if (column.cell) {
|
|
142
170
|
return <td key={`cell-${colIndex}`}>{column.cell(item)}</td>;
|
|
143
171
|
}
|
|
144
172
|
|
|
145
|
-
// Render padrão
|
|
146
173
|
return column.render ? (
|
|
147
174
|
<td
|
|
148
175
|
key={`column-${colIndex}`}
|
|
@@ -168,8 +195,6 @@ function BodyTable<T extends { id: string | number; children: Array<T> }>(
|
|
|
168
195
|
);
|
|
169
196
|
})}
|
|
170
197
|
</tr>,
|
|
171
|
-
|
|
172
|
-
// Renderiza os filhos recursivamente, caso existam
|
|
173
198
|
...(expandedRows.has(item.id)
|
|
174
199
|
? item.children.flatMap(child =>
|
|
175
200
|
renderRows(
|
|
@@ -186,11 +211,12 @@ function BodyTable<T extends { id: string | number; children: Array<T> }>(
|
|
|
186
211
|
: []),
|
|
187
212
|
];
|
|
188
213
|
}
|
|
214
|
+
|
|
189
215
|
return (
|
|
190
216
|
<S.TabelaBody
|
|
217
|
+
ref={tableBodyRef}
|
|
191
218
|
color={color}
|
|
192
219
|
hasselect={hasSelect ? 'true' : 'false'}
|
|
193
|
-
// style={{ left: hasSelect ? '52px' : '17px' }}
|
|
194
220
|
>
|
|
195
221
|
{data.flatMap(item =>
|
|
196
222
|
renderRows(
|
|
@@ -204,6 +230,13 @@ function BodyTable<T extends { id: string | number; children: Array<T> }>(
|
|
|
204
230
|
color
|
|
205
231
|
)
|
|
206
232
|
)}
|
|
233
|
+
{hasInfinitScrool && hasMoreData && (
|
|
234
|
+
<tr>
|
|
235
|
+
<td colSpan={columns.length + (hasSelect ? 1 : 0)}>
|
|
236
|
+
<div className="load-more" style={{ height: '1px' }} />
|
|
237
|
+
</td>
|
|
238
|
+
</tr>
|
|
239
|
+
)}
|
|
207
240
|
</S.TabelaBody>
|
|
208
241
|
);
|
|
209
242
|
}
|
|
@@ -1,13 +1,11 @@
|
|
|
1
|
-
import React from 'react';
|
|
1
|
+
import React, { useEffect, useRef, useState } from 'react';
|
|
2
2
|
import BodyTable from './body';
|
|
3
3
|
import HeaderTabela from './header';
|
|
4
4
|
import Paginacao from './pagination';
|
|
5
5
|
import SkeletonTable from './SkeletonTable';
|
|
6
6
|
import * as S from './styles';
|
|
7
|
-
import { useState } from 'react';
|
|
8
7
|
import TabelaHeader from '../../molecules/TabelaHeader';
|
|
9
8
|
|
|
10
|
-
// Tipo genérico para as colunas
|
|
11
9
|
export type Column<T> = {
|
|
12
10
|
key: keyof T;
|
|
13
11
|
header: string;
|
|
@@ -63,6 +61,8 @@ export type CustomTableProps<
|
|
|
63
61
|
};
|
|
64
62
|
quantidadeRegistros?: number | undefined;
|
|
65
63
|
tamanhoPaginacao?: number | undefined;
|
|
64
|
+
fetchMoreData?: (() => void) | undefined;
|
|
65
|
+
hasInfinitScrool?: boolean;
|
|
66
66
|
};
|
|
67
67
|
|
|
68
68
|
const Tabela = <T extends { id: string | number; children: Array<T> }>({
|
|
@@ -85,6 +85,7 @@ const Tabela = <T extends { id: string | number; children: Array<T> }>({
|
|
|
85
85
|
setGlobalFilter,
|
|
86
86
|
hasOrder,
|
|
87
87
|
headerSelection,
|
|
88
|
+
fetchMoreData,
|
|
88
89
|
setOrdenarPor,
|
|
89
90
|
setSimples,
|
|
90
91
|
handleAdicionar,
|
|
@@ -92,22 +93,52 @@ const Tabela = <T extends { id: string | number; children: Array<T> }>({
|
|
|
92
93
|
quantidadeRegistros,
|
|
93
94
|
tamanhoPaginacao,
|
|
94
95
|
fixedPosition,
|
|
96
|
+
hasInfinitScrool,
|
|
95
97
|
}: CustomTableProps<T>) => {
|
|
96
98
|
const [toogleFiltros, setToogleFiltros] = useState(false);
|
|
97
|
-
|
|
98
99
|
const [formatedColumns, setFormatedColumns] = useState(
|
|
99
|
-
columns.map(column => {
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
};
|
|
104
|
-
})
|
|
100
|
+
columns.map(column => ({
|
|
101
|
+
...column,
|
|
102
|
+
active: true,
|
|
103
|
+
}))
|
|
105
104
|
);
|
|
106
105
|
|
|
106
|
+
const [tamScroll, setTamScroll] = useState<number>(0);
|
|
107
107
|
const columnsToShow = formatedColumns.filter(
|
|
108
108
|
column => column.active === true
|
|
109
109
|
);
|
|
110
110
|
|
|
111
|
+
const topScrollRef = useRef<HTMLDivElement>(null);
|
|
112
|
+
const bottomScrollRef = useRef<HTMLDivElement>(null);
|
|
113
|
+
|
|
114
|
+
// Sincroniza o scroll horizontal entre os dois scrollbars
|
|
115
|
+
useEffect(() => {
|
|
116
|
+
const topScroll = topScrollRef.current;
|
|
117
|
+
const bottomScroll = bottomScrollRef.current;
|
|
118
|
+
|
|
119
|
+
if (topScroll && bottomScroll) {
|
|
120
|
+
const syncScroll = (source: HTMLDivElement, target: HTMLDivElement) => {
|
|
121
|
+
target.scrollLeft = source.scrollLeft;
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
setTamScroll(bottomScroll.scrollWidth);
|
|
125
|
+
const onTopScroll = () =>
|
|
126
|
+
topScroll && bottomScroll && syncScroll(topScroll, bottomScroll);
|
|
127
|
+
const onBottomScroll = () =>
|
|
128
|
+
topScroll && bottomScroll && syncScroll(bottomScroll, topScroll);
|
|
129
|
+
|
|
130
|
+
topScroll.addEventListener('scroll', onTopScroll);
|
|
131
|
+
bottomScroll.addEventListener('scroll', onBottomScroll);
|
|
132
|
+
|
|
133
|
+
return () => {
|
|
134
|
+
topScroll.removeEventListener('scroll', onTopScroll);
|
|
135
|
+
bottomScroll.removeEventListener('scroll', onBottomScroll);
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
return;
|
|
140
|
+
}, []);
|
|
141
|
+
|
|
111
142
|
return (
|
|
112
143
|
<S.TableWrapper>
|
|
113
144
|
<TabelaHeader
|
|
@@ -125,7 +156,11 @@ const Tabela = <T extends { id: string | number; children: Array<T> }>({
|
|
|
125
156
|
ordenarPor={ordenarPor}
|
|
126
157
|
setOrdenarPor={setOrdenarPor}
|
|
127
158
|
/>
|
|
128
|
-
|
|
159
|
+
{/* Scroll horizontal no topo */}
|
|
160
|
+
<S.ScrollWrapper ref={topScrollRef}>
|
|
161
|
+
<S.FakeTable columnswidth={tamScroll} />
|
|
162
|
+
</S.ScrollWrapper>
|
|
163
|
+
<S.TableContainer ref={bottomScrollRef}>
|
|
129
164
|
<S.StyledTable>
|
|
130
165
|
{data?.length == 0 ? (
|
|
131
166
|
<></>
|
|
@@ -156,7 +191,10 @@ const Tabela = <T extends { id: string | number; children: Array<T> }>({
|
|
|
156
191
|
) : (
|
|
157
192
|
<BodyTable
|
|
158
193
|
color={color}
|
|
194
|
+
fetchMoreData={fetchMoreData}
|
|
195
|
+
hasInfinitScrool={hasInfinitScrool}
|
|
159
196
|
hasSelect={hasSelect}
|
|
197
|
+
hasMoreData={quantidadeRegistros !== data.length}
|
|
160
198
|
rowSelection={rowSelection}
|
|
161
199
|
setRowSelection={setRowSelection}
|
|
162
200
|
acoesChildren={acoesChildren}
|
|
@@ -12,6 +12,9 @@ export const TableWrapper = styled.div`
|
|
|
12
12
|
${() => css`
|
|
13
13
|
max-width: 1500px;
|
|
14
14
|
overflow-x: hidden;
|
|
15
|
+
display: flex;
|
|
16
|
+
flex-direction: column;
|
|
17
|
+
gap: 1rem;
|
|
15
18
|
`}
|
|
16
19
|
`;
|
|
17
20
|
|
|
@@ -231,3 +234,14 @@ export const PaginationButtons = styled.div<ColorsProp>`
|
|
|
231
234
|
}
|
|
232
235
|
`}
|
|
233
236
|
`;
|
|
237
|
+
|
|
238
|
+
export const ScrollWrapper = styled.div`
|
|
239
|
+
overflow-x: auto;
|
|
240
|
+
width: 100%;
|
|
241
|
+
margin-bottom: -15px;
|
|
242
|
+
`;
|
|
243
|
+
|
|
244
|
+
export const FakeTable = styled.div<{ columnswidth: number }>`
|
|
245
|
+
width: ${({ columnswidth }) => columnswidth}px;
|
|
246
|
+
height: 1px; /* Apenas para criar a barra de rolagem */
|
|
247
|
+
`;
|