Skip to content

Commit 139c391

Browse files
authored
feat(STable): use resize indicator during column drag for better perf (#658)
1 parent e1d8859 commit 139c391

File tree

2 files changed

+80
-9
lines changed

2 files changed

+80
-9
lines changed

lib/components/STable.vue

Lines changed: 50 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ const head = shallowRef<HTMLElement | null>(null)
2222
const body = shallowRef<HTMLElement | null>(null)
2323
const block = shallowRef<HTMLElement | null>(null)
2424
const row = shallowRef<HTMLElement | null>(null)
25+
const table = shallowRef<HTMLElement | null>(null)
2526
2627
const ordersToShow = smartComputed(() => {
2728
const orders = unref(props.options.orders).filter((key) => {
@@ -61,6 +62,12 @@ const cellOfColToGrow = computed(() => {
6162
const colWidths = reactive<Record<string, string>>({})
6263
const blockWidth = ref<number | undefined>()
6364
65+
const resizeState = ref<{
66+
columnName: string
67+
startX: number
68+
indicatorX: number
69+
} | null>(null)
70+
6471
watch(() => unref(props.options.columns), (columns) => {
6572
Object.keys(columns).forEach((key) => {
6673
const width = columns[key]?.width
@@ -386,6 +393,29 @@ function getStyles(key: string) {
386393
'--table-col-left': widthSum ? `calc(${widthSum})` : '0px'
387394
}
388395
}
396+
397+
function handleResizeStart(data: { columnName: string; startX: number; initialX: number }) {
398+
const tableRect = table.value?.getBoundingClientRect()
399+
const tableLeft = tableRect?.left ?? 0
400+
const indicatorX = data.initialX - tableLeft
401+
402+
resizeState.value = {
403+
columnName: data.columnName,
404+
startX: indicatorX,
405+
indicatorX
406+
}
407+
}
408+
409+
function handleResizeMove(data: { deltaX: number }) {
410+
if (resizeState.value) {
411+
resizeState.value.indicatorX = resizeState.value.startX + data.deltaX
412+
}
413+
}
414+
415+
function handleResizeEnd(data: { columnName: string; finalWidth: string }) {
416+
updateColWidth(data.columnName, data.finalWidth, true)
417+
resizeState.value = null
418+
}
389419
</script>
390420

391421
<template>
@@ -401,7 +431,13 @@ function getStyles(key: string) {
401431
:selected="Array.isArray(selected) ? selected : undefined"
402432
/>
403433

404-
<div class="table" role="grid">
434+
<div ref="table" class="table" role="grid">
435+
<div
436+
v-if="resizeState"
437+
class="resize-indicator"
438+
:style="{ left: `${resizeState.indicatorX}px` }"
439+
/>
440+
405441
<div ref="head" class="container head" @scroll="syncHeadScroll">
406442
<div ref="block" class="block">
407443
<div ref="row" class="row">
@@ -420,7 +456,9 @@ function getStyles(key: string) {
420456
:dropdown="unref(options.columns)[key]?.dropdown"
421457
:has-header="showHeader"
422458
:resizable="unref(options.columns)[key]?.resizable"
423-
@resize="(value) => updateColWidth(key, value, true)"
459+
@resize-start="handleResizeStart"
460+
@resize-move="handleResizeMove"
461+
@resize-end="handleResizeEnd"
424462
>
425463
<SInputCheckbox
426464
v-if="
@@ -636,6 +674,16 @@ function getStyles(key: string) {
636674
color: var(--c-text-1);
637675
}
638676
677+
.resize-indicator {
678+
position: absolute;
679+
top: 0;
680+
bottom: 0;
681+
width: 1px;
682+
background-color: var(--c-border-info-1);
683+
z-index: 200;
684+
pointer-events: none;
685+
}
686+
639687
.STable .col-__select {
640688
--table-col-width: calc(48px + var(--table-padding-left));
641689

lib/components/STableColumn.vue

Lines changed: 30 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,16 @@ const props = withDefaults(defineProps<{
1717
})
1818
1919
const emit = defineEmits<{
20-
resize: [value: string]
20+
'resize-start': [data: { columnName: string; startX: number; initialX: number }]
21+
'resize-move': [data: { deltaX: number }]
22+
'resize-end': [data: { columnName: string; finalWidth: string }]
2123
}>()
2224
2325
const { container, isOpen, toggle } = useFlyout()
2426
2527
let startWidth = 0
2628
let startPoint = 0
29+
let originalUserSelect = ''; let originalPointerEvents = ''
2730
2831
const column = ref<HTMLElement | null>(null)
2932
const dialog = ref<HTMLElement | null>(null)
@@ -67,24 +70,43 @@ watch(isOpen, (value) => {
6770
value ? adjustDialogPosition() : stopDialogPositionListener()
6871
})
6972
70-
function grip(e: any) {
73+
function grip(e: MouseEvent) {
7174
startWidth = column.value?.offsetWidth ?? 0
7275
startPoint = e.pageX
7376
77+
emit('resize-start', {
78+
columnName: props.name,
79+
startX: e.pageX,
80+
initialX: e.pageX
81+
})
82+
83+
originalUserSelect = document.body.style.userSelect
84+
originalPointerEvents = document.body.style.pointerEvents
85+
document.body.style.userSelect = 'none'
86+
document.body.style.pointerEvents = 'none'
7487
document.addEventListener('mousemove', resize)
7588
document.addEventListener('mouseup', stopResizeListener)
7689
}
7790
7891
function resize(e: MouseEvent) {
79-
const movedWidth = e.pageX - startPoint
80-
const resized = startWidth + movedWidth
81-
82-
emit('resize', resized > -1 ? `${resized}px` : 'var(--table-col-width)')
92+
const deltaX = e.pageX - startPoint
93+
emit('resize-move', { deltaX })
8394
}
8495
85-
function stopResizeListener() {
96+
function stopResizeListener(e: MouseEvent) {
8697
document.removeEventListener('mousemove', resize)
8798
document.removeEventListener('mouseup', stopResizeListener)
99+
document.body.style.userSelect = originalUserSelect
100+
document.body.style.pointerEvents = originalPointerEvents
101+
102+
const movedWidth = e.pageX - startPoint
103+
const resized = startWidth + movedWidth
104+
const finalWidth = resized > -1 ? `${resized}px` : 'var(--table-col-width)'
105+
106+
emit('resize-end', {
107+
columnName: props.name,
108+
finalWidth
109+
})
88110
}
89111
90112
async function adjustDialogPosition() {
@@ -138,6 +160,7 @@ function stopDialogPositionListener() {
138160
</div>
139161

140162
<div v-if="resizable" class="grip" @mousedown="grip" />
163+
<div v-if="resizable" class="resize-indicator" />
141164
</slot>
142165
</div>
143166
</div>

0 commit comments

Comments
 (0)