@rainbow-oh/yee-c 0.10.0 → 0.12.0
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/dist/cjs/Anchor/style/index.less +3 -3
- package/dist/cjs/Button/demo/icon.js +1 -1
- package/dist/cjs/Button/style/index.less +72 -52
- package/dist/cjs/DatePicker/style/mobile.less +1 -1
- package/dist/cjs/Descriptions/style/index.less +6 -6
- package/dist/cjs/FloatButton/style/index.less +2 -2
- package/dist/cjs/Form/style/index.less +21 -21
- package/dist/cjs/ImageViewer/style/index.less +13 -13
- package/dist/cjs/Input/style/index.less +1 -1
- package/dist/cjs/InputNumber/style/index.less +9 -9
- package/dist/cjs/Menu/style/index.less +1 -1
- package/dist/cjs/PickerPanel/style/index.less +5 -5
- package/dist/cjs/RangePicker/style/index.less +53 -52
- package/dist/cjs/Rate/style/index.less +1 -1
- package/dist/cjs/Search/style/index.less +10 -10
- package/dist/cjs/Segmented/style/index.less +2 -2
- package/dist/cjs/Selector/selector.js +1 -1
- package/dist/cjs/Selector/style/index.less +1 -1
- package/dist/cjs/Table/demo/resizable.d.ts +3 -0
- package/dist/cjs/Table/demo/resizable.js +143 -0
- package/dist/cjs/Table/hooks/useColumnResize.d.ts +30 -0
- package/dist/cjs/Table/hooks/useColumnResize.js +217 -0
- package/dist/cjs/Table/hooks/useColumns.d.ts +3 -1
- package/dist/cjs/Table/hooks/useColumns.js +14 -3
- package/dist/cjs/Table/interface.d.ts +49 -0
- package/dist/cjs/Table/style/index.less +43 -1
- package/dist/cjs/Table/table.js +62 -9
- package/dist/cjs/Table/theader/cell.js +46 -4
- package/dist/cjs/Table/util/index.d.ts +19 -1
- package/dist/cjs/Table/util/index.js +22 -0
- package/dist/cjs/Tabs/interface.d.ts +1 -1
- package/dist/cjs/Tabs/style/index.less +9 -9
- package/dist/cjs/Tabs/tab-header/index.js +5 -1
- package/dist/cjs/Tour/demo/indicators.js +1 -1
- package/dist/cjs/Tour/demo/placement.js +1 -1
- package/dist/cjs/Transfer/style/index.less +1 -1
- package/dist/cjs/Tree/style/index.less +3 -3
- package/dist/cjs/TreeSelect/style/index.less +1 -1
- package/dist/cjs/Upload/style/index.less +11 -12
- package/dist/cjs/WheelPicker/style/index.less +3 -3
- package/dist/cjs/locale/interface.d.ts +1 -0
- package/dist/cjs/locale/lang/en_US.js +2 -1
- package/dist/cjs/locale/lang/ja_JP.js +2 -1
- package/dist/cjs/locale/lang/zh_CN.js +2 -1
- package/dist/cjs/locale/lang/zh_TW.js +2 -1
- package/dist/cjs/message/style/index.less +4 -4
- package/dist/cjs/style/color.blue.css +254 -290
- package/dist/cjs/style/color.crimson.css +434 -471
- package/dist/cjs/style/color.css +736 -508
- package/dist/cjs/style/color.dark.css +464 -501
- package/dist/cjs/style/color.gemini.css +134 -0
- package/dist/cjs/style/color.green.css +459 -495
- package/dist/cjs/style/color.jam.css +463 -500
- package/dist/cjs/style/color.navy.css +466 -503
- package/dist/cjs/style/color.peach.css +464 -501
- package/dist/cjs/style/color.pine.css +462 -500
- package/dist/cjs/style/color.pitaya.css +465 -502
- package/dist/cjs/style/color.pumpkin.css +464 -501
- package/dist/cjs/style/color.ruby.css +466 -503
- package/dist/cjs/style/size.css +144 -2
- package/dist/cjs/style/themes.css +4999 -5268
- package/dist/cjs/style/variables.css +882 -512
- package/dist/cjs/version.d.ts +1 -1
- package/dist/cjs/version.js +1 -1
- package/dist/esm/Anchor/style/index.less +3 -3
- package/dist/esm/Button/demo/icon.js +7 -4
- package/dist/esm/Button/style/index.less +72 -52
- package/dist/esm/DatePicker/style/mobile.less +1 -1
- package/dist/esm/Descriptions/style/index.less +6 -6
- package/dist/esm/FloatButton/style/index.less +2 -2
- package/dist/esm/Form/style/index.less +21 -21
- package/dist/esm/ImageViewer/style/index.less +13 -13
- package/dist/esm/Input/style/index.less +1 -1
- package/dist/esm/InputNumber/style/index.less +9 -9
- package/dist/esm/Menu/style/index.less +1 -1
- package/dist/esm/PickerPanel/style/index.less +5 -5
- package/dist/esm/RangePicker/style/index.less +53 -52
- package/dist/esm/Rate/style/index.less +1 -1
- package/dist/esm/Search/style/index.less +10 -10
- package/dist/esm/Segmented/style/index.less +2 -2
- package/dist/esm/Selector/selector.js +1 -1
- package/dist/esm/Selector/style/index.less +1 -1
- package/dist/esm/Table/demo/resizable.d.ts +3 -0
- package/dist/esm/Table/demo/resizable.js +110 -0
- package/dist/esm/Table/hooks/useColumnResize.d.ts +30 -0
- package/dist/esm/Table/hooks/useColumnResize.js +206 -0
- package/dist/esm/Table/hooks/useColumns.d.ts +3 -1
- package/dist/esm/Table/hooks/useColumns.js +9 -4
- package/dist/esm/Table/interface.d.ts +49 -0
- package/dist/esm/Table/style/index.less +43 -1
- package/dist/esm/Table/table.js +83 -13
- package/dist/esm/Table/theader/cell.js +38 -3
- package/dist/esm/Table/util/index.d.ts +19 -1
- package/dist/esm/Table/util/index.js +37 -0
- package/dist/esm/Tabs/interface.d.ts +1 -1
- package/dist/esm/Tabs/style/index.less +9 -9
- package/dist/esm/Tabs/tab-header/index.js +7 -2
- package/dist/esm/Tour/demo/indicators.js +1 -1
- package/dist/esm/Tour/demo/placement.js +1 -1
- package/dist/esm/Transfer/style/index.less +1 -1
- package/dist/esm/Tree/style/index.less +3 -3
- package/dist/esm/TreeSelect/style/index.less +1 -1
- package/dist/esm/Upload/style/index.less +11 -12
- package/dist/esm/WheelPicker/style/index.less +3 -3
- package/dist/esm/index.js +1 -0
- package/dist/esm/locale/interface.d.ts +1 -0
- package/dist/esm/locale/lang/en_US.js +2 -1
- package/dist/esm/locale/lang/ja_JP.js +2 -1
- package/dist/esm/locale/lang/zh_CN.js +2 -1
- package/dist/esm/locale/lang/zh_TW.js +2 -1
- package/dist/esm/message/style/index.less +4 -4
- package/dist/esm/style/color.blue.css +254 -290
- package/dist/esm/style/color.crimson.css +434 -471
- package/dist/esm/style/color.css +736 -508
- package/dist/esm/style/color.dark.css +464 -501
- package/dist/esm/style/color.gemini.css +134 -0
- package/dist/esm/style/color.green.css +459 -495
- package/dist/esm/style/color.jam.css +463 -500
- package/dist/esm/style/color.navy.css +466 -503
- package/dist/esm/style/color.peach.css +464 -501
- package/dist/esm/style/color.pine.css +462 -500
- package/dist/esm/style/color.pitaya.css +465 -502
- package/dist/esm/style/color.pumpkin.css +464 -501
- package/dist/esm/style/color.ruby.css +466 -503
- package/dist/esm/style/size.css +144 -2
- package/dist/esm/style/themes.css +4999 -5268
- package/dist/esm/style/variables.css +882 -512
- package/dist/esm/version.d.ts +1 -1
- package/dist/esm/version.js +1 -1
- package/dist/umd/yee-c.min.css +80 -22
- package/dist/umd/yee-c.min.js +1 -1
- package/package.json +4 -2
- package/src/Anchor/style/index.less +3 -3
- package/src/Button/demo/icon.tsx +5 -3
- package/src/Button/style/index.less +72 -52
- package/src/DatePicker/style/mobile.less +1 -1
- package/src/Descriptions/style/index.less +6 -6
- package/src/FloatButton/style/index.less +2 -2
- package/src/Form/style/index.less +21 -21
- package/src/ImageViewer/style/index.less +13 -13
- package/src/Input/style/index.less +1 -1
- package/src/InputNumber/style/index.less +9 -9
- package/src/Menu/style/index.less +1 -1
- package/src/PickerPanel/style/index.less +5 -5
- package/src/RangePicker/style/index.less +53 -52
- package/src/Rate/style/index.less +1 -1
- package/src/Search/style/index.less +10 -10
- package/src/Segmented/style/index.less +2 -2
- package/src/Selector/selector.tsx +1 -1
- package/src/Selector/style/index.less +1 -1
- package/src/Table/demo/resizable.tsx +106 -0
- package/src/Table/hooks/useColumnResize.ts +224 -0
- package/src/Table/hooks/useColumns.ts +18 -3
- package/src/Table/index.en-US.md +7 -0
- package/src/Table/index.zh-CN.md +7 -0
- package/src/Table/interface.ts +52 -0
- package/src/Table/style/index.less +43 -1
- package/src/Table/table-resize.test.tsx +234 -0
- package/src/Table/table.tsx +90 -13
- package/src/Table/theader/cell.tsx +52 -2
- package/src/Table/util/index.ts +44 -1
- package/src/Tabs/interface.ts +1 -1
- package/src/Tabs/style/index.less +9 -9
- package/src/Tabs/tab-header/index.tsx +5 -1
- package/src/Tour/demo/indicators.tsx +2 -2
- package/src/Tour/demo/placement.tsx +1 -1
- package/src/Transfer/style/index.less +1 -1
- package/src/Tree/style/index.less +3 -3
- package/src/TreeSelect/style/index.less +1 -1
- package/src/Upload/style/index.less +11 -12
- package/src/WheelPicker/style/index.less +3 -3
- package/src/index.ts +1 -0
- package/src/locale/interface.ts +1 -0
- package/src/locale/lang/en_US.ts +1 -0
- package/src/locale/lang/ja_JP.ts +1 -0
- package/src/locale/lang/zh_CN.ts +1 -0
- package/src/locale/lang/zh_TW.ts +1 -0
- package/src/message/style/index.less +4 -4
- package/src/style/color.blue.css +254 -290
- package/src/style/color.crimson.css +434 -471
- package/src/style/color.css +736 -508
- package/src/style/color.dark.css +464 -501
- package/src/style/color.gemini.css +134 -0
- package/src/style/color.green.css +459 -495
- package/src/style/color.jam.css +463 -500
- package/src/style/color.navy.css +466 -503
- package/src/style/color.peach.css +464 -501
- package/src/style/color.pine.css +462 -500
- package/src/style/color.pitaya.css +465 -502
- package/src/style/color.pumpkin.css +464 -501
- package/src/style/color.ruby.css +466 -503
- package/src/style/size.css +144 -2
- package/src/version.ts +1 -1
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
// @vitest-environment jsdom
|
|
2
|
+
import { act, cleanup, render } from '@testing-library/react';
|
|
3
|
+
import React from 'react';
|
|
4
|
+
import { afterEach, describe, expect, it, vi } from 'vitest';
|
|
5
|
+
import Table from './table';
|
|
6
|
+
|
|
7
|
+
import type { ColumnProps } from './interface';
|
|
8
|
+
|
|
9
|
+
const columns: ColumnProps[] = [
|
|
10
|
+
{ title: 'Name', dataIndex: 'name', key: 'name', width: 100, minWidth: 60 },
|
|
11
|
+
{ title: 'Age', dataIndex: 'age', key: 'age', width: 80, sorter: true },
|
|
12
|
+
{ title: 'Act', key: 'act', width: 120, resizable: false },
|
|
13
|
+
];
|
|
14
|
+
|
|
15
|
+
const dataSource = [{ key: '1', name: 'a', age: 1 }];
|
|
16
|
+
|
|
17
|
+
/** jsdom has no layout, so feed the hook the widths it would have measured. */
|
|
18
|
+
const stubSyncRaf = () =>
|
|
19
|
+
vi.spyOn(window, 'requestAnimationFrame').mockImplementation((cb) => {
|
|
20
|
+
cb(0);
|
|
21
|
+
return 1;
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
const stubColWidths = (widths: number[]) => {
|
|
25
|
+
let index = 0;
|
|
26
|
+
const spy = vi
|
|
27
|
+
.spyOn(HTMLTableColElement.prototype, 'getBoundingClientRect')
|
|
28
|
+
.mockImplementation(function () {
|
|
29
|
+
const width = widths[index % widths.length];
|
|
30
|
+
index += 1;
|
|
31
|
+
return { width } as DOMRect;
|
|
32
|
+
});
|
|
33
|
+
return spy;
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
const pointer = (type: string, clientX: number) => {
|
|
37
|
+
const event = new MouseEvent(type, { bubbles: true, clientX, button: 0 });
|
|
38
|
+
Object.defineProperty(event, 'clientX', { value: clientX });
|
|
39
|
+
return event;
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
afterEach(() => {
|
|
43
|
+
cleanup();
|
|
44
|
+
vi.restoreAllMocks();
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
describe('Table column resize', () => {
|
|
48
|
+
it('renders a handle only on columns that opted in', () => {
|
|
49
|
+
const { container } = render(
|
|
50
|
+
<Table resizable columns={columns} dataSource={dataSource} />,
|
|
51
|
+
);
|
|
52
|
+
const handles = container.querySelectorAll('.yee-table-resize-handle');
|
|
53
|
+
expect(handles.length).toBe(2);
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
it('renders no handle when resizing is off', () => {
|
|
57
|
+
const { container } = render(
|
|
58
|
+
<Table columns={columns} dataSource={dataSource} />,
|
|
59
|
+
);
|
|
60
|
+
expect(container.querySelectorAll('.yee-table-resize-handle').length).toBe(
|
|
61
|
+
0,
|
|
62
|
+
);
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
it('never puts a handle on a group header cell', () => {
|
|
66
|
+
const { container } = render(
|
|
67
|
+
<Table
|
|
68
|
+
resizable
|
|
69
|
+
columns={[
|
|
70
|
+
{
|
|
71
|
+
title: 'Group',
|
|
72
|
+
key: 'group',
|
|
73
|
+
children: [
|
|
74
|
+
{ title: 'A', dataIndex: 'name', key: 'name', width: 100 },
|
|
75
|
+
{ title: 'B', dataIndex: 'age', key: 'age', width: 100 },
|
|
76
|
+
],
|
|
77
|
+
},
|
|
78
|
+
]}
|
|
79
|
+
dataSource={dataSource}
|
|
80
|
+
/>,
|
|
81
|
+
);
|
|
82
|
+
const rows = container.querySelectorAll('thead tr');
|
|
83
|
+
expect(rows.length).toBe(2);
|
|
84
|
+
expect(rows[0].querySelectorAll('.yee-table-resize-handle').length).toBe(0);
|
|
85
|
+
expect(rows[1].querySelectorAll('.yee-table-resize-handle').length).toBe(2);
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
it('widens the dragged column and freezes the rest at their measured width', () => {
|
|
89
|
+
stubSyncRaf();
|
|
90
|
+
stubColWidths([100, 80, 120]);
|
|
91
|
+
const onColumnResizeEnd = vi.fn();
|
|
92
|
+
const { container } = render(
|
|
93
|
+
<Table
|
|
94
|
+
resizable
|
|
95
|
+
columns={columns}
|
|
96
|
+
dataSource={dataSource}
|
|
97
|
+
onColumnResizeEnd={onColumnResizeEnd}
|
|
98
|
+
/>,
|
|
99
|
+
);
|
|
100
|
+
|
|
101
|
+
const handle = container.querySelector(
|
|
102
|
+
'.yee-table-resize-handle',
|
|
103
|
+
) as HTMLElement;
|
|
104
|
+
|
|
105
|
+
act(() => {
|
|
106
|
+
handle.dispatchEvent(pointer('pointerdown', 200));
|
|
107
|
+
});
|
|
108
|
+
act(() => {
|
|
109
|
+
window.dispatchEvent(pointer('pointermove', 260));
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
const cols = container.querySelectorAll('colgroup col');
|
|
113
|
+
expect((cols[0] as HTMLElement).style.width).toBe('160px');
|
|
114
|
+
expect((cols[1] as HTMLElement).style.width).toBe('80px');
|
|
115
|
+
expect((cols[2] as HTMLElement).style.width).toBe('120px');
|
|
116
|
+
|
|
117
|
+
const table = container.querySelector('table') as HTMLElement;
|
|
118
|
+
expect(table.style.tableLayout).toBe('fixed');
|
|
119
|
+
expect(table.style.width).toBe('360px');
|
|
120
|
+
|
|
121
|
+
act(() => {
|
|
122
|
+
window.dispatchEvent(pointer('pointerup', 260));
|
|
123
|
+
});
|
|
124
|
+
expect(onColumnResizeEnd).toHaveBeenCalledWith(
|
|
125
|
+
160,
|
|
126
|
+
expect.objectContaining({ key: 'name' }),
|
|
127
|
+
);
|
|
128
|
+
expect(document.body.style.cursor).toBe('');
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
it('shifts the sticky offset of following fixed columns', () => {
|
|
132
|
+
stubSyncRaf();
|
|
133
|
+
stubColWidths([100, 80, 120]);
|
|
134
|
+
const { container } = render(
|
|
135
|
+
<Table
|
|
136
|
+
resizable
|
|
137
|
+
columns={[
|
|
138
|
+
{
|
|
139
|
+
title: 'Name',
|
|
140
|
+
dataIndex: 'name',
|
|
141
|
+
key: 'name',
|
|
142
|
+
width: 100,
|
|
143
|
+
fixed: 'left',
|
|
144
|
+
},
|
|
145
|
+
{
|
|
146
|
+
title: 'Age',
|
|
147
|
+
dataIndex: 'age',
|
|
148
|
+
key: 'age',
|
|
149
|
+
width: 80,
|
|
150
|
+
fixed: 'left',
|
|
151
|
+
},
|
|
152
|
+
{ title: 'Act', key: 'act', width: 120 },
|
|
153
|
+
]}
|
|
154
|
+
dataSource={dataSource}
|
|
155
|
+
/>,
|
|
156
|
+
);
|
|
157
|
+
|
|
158
|
+
const secondFixedTh = container.querySelectorAll(
|
|
159
|
+
'thead th',
|
|
160
|
+
)[1] as HTMLElement;
|
|
161
|
+
expect(secondFixedTh.style.left).toBe('100px');
|
|
162
|
+
|
|
163
|
+
const handle = container.querySelector(
|
|
164
|
+
'.yee-table-resize-handle',
|
|
165
|
+
) as HTMLElement;
|
|
166
|
+
act(() => {
|
|
167
|
+
handle.dispatchEvent(pointer('pointerdown', 200));
|
|
168
|
+
});
|
|
169
|
+
act(() => {
|
|
170
|
+
window.dispatchEvent(pointer('pointermove', 250));
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
expect(
|
|
174
|
+
(container.querySelectorAll('thead th')[1] as HTMLElement).style.left,
|
|
175
|
+
).toBe('150px');
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
it('drops the snapshot when the column set changes', () => {
|
|
179
|
+
stubSyncRaf();
|
|
180
|
+
stubColWidths([100, 80, 120]);
|
|
181
|
+
const { container, rerender } = render(
|
|
182
|
+
<Table resizable columns={columns} dataSource={dataSource} />,
|
|
183
|
+
);
|
|
184
|
+
const handle = container.querySelector(
|
|
185
|
+
'.yee-table-resize-handle',
|
|
186
|
+
) as HTMLElement;
|
|
187
|
+
act(() => {
|
|
188
|
+
handle.dispatchEvent(pointer('pointerdown', 200));
|
|
189
|
+
});
|
|
190
|
+
act(() => {
|
|
191
|
+
window.dispatchEvent(pointer('pointermove', 260));
|
|
192
|
+
});
|
|
193
|
+
act(() => {
|
|
194
|
+
window.dispatchEvent(pointer('pointerup', 260));
|
|
195
|
+
});
|
|
196
|
+
expect((container.querySelector('table') as HTMLElement).style.width).toBe(
|
|
197
|
+
'360px',
|
|
198
|
+
);
|
|
199
|
+
|
|
200
|
+
// A brand new column has no snapshotted width, so the px controlled layout
|
|
201
|
+
// would have a hole in it; the table must fall back to natural sizing.
|
|
202
|
+
rerender(
|
|
203
|
+
<Table
|
|
204
|
+
resizable
|
|
205
|
+
columns={[...columns, { title: 'New', dataIndex: 'x', key: 'x' }]}
|
|
206
|
+
dataSource={dataSource}
|
|
207
|
+
/>,
|
|
208
|
+
);
|
|
209
|
+
const table = container.querySelector('table') as HTMLElement;
|
|
210
|
+
expect(table.style.width).toBe('');
|
|
211
|
+
expect(table.style.tableLayout).toBe('');
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
it('clamps to minWidth', () => {
|
|
215
|
+
stubSyncRaf();
|
|
216
|
+
stubColWidths([100, 80, 120]);
|
|
217
|
+
const { container } = render(
|
|
218
|
+
<Table resizable columns={columns} dataSource={dataSource} />,
|
|
219
|
+
);
|
|
220
|
+
const handle = container.querySelector(
|
|
221
|
+
'.yee-table-resize-handle',
|
|
222
|
+
) as HTMLElement;
|
|
223
|
+
|
|
224
|
+
act(() => {
|
|
225
|
+
handle.dispatchEvent(pointer('pointerdown', 200));
|
|
226
|
+
});
|
|
227
|
+
act(() => {
|
|
228
|
+
window.dispatchEvent(pointer('pointermove', 0));
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
const cols = container.querySelectorAll('colgroup col');
|
|
232
|
+
expect((cols[0] as HTMLElement).style.width).toBe('60px');
|
|
233
|
+
});
|
|
234
|
+
});
|
package/src/Table/table.tsx
CHANGED
|
@@ -4,6 +4,7 @@ import Spin from '../Spin';
|
|
|
4
4
|
import ColGroup from './col-group';
|
|
5
5
|
import Footer from './footer';
|
|
6
6
|
import Header from './header';
|
|
7
|
+
import useColumnResize from './hooks/useColumnResize';
|
|
7
8
|
import useColumns from './hooks/useColumns';
|
|
8
9
|
import useExpand from './hooks/useExpand';
|
|
9
10
|
import useSelection from './hooks/useSelection';
|
|
@@ -12,7 +13,6 @@ import TFooter from './tfooter';
|
|
|
12
13
|
import THeader from './theader';
|
|
13
14
|
|
|
14
15
|
import { GlobalContext } from '../Config-Provider';
|
|
15
|
-
import useLatest from '../hooks/useLatest';
|
|
16
16
|
import mergeContextToProps from '../utils/mergeContextToProps';
|
|
17
17
|
import omit from '../utils/omit';
|
|
18
18
|
import { pickDataAttrs } from '../utils/types';
|
|
@@ -20,7 +20,11 @@ import { pickDataAttrs } from '../utils/types';
|
|
|
20
20
|
import useFilter from './hooks/useFilter';
|
|
21
21
|
import usePagination from './hooks/usePagination';
|
|
22
22
|
import useSorter from './hooks/useSorter';
|
|
23
|
-
import type {
|
|
23
|
+
import type {
|
|
24
|
+
PaginationType,
|
|
25
|
+
TableProps,
|
|
26
|
+
WrapedColumnProps,
|
|
27
|
+
} from './interface';
|
|
24
28
|
import './style/index.less';
|
|
25
29
|
|
|
26
30
|
export const TableCtx = React.createContext({} as any);
|
|
@@ -38,6 +42,9 @@ const Table = React.forwardRef<HTMLDivElement, TableProps>((baseprops, ref) => {
|
|
|
38
42
|
rowKey = 'key',
|
|
39
43
|
bordered,
|
|
40
44
|
tableLayout,
|
|
45
|
+
resizable,
|
|
46
|
+
onColumnResize,
|
|
47
|
+
onColumnResizeEnd,
|
|
41
48
|
loading = false,
|
|
42
49
|
showHeader = true,
|
|
43
50
|
pagination: propPagination = true,
|
|
@@ -64,6 +71,32 @@ const Table = React.forwardRef<HTMLDivElement, TableProps>((baseprops, ref) => {
|
|
|
64
71
|
const dataAttrs = pickDataAttrs(rest as Record<string, unknown>);
|
|
65
72
|
const [measuredColumnWidths, setMeasuredColumnWidths] =
|
|
66
73
|
React.useState<number[]>();
|
|
74
|
+
const contentRef = useRef<HTMLDivElement>(null);
|
|
75
|
+
const tableRef = useRef<HTMLTableElement>(null);
|
|
76
|
+
|
|
77
|
+
// Filled in right after `useColumns` below. The resize hook needs the latest
|
|
78
|
+
// leaf columns, but `wrapedColumns` is itself derived from the resized widths,
|
|
79
|
+
// so depending on it directly would rebuild the drag handlers mid-drag.
|
|
80
|
+
const wrapedColumnsRef = useRef<WrapedColumnProps[]>([]);
|
|
81
|
+
|
|
82
|
+
const {
|
|
83
|
+
resizedWidths,
|
|
84
|
+
resizingKey,
|
|
85
|
+
onResizeStart,
|
|
86
|
+
onResizeStep,
|
|
87
|
+
resetResizedWidths,
|
|
88
|
+
} = useColumnResize({
|
|
89
|
+
tableRef,
|
|
90
|
+
columnsRef: wrapedColumnsRef,
|
|
91
|
+
onColumnResize,
|
|
92
|
+
onColumnResizeEnd,
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
// Once a snapshot exists, every column carries an explicit px width and the
|
|
96
|
+
// table switches to `table-layout: fixed`, which makes those declared widths
|
|
97
|
+
// authoritative. The measured widths lag one frame behind a drag, so they must
|
|
98
|
+
// stop feeding the fixed-column offsets while a snapshot is in play.
|
|
99
|
+
const hasResizeSnapshot = Object.keys(resizedWidths).length > 0;
|
|
67
100
|
|
|
68
101
|
const getRowKey = React.useCallback(
|
|
69
102
|
(record: Record<string, any>, key = rowKey) => {
|
|
@@ -83,9 +116,42 @@ const Table = React.forwardRef<HTMLDivElement, TableProps>((baseprops, ref) => {
|
|
|
83
116
|
columns: propColumns,
|
|
84
117
|
expandable,
|
|
85
118
|
rowSelection,
|
|
86
|
-
measuredColumnWidths,
|
|
119
|
+
measuredColumnWidths: hasResizeSnapshot ? undefined : measuredColumnWidths,
|
|
120
|
+
resizable,
|
|
121
|
+
resizedWidths,
|
|
87
122
|
});
|
|
88
123
|
|
|
124
|
+
wrapedColumnsRef.current = wrapedColumns;
|
|
125
|
+
|
|
126
|
+
// Total width of the px controlled layout. Deliberately `undefined` unless the
|
|
127
|
+
// snapshot covers every single column: a partial snapshot would leave a hole in
|
|
128
|
+
// the layout, so it is safer to fall back to the browser's own distribution.
|
|
129
|
+
const resizeTotalWidth = useMemo(() => {
|
|
130
|
+
if (!wrapedColumns.length) return undefined;
|
|
131
|
+
let total = 0;
|
|
132
|
+
for (const column of wrapedColumns) {
|
|
133
|
+
const width = column.resizeKey
|
|
134
|
+
? resizedWidths[column.resizeKey]
|
|
135
|
+
: undefined;
|
|
136
|
+
if (!width || !Number.isFinite(width)) return undefined;
|
|
137
|
+
total += width;
|
|
138
|
+
}
|
|
139
|
+
return total;
|
|
140
|
+
}, [wrapedColumns, resizedWidths]);
|
|
141
|
+
|
|
142
|
+
const resizeActive = resizeTotalWidth !== undefined;
|
|
143
|
+
|
|
144
|
+
// Identity of the column set, ignoring widths. When it changes the snapshot is
|
|
145
|
+
// stale (a new column would have no width of its own), so it gets dropped.
|
|
146
|
+
const resizeKeysSignature = useMemo(
|
|
147
|
+
() => wrapedColumns.map((column) => column.resizeKey).join('|'),
|
|
148
|
+
[wrapedColumns],
|
|
149
|
+
);
|
|
150
|
+
|
|
151
|
+
useEffect(() => {
|
|
152
|
+
resetResizedWidths();
|
|
153
|
+
}, [resizeKeysSignature, resetResizedWidths]);
|
|
154
|
+
|
|
89
155
|
// Filter
|
|
90
156
|
const {
|
|
91
157
|
data: filteredData,
|
|
@@ -157,8 +223,6 @@ const Table = React.forwardRef<HTMLDivElement, TableProps>((baseprops, ref) => {
|
|
|
157
223
|
|
|
158
224
|
// Track filter changes to emit onChange after filtered data is recalculated
|
|
159
225
|
const prevFilterRecordsRef = useRef(filterRecords);
|
|
160
|
-
const contentRef = useRef<HTMLDivElement>(null);
|
|
161
|
-
const tableRef = useRef<HTMLTableElement>(null);
|
|
162
226
|
const [horizontalScroll, setHorizontalScroll] = React.useState({
|
|
163
227
|
left: false,
|
|
164
228
|
right: false,
|
|
@@ -197,12 +261,10 @@ const Table = React.forwardRef<HTMLDivElement, TableProps>((baseprops, ref) => {
|
|
|
197
261
|
[wrapedColumns],
|
|
198
262
|
);
|
|
199
263
|
|
|
200
|
-
//
|
|
201
|
-
// `wrapedColumns` is derived from `measuredColumnWidths`, so depending
|
|
202
|
-
// directly would recreate the callback (and the ResizeObserver) on every
|
|
264
|
+
// `wrapedColumnsRef` (assigned during render, above) keeps the measure callback
|
|
265
|
+
// stable: `wrapedColumns` is derived from `measuredColumnWidths`, so depending
|
|
266
|
+
// on it directly would recreate the callback (and the ResizeObserver) on every
|
|
203
267
|
// measurement.
|
|
204
|
-
const wrapedColumnsRef = useLatest(wrapedColumns);
|
|
205
|
-
|
|
206
268
|
const updateMeasuredColumnWidths = React.useCallback(() => {
|
|
207
269
|
const columns = wrapedColumnsRef.current;
|
|
208
270
|
if (!columns.some((column) => column.fixed)) return;
|
|
@@ -319,6 +381,9 @@ const Table = React.forwardRef<HTMLDivElement, TableProps>((baseprops, ref) => {
|
|
|
319
381
|
onSort={handleSort}
|
|
320
382
|
onCheckAll={onCheckAll}
|
|
321
383
|
onInternalFilter={onFilterInternal}
|
|
384
|
+
resizingKey={resizingKey}
|
|
385
|
+
onResizeStart={onResizeStart}
|
|
386
|
+
onResizeStep={onResizeStep}
|
|
322
387
|
/>
|
|
323
388
|
);
|
|
324
389
|
};
|
|
@@ -349,8 +414,16 @@ const Table = React.forwardRef<HTMLDivElement, TableProps>((baseprops, ref) => {
|
|
|
349
414
|
const tableProps = {
|
|
350
415
|
style: {
|
|
351
416
|
...styles?.table,
|
|
352
|
-
|
|
353
|
-
|
|
417
|
+
// An explicit total width replaces both `width: 100%` and `scroll.x`:
|
|
418
|
+
// either of those would let the browser stretch the table and redistribute
|
|
419
|
+
// the extra space across the columns, undoing the drag.
|
|
420
|
+
minWidth: resizeActive ? undefined : scroll?.x,
|
|
421
|
+
width: resizeTotalWidth,
|
|
422
|
+
tableLayout: resizeActive
|
|
423
|
+
? 'fixed'
|
|
424
|
+
: pageData?.length
|
|
425
|
+
? tableLayout
|
|
426
|
+
: undefined,
|
|
354
427
|
},
|
|
355
428
|
className: clsx(
|
|
356
429
|
{ [`${prefixCls}-bordered`]: bordered },
|
|
@@ -414,7 +487,11 @@ const Table = React.forwardRef<HTMLDivElement, TableProps>((baseprops, ref) => {
|
|
|
414
487
|
|
|
415
488
|
return (
|
|
416
489
|
<div
|
|
417
|
-
className={clsx(
|
|
490
|
+
className={clsx(
|
|
491
|
+
`${prefixCls}-box`,
|
|
492
|
+
{ [`${prefixCls}-resizing`]: !!resizingKey },
|
|
493
|
+
className,
|
|
494
|
+
)}
|
|
418
495
|
style={style}
|
|
419
496
|
ref={ref}
|
|
420
497
|
{...dataAttrs}
|
|
@@ -29,9 +29,14 @@ const HeadCell: React.FC<HeadCellProps> = (props) => {
|
|
|
29
29
|
width,
|
|
30
30
|
colSpan,
|
|
31
31
|
rowSpan,
|
|
32
|
+
resizable,
|
|
33
|
+
resizeKey,
|
|
34
|
+
resizingKey,
|
|
32
35
|
onHeaderCell,
|
|
33
36
|
onSort,
|
|
34
37
|
onInternalFilter,
|
|
38
|
+
onResizeStart,
|
|
39
|
+
onResizeStep,
|
|
35
40
|
...rest
|
|
36
41
|
} = props;
|
|
37
42
|
|
|
@@ -148,6 +153,41 @@ const HeadCell: React.FC<HeadCellProps> = (props) => {
|
|
|
148
153
|
className,
|
|
149
154
|
);
|
|
150
155
|
|
|
156
|
+
// `resizeKey` is only stamped onto leaf columns, so it doubles as the guard that
|
|
157
|
+
// keeps handles off group header cells (whose width is the sum of their leaves).
|
|
158
|
+
const showResizeHandle = !!(resizable && resizeKey && onResizeStart);
|
|
159
|
+
const isResizing = !!resizeKey && resizingKey === resizeKey;
|
|
160
|
+
|
|
161
|
+
const renderResizeHandle = () => {
|
|
162
|
+
if (!showResizeHandle) return null;
|
|
163
|
+
return (
|
|
164
|
+
<span
|
|
165
|
+
role="separator"
|
|
166
|
+
aria-orientation="vertical"
|
|
167
|
+
aria-label={tableLocale.resizeColumn || 'Drag to resize column'}
|
|
168
|
+
tabIndex={0}
|
|
169
|
+
className={clsx(`${prefixCls}-resize-handle`, {
|
|
170
|
+
[`${prefixCls}-resize-handle-active`]: isResizing,
|
|
171
|
+
})}
|
|
172
|
+
onPointerDown={(event) => onResizeStart?.(event, resizeKey as string)}
|
|
173
|
+
// The whole title area is a sort trigger, and a resize drag ends with a
|
|
174
|
+
// click on top of it; swallow it so dragging never toggles the sorter.
|
|
175
|
+
onClick={(event) => {
|
|
176
|
+
event.preventDefault();
|
|
177
|
+
event.stopPropagation();
|
|
178
|
+
}}
|
|
179
|
+
onKeyDown={(event) => {
|
|
180
|
+
if (event.key !== 'ArrowLeft' && event.key !== 'ArrowRight') return;
|
|
181
|
+
event.preventDefault();
|
|
182
|
+
onResizeStep?.(
|
|
183
|
+
resizeKey as string,
|
|
184
|
+
event.key === 'ArrowLeft' ? -1 : 1,
|
|
185
|
+
);
|
|
186
|
+
}}
|
|
187
|
+
/>
|
|
188
|
+
);
|
|
189
|
+
};
|
|
190
|
+
|
|
151
191
|
const getThProps = () => {
|
|
152
192
|
const styles = { ...style, width, maxWidth: width, textAlign: align };
|
|
153
193
|
const cellProps = onHeaderCell ? onHeaderCell(props) : {};
|
|
@@ -165,10 +205,20 @@ const HeadCell: React.FC<HeadCellProps> = (props) => {
|
|
|
165
205
|
|
|
166
206
|
const renderCell = () => {
|
|
167
207
|
if (!filter && !sorter) {
|
|
168
|
-
return
|
|
208
|
+
return (
|
|
209
|
+
<th {...thProps}>
|
|
210
|
+
{title}
|
|
211
|
+
{renderResizeHandle()}
|
|
212
|
+
</th>
|
|
213
|
+
);
|
|
169
214
|
}
|
|
170
215
|
|
|
171
|
-
return
|
|
216
|
+
return (
|
|
217
|
+
<th {...thProps}>
|
|
218
|
+
{renderCellWithAction()}
|
|
219
|
+
{renderResizeHandle()}
|
|
220
|
+
</th>
|
|
221
|
+
);
|
|
172
222
|
};
|
|
173
223
|
|
|
174
224
|
const cell = renderCell();
|
package/src/Table/util/index.ts
CHANGED
|
@@ -1,4 +1,47 @@
|
|
|
1
|
-
import { WrapedColumnProps } from '../interface';
|
|
1
|
+
import { ColumnProps, WrapedColumnProps } from '../interface';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Stable identity for a leaf column, used to key its resized width.
|
|
5
|
+
*
|
|
6
|
+
* `key`/`dataIndex` survive re-ordering and data changes, so they are preferred.
|
|
7
|
+
* Columns declaring neither fall back to their position, which is the best that
|
|
8
|
+
* can be done without asking the caller for an id.
|
|
9
|
+
*/
|
|
10
|
+
export function getColumnResizeKey(column: ColumnProps, index: number) {
|
|
11
|
+
const id = column.key ?? column.dataIndex;
|
|
12
|
+
return id === undefined || id === null || id === ''
|
|
13
|
+
? `index:${index}`
|
|
14
|
+
: `id:${id}`;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Stamp every leaf column with its `resizeKey`, resolve whether it is resizable
|
|
19
|
+
* (column level setting wins over the table level one) and override `width` with
|
|
20
|
+
* the dragged value when there is one.
|
|
21
|
+
*
|
|
22
|
+
* This runs before `handleColumns` so that the colgroup, the header cells, the
|
|
23
|
+
* body cells and the sticky offsets of fixed columns all read the same width
|
|
24
|
+
* from a single place.
|
|
25
|
+
*/
|
|
26
|
+
export function applyColumnResize(
|
|
27
|
+
columns: Array<WrapedColumnProps>,
|
|
28
|
+
resizable?: boolean,
|
|
29
|
+
resizedWidths?: Record<string, number>,
|
|
30
|
+
) {
|
|
31
|
+
return columns.map((column, index) => {
|
|
32
|
+
const resizeKey = getColumnResizeKey(column, index);
|
|
33
|
+
const resizedWidth = resizedWidths?.[resizeKey];
|
|
34
|
+
const next: WrapedColumnProps = {
|
|
35
|
+
...column,
|
|
36
|
+
resizeKey,
|
|
37
|
+
resizable: column.resizable ?? resizable ?? false,
|
|
38
|
+
};
|
|
39
|
+
if (Number.isFinite(resizedWidth) && (resizedWidth as number) > 0) {
|
|
40
|
+
next.width = resizedWidth;
|
|
41
|
+
}
|
|
42
|
+
return next;
|
|
43
|
+
});
|
|
44
|
+
}
|
|
2
45
|
|
|
3
46
|
const getColumnWidth = (
|
|
4
47
|
column: WrapedColumnProps,
|
package/src/Tabs/interface.ts
CHANGED
|
@@ -169,7 +169,7 @@
|
|
|
169
169
|
user-select: none;
|
|
170
170
|
|
|
171
171
|
&.disabled {
|
|
172
|
-
color: var(--yee-
|
|
172
|
+
color: var(--yee-tabs-tab-title-disabled-color);
|
|
173
173
|
cursor: not-allowed;
|
|
174
174
|
|
|
175
175
|
& > * {
|
|
@@ -178,7 +178,7 @@
|
|
|
178
178
|
}
|
|
179
179
|
|
|
180
180
|
&.active {
|
|
181
|
-
color: var(--yee-
|
|
181
|
+
color: var(--yee-tabs-tab-title-active-hover-color);
|
|
182
182
|
}
|
|
183
183
|
|
|
184
184
|
&:not(.disabled):not(.active):hover {
|
|
@@ -253,8 +253,8 @@
|
|
|
253
253
|
|
|
254
254
|
@{pls}-tab.active {
|
|
255
255
|
z-index: 2;
|
|
256
|
-
color: var(--yee-
|
|
257
|
-
border-color: var(--yee-
|
|
256
|
+
color: var(--yee-tabs-tab-active-hover-pills-color);
|
|
257
|
+
border-color: var(--yee-tabs-tab-active-pills-border-color);
|
|
258
258
|
}
|
|
259
259
|
|
|
260
260
|
@{pls}-active {
|
|
@@ -461,16 +461,16 @@
|
|
|
461
461
|
cursor: pointer;
|
|
462
462
|
|
|
463
463
|
&:hover {
|
|
464
|
-
background-color: var(--yee-
|
|
464
|
+
background-color: var(--yee-tabs-tab-dropdown-title-hover-bg-color);
|
|
465
465
|
|
|
466
466
|
@{pls}-tab-title {
|
|
467
|
-
color: var(--yee-
|
|
467
|
+
color: var(--yee-tabs-tab-dropdown-title-hover-color);
|
|
468
468
|
}
|
|
469
469
|
}
|
|
470
470
|
|
|
471
471
|
&:active {
|
|
472
|
-
color: var(--yee-
|
|
473
|
-
background-color: var(--yee-
|
|
472
|
+
color: var(--yee-tabs-tab-dropdown-title-active-color);
|
|
473
|
+
background-color: var(--yee-tabs-tab-dropdown-title-active-bg-color);
|
|
474
474
|
}
|
|
475
475
|
}
|
|
476
476
|
}
|
|
@@ -491,7 +491,7 @@
|
|
|
491
491
|
user-select: none;
|
|
492
492
|
|
|
493
493
|
&:hover {
|
|
494
|
-
color: var(--yee-
|
|
494
|
+
color: var(--yee-tabs-tab-title-active-hover-color);
|
|
495
495
|
}
|
|
496
496
|
}
|
|
497
497
|
}
|
|
@@ -153,6 +153,9 @@ const TabHeader: React.FC<TabHeaderProps> = (props) => {
|
|
|
153
153
|
const cls = clsx(
|
|
154
154
|
`${prefixCls}-nav-wrapper`,
|
|
155
155
|
`${prefixCls}-nav-wrapper-${position}`,
|
|
156
|
+
{
|
|
157
|
+
[`${prefixCls}-nav-wrapper-with-more`]: moreOpen,
|
|
158
|
+
},
|
|
156
159
|
classNames?.header,
|
|
157
160
|
);
|
|
158
161
|
|
|
@@ -221,7 +224,8 @@ const TabHeader: React.FC<TabHeaderProps> = (props) => {
|
|
|
221
224
|
{moreOpen && (
|
|
222
225
|
<DropDownTabs visible={moreOpen} items={items || []} range={range}>
|
|
223
226
|
<Button
|
|
224
|
-
className={`${prefixCls}-nav-more
|
|
227
|
+
className={clsx(`${prefixCls}-nav-more`, classNames?.more)}
|
|
228
|
+
style={styles?.more}
|
|
225
229
|
size="small"
|
|
226
230
|
icon={<Ellipsis size={14} strokeWidth={1.5} />}
|
|
227
231
|
type="text"
|
|
@@ -17,15 +17,15 @@
|
|
|
17
17
|
}
|
|
18
18
|
|
|
19
19
|
&-drag-over-before {
|
|
20
|
-
box-shadow: inset 0 2px 0 0 var(--yee-primary
|
|
20
|
+
box-shadow: inset 0 2px 0 0 var(--yee-color-primary);
|
|
21
21
|
}
|
|
22
22
|
|
|
23
23
|
&-drag-over-after {
|
|
24
|
-
box-shadow: inset 0 -2px 0 0 var(--yee-primary
|
|
24
|
+
box-shadow: inset 0 -2px 0 0 var(--yee-color-primary);
|
|
25
25
|
}
|
|
26
26
|
|
|
27
27
|
&-drag-over-inside {
|
|
28
|
-
background: color-mix(in srgb, var(--yee-primary
|
|
28
|
+
background: color-mix(in srgb, var(--yee-color-primary) 12%, transparent);
|
|
29
29
|
}
|
|
30
30
|
|
|
31
31
|
&-switcher {
|