Skip to content

Commit dc8f283

Browse files
committed
feat: add left frozen and right frozen columns region scroll component
1 parent 2f6e00b commit dc8f283

12 files changed

Lines changed: 541 additions & 36 deletions

File tree

packages/vtable/examples/frozen/list-table-scrollable-frozen-cols.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,16 @@ export function createTable() {
4343
records,
4444
columns,
4545
frozenColCount: 6,
46+
rightFrozenColCount: 4,
4647
maxFrozenWidth: 320,
4748
scrollFrozenCols: true,
49+
maxRightFrozenWidth: 320,
50+
scrollRightFrozenCols: true,
51+
theme: VTable.themes.DEFAULT.extends({
52+
scrollStyle: {
53+
visible: 'none'
54+
}
55+
}),
4856
excelOptions: {
4957
fillHandle: args => {
5058
const { selectRanges, table } = args;

packages/vtable/src/core/BaseTable.ts

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1088,6 +1088,10 @@ export abstract class BaseTable extends EventTarget implements BaseTableAPI {
10881088
const maxFrozenWidth = this.options.maxFrozenWidth ?? '80%';
10891089
return _toPxWidth(this, maxFrozenWidth);
10901090
}
1091+
_getMaxRightFrozenWidth(): number {
1092+
const maxRightFrozenWidth = this.options.maxRightFrozenWidth ?? this.options.maxFrozenWidth ?? '80%';
1093+
return _toPxWidth(this, maxRightFrozenWidth);
1094+
}
10911095
_getComputedFrozenColCount(frozenColCount: number): number {
10921096
const maxFrozenWidth = this._getMaxFrozenWidth();
10931097
let computedfrozenColCount = frozenColCount;
@@ -3047,8 +3051,15 @@ export abstract class BaseTable extends EventTarget implements BaseTableAPI {
30473051
* @returns
30483052
*/
30493053
getRightFrozenColsWidth(): number {
3054+
const contentWidth = this.getRightFrozenColsContentWidth();
3055+
if (!this.options.scrollRightFrozenCols) {
3056+
return contentWidth;
3057+
}
3058+
const maxRightFrozenWidth = this._getMaxRightFrozenWidth();
3059+
return Math.min(contentWidth, maxRightFrozenWidth);
3060+
}
3061+
getRightFrozenColsContentWidth(): number {
30503062
if (this.rightFrozenColCount > 0) {
3051-
// const width = this.getColsWidth(this.colCount - this.rightFrozenColCount, this.colCount - 1); // 同getBottomFrozenRowsHeight的原因
30523063
let width = 0;
30533064
for (let col = this.colCount - this.rightFrozenColCount; col <= this.colCount - 1; col++) {
30543065
width += this.getColWidth(col);
@@ -3057,6 +3068,14 @@ export abstract class BaseTable extends EventTarget implements BaseTableAPI {
30573068
}
30583069
return 0;
30593070
}
3071+
getRightFrozenColsOffset(): number {
3072+
const contentWidth = this.getRightFrozenColsContentWidth();
3073+
const viewportWidth = this.getRightFrozenColsWidth();
3074+
return Math.max(0, contentWidth - viewportWidth);
3075+
}
3076+
getRightFrozenColsScrollLeft(): number {
3077+
return this.stateManager.scroll.rightFrozenHorizontalBarPos ?? 0;
3078+
}
30603079
/**
30613080
* 获取实际绘制范围的宽高,而非可绘制画布大小
30623081
* @param table

packages/vtable/src/core/utils/get-cell-position.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -183,12 +183,14 @@ export function getTargetColAtConsiderRightFrozen(
183183
return { left: 0, col: 0, right: 0, width: 0 };
184184
}
185185
absoluteX = absoluteX - _this.tableX;
186+
const rightFrozenScrollLeft = _this.getRightFrozenColsScrollLeft?.() ?? 0;
186187
if (
187188
isConsider &&
188189
absoluteX > _this.tableNoFrameWidth - _this.getRightFrozenColsWidth() &&
189190
absoluteX < _this.tableNoFrameWidth &&
190191
absoluteX <= _this.getAllColsWidth()
191192
) {
193+
absoluteX -= rightFrozenScrollLeft;
192194
for (let i = 0; i < _this.rightFrozenColCount; i++) {
193195
if (absoluteX > _this.tableNoFrameWidth - _this.getColsWidth(_this.colCount - i - 1, _this.colCount - 1)) {
194196
return {

packages/vtable/src/event/listener/scroll-bar.ts

Lines changed: 159 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,21 +11,62 @@ export function bindScrollBarListener(eventManager: EventManager) {
1111
const table = eventManager.table;
1212
const stateManager = table.stateManager;
1313
const scenegraph = table.scenegraph;
14+
const visible1 = table.theme.scrollStyle?.visible as string;
15+
const horizontalVisible = table.theme.scrollStyle?.horizontalVisible ?? visible1;
16+
const verticalVisible = table.theme.scrollStyle?.verticalVisible ?? visible1;
1417

1518
// 监听滚动条组件pointover事件
1619
scenegraph.component.vScrollBar.addEventListener('pointerover', (e: any) => {
17-
stateManager.showVerticalScrollBar();
20+
if (verticalVisible === 'focus') {
21+
stateManager.showVerticalScrollBar();
22+
}
1823
});
1924
scenegraph.component.hScrollBar.addEventListener('pointerover', (e: any) => {
20-
stateManager.showHorizontalScrollBar();
25+
if (horizontalVisible === 'focus') {
26+
stateManager.showHorizontalScrollBar(false, 'body');
27+
}
28+
});
29+
scenegraph.component.frozenHScrollBar.addEventListener('pointerover', (e: any) => {
30+
if (horizontalVisible === 'focus') {
31+
stateManager.showHorizontalScrollBar(false, 'frozen');
32+
}
33+
});
34+
scenegraph.component.rightFrozenHScrollBar.addEventListener('pointerover', (e: any) => {
35+
if (horizontalVisible === 'focus') {
36+
stateManager.showHorizontalScrollBar(false, 'rightFrozen');
37+
}
2138
});
2239
scenegraph.component.vScrollBar.addEventListener('pointerout', (e: any) => {
40+
if (verticalVisible !== 'focus') {
41+
return;
42+
}
2343
if (stateManager.interactionState === InteractionState.scrolling) {
2444
return;
2545
}
2646
stateManager.hideVerticalScrollBar();
2747
});
2848
scenegraph.component.hScrollBar.addEventListener('pointerout', (e: any) => {
49+
if (horizontalVisible !== 'focus') {
50+
return;
51+
}
52+
if (stateManager.interactionState === InteractionState.scrolling) {
53+
return;
54+
}
55+
stateManager.hideHorizontalScrollBar();
56+
});
57+
scenegraph.component.frozenHScrollBar.addEventListener('pointerout', (e: any) => {
58+
if (horizontalVisible !== 'focus') {
59+
return;
60+
}
61+
if (stateManager.interactionState === InteractionState.scrolling) {
62+
return;
63+
}
64+
stateManager.hideHorizontalScrollBar();
65+
});
66+
scenegraph.component.rightFrozenHScrollBar.addEventListener('pointerout', (e: any) => {
67+
if (horizontalVisible !== 'focus') {
68+
return;
69+
}
2970
if (stateManager.interactionState === InteractionState.scrolling) {
3071
return;
3172
}
@@ -78,6 +119,14 @@ export function bindScrollBarListener(eventManager: EventManager) {
78119
scenegraph.table.stateManager.updateCursor();
79120
e.stopPropagation(); //防止冒泡到stage上 检测到挨着列间隔线判断成可拖拽
80121
});
122+
scenegraph.component.frozenHScrollBar.addEventListener('pointermove', (e: FederatedPointerEvent) => {
123+
scenegraph.table.stateManager.updateCursor();
124+
e.stopPropagation();
125+
});
126+
scenegraph.component.rightFrozenHScrollBar.addEventListener('pointermove', (e: FederatedPointerEvent) => {
127+
scenegraph.table.stateManager.updateCursor();
128+
e.stopPropagation();
129+
});
81130
scenegraph.component.hScrollBar.addEventListener('pointerdown', (e: FederatedPointerEvent) => {
82131
e.stopPropagation(); //防止冒泡到stage上 检测到挨着列间隔线判断成拖拽状态
83132
if ((scenegraph.table as any).hasListeners(TABLE_EVENT_TYPE.MOUSEDOWN_TABLE)) {
@@ -86,6 +135,22 @@ export function bindScrollBarListener(eventManager: EventManager) {
86135
});
87136
}
88137
});
138+
scenegraph.component.frozenHScrollBar.addEventListener('pointerdown', (e: FederatedPointerEvent) => {
139+
e.stopPropagation();
140+
if ((scenegraph.table as any).hasListeners(TABLE_EVENT_TYPE.MOUSEDOWN_TABLE)) {
141+
scenegraph.table.fireListeners(TABLE_EVENT_TYPE.MOUSEDOWN_TABLE, {
142+
event: e.nativeEvent
143+
});
144+
}
145+
});
146+
scenegraph.component.rightFrozenHScrollBar.addEventListener('pointerdown', (e: FederatedPointerEvent) => {
147+
e.stopPropagation();
148+
if ((scenegraph.table as any).hasListeners(TABLE_EVENT_TYPE.MOUSEDOWN_TABLE)) {
149+
scenegraph.table.fireListeners(TABLE_EVENT_TYPE.MOUSEDOWN_TABLE, {
150+
event: e.nativeEvent
151+
});
152+
}
153+
});
89154
scenegraph.component.hScrollBar.addEventListener('scrollDown', (e: FederatedPointerEvent) => {
90155
scenegraph.table.eventManager.LastBodyPointerXY = { x: e.x, y: e.y };
91156
scenegraph.table.eventManager.isDown = true;
@@ -100,6 +165,34 @@ export function bindScrollBarListener(eventManager: EventManager) {
100165
});
101166
}
102167
});
168+
scenegraph.component.frozenHScrollBar.addEventListener('scrollDown', (e: FederatedPointerEvent) => {
169+
scenegraph.table.eventManager.LastBodyPointerXY = { x: e.x, y: e.y };
170+
scenegraph.table.eventManager.isDown = true;
171+
if (stateManager.interactionState !== InteractionState.scrolling) {
172+
stateManager.updateInteractionState(InteractionState.scrolling);
173+
}
174+
scenegraph.table.stateManager.hideMenu();
175+
(scenegraph.table as ListTableAPI).editorManager?.completeEdit();
176+
if ((scenegraph.table as any).hasListeners(TABLE_EVENT_TYPE.MOUSEDOWN_TABLE)) {
177+
scenegraph.table.fireListeners(TABLE_EVENT_TYPE.MOUSEDOWN_TABLE, {
178+
event: e.nativeEvent
179+
});
180+
}
181+
});
182+
scenegraph.component.rightFrozenHScrollBar.addEventListener('scrollDown', (e: FederatedPointerEvent) => {
183+
scenegraph.table.eventManager.LastBodyPointerXY = { x: e.x, y: e.y };
184+
scenegraph.table.eventManager.isDown = true;
185+
if (stateManager.interactionState !== InteractionState.scrolling) {
186+
stateManager.updateInteractionState(InteractionState.scrolling);
187+
}
188+
scenegraph.table.stateManager.hideMenu();
189+
(scenegraph.table as ListTableAPI).editorManager?.completeEdit();
190+
if ((scenegraph.table as any).hasListeners(TABLE_EVENT_TYPE.MOUSEDOWN_TABLE)) {
191+
scenegraph.table.fireListeners(TABLE_EVENT_TYPE.MOUSEDOWN_TABLE, {
192+
event: e.nativeEvent
193+
});
194+
}
195+
});
103196
scenegraph.component.hScrollBar.addEventListener('pointerup', () => {
104197
stateManager.fastScrolling = false;
105198
scenegraph.table.eventManager.isDraging = false;
@@ -116,8 +209,42 @@ export function bindScrollBarListener(eventManager: EventManager) {
116209
scenegraph.component.hScrollBar.addEventListener('scrollUp', (e: FederatedPointerEvent) => {
117210
scenegraph.table.eventManager.isDraging = false;
118211
});
212+
scenegraph.component.frozenHScrollBar.addEventListener('pointerup', () => {
213+
stateManager.fastScrolling = false;
214+
scenegraph.table.eventManager.isDraging = false;
215+
if (stateManager.interactionState === InteractionState.scrolling) {
216+
stateManager.updateInteractionState(InteractionState.default);
217+
}
218+
});
219+
scenegraph.component.frozenHScrollBar.addEventListener('pointerupoutside', () => {
220+
stateManager.fastScrolling = false;
221+
if (stateManager.interactionState === InteractionState.scrolling) {
222+
stateManager.updateInteractionState(InteractionState.default);
223+
}
224+
});
225+
scenegraph.component.frozenHScrollBar.addEventListener('scrollUp', (e: FederatedPointerEvent) => {
226+
scenegraph.table.eventManager.isDraging = false;
227+
});
228+
scenegraph.component.rightFrozenHScrollBar.addEventListener('pointerup', () => {
229+
stateManager.fastScrolling = false;
230+
scenegraph.table.eventManager.isDraging = false;
231+
if (stateManager.interactionState === InteractionState.scrolling) {
232+
stateManager.updateInteractionState(InteractionState.default);
233+
}
234+
});
235+
scenegraph.component.rightFrozenHScrollBar.addEventListener('pointerupoutside', () => {
236+
stateManager.fastScrolling = false;
237+
if (stateManager.interactionState === InteractionState.scrolling) {
238+
stateManager.updateInteractionState(InteractionState.default);
239+
}
240+
});
241+
scenegraph.component.rightFrozenHScrollBar.addEventListener('scrollUp', (e: FederatedPointerEvent) => {
242+
scenegraph.table.eventManager.isDraging = false;
243+
});
119244
const throttleVerticalWheel = throttle(stateManager.updateVerticalScrollBar, 20);
120245
const throttleHorizontalWheel = throttle(stateManager.updateHorizontalScrollBar, 20);
246+
const throttleFrozenHorizontalWheel = throttle(stateManager.updateFrozenHorizontalScrollBar, 20);
247+
const throttleRightFrozenHorizontalWheel = throttle(stateManager.updateRightFrozenHorizontalScrollBar, 20);
121248

122249
// 监听滚动条组件scroll事件
123250
scenegraph.component.vScrollBar.addEventListener('scrollDrag', (e: any) => {
@@ -158,4 +285,34 @@ export function bindScrollBarListener(eventManager: EventManager) {
158285
// stateManager.table.scenegraph.proxy.isSkipProgress = false;
159286
// }, 10);
160287
});
288+
289+
scenegraph.component.frozenHScrollBar.addEventListener('scrollDrag', (e: any) => {
290+
if (scenegraph.table.eventManager.isDown) {
291+
scenegraph.table.eventManager.isDraging = true;
292+
}
293+
stateManager.fastScrolling = true;
294+
if (stateManager.interactionState !== InteractionState.scrolling) {
295+
stateManager.updateInteractionState(InteractionState.scrolling);
296+
}
297+
scenegraph.table.stateManager.hideMenu();
298+
(scenegraph.table as ListTableAPI).editorManager?.completeEdit();
299+
table.scenegraph.deactivateChart(-1, -1, true);
300+
const ratio = e.detail.value[0] / (1 - e.detail.value[1] + e.detail.value[0]);
301+
throttleFrozenHorizontalWheel(ratio);
302+
});
303+
304+
scenegraph.component.rightFrozenHScrollBar.addEventListener('scrollDrag', (e: any) => {
305+
if (scenegraph.table.eventManager.isDown) {
306+
scenegraph.table.eventManager.isDraging = true;
307+
}
308+
stateManager.fastScrolling = true;
309+
if (stateManager.interactionState !== InteractionState.scrolling) {
310+
stateManager.updateInteractionState(InteractionState.scrolling);
311+
}
312+
scenegraph.table.stateManager.hideMenu();
313+
(scenegraph.table as ListTableAPI).editorManager?.completeEdit();
314+
table.scenegraph.deactivateChart(-1, -1, true);
315+
const ratio = e.detail.value[0] / (1 - e.detail.value[1] + e.detail.value[0]);
316+
throttleRightFrozenHorizontalWheel(ratio);
317+
});
161318
}

packages/vtable/src/event/listener/table-group.ts

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,25 @@ export function bindTableGroupListener(eventManager: EventManager) {
179179
mergeCellInfo: eventArgsSet.eventArgs?.mergeInfo
180180
});
181181
}
182+
183+
if (
184+
(table.theme.scrollStyle.horizontalVisible && table.theme.scrollStyle.horizontalVisible === 'focus') ||
185+
(!table.theme.scrollStyle.horizontalVisible && table.theme.scrollStyle.visible === 'focus')
186+
) {
187+
const relativeX = e.x - table.tableX;
188+
const target =
189+
table.options.scrollFrozenCols &&
190+
table.getFrozenColsOffset?.() > 0 &&
191+
relativeX >= 0 &&
192+
relativeX < table.getFrozenColsWidth()
193+
? 'frozen'
194+
: table.options.scrollRightFrozenCols &&
195+
table.getRightFrozenColsOffset?.() > 0 &&
196+
relativeX > table.tableNoFrameWidth - table.getRightFrozenColsWidth()
197+
? 'rightFrozen'
198+
: 'body';
199+
stateManager.showHorizontalScrollBar(false, target);
200+
}
182201
});
183202

184203
table.scenegraph.tableGroup.addEventListener('pointerout', (e: FederatedPointerEvent) => {
@@ -267,7 +286,19 @@ export function bindTableGroupListener(eventManager: EventManager) {
267286
(table.theme.scrollStyle.horizontalVisible && table.theme.scrollStyle.horizontalVisible === 'focus') ||
268287
(!table.theme.scrollStyle.horizontalVisible && table.theme.scrollStyle.visible === 'focus')
269288
) {
270-
stateManager.showHorizontalScrollBar();
289+
const relativeX = e.x - table.tableX;
290+
const target =
291+
table.options.scrollFrozenCols &&
292+
table.getFrozenColsOffset?.() > 0 &&
293+
relativeX >= 0 &&
294+
relativeX < table.getFrozenColsWidth()
295+
? 'frozen'
296+
: table.options.scrollRightFrozenCols &&
297+
table.getRightFrozenColsOffset?.() > 0 &&
298+
relativeX > table.tableNoFrameWidth - table.getRightFrozenColsWidth()
299+
? 'rightFrozen'
300+
: 'body';
301+
stateManager.showHorizontalScrollBar(false, target);
271302
}
272303
if (
273304
(table.theme.scrollStyle.verticalVisible && table.theme.scrollStyle.verticalVisible === 'focus') ||

0 commit comments

Comments
 (0)