Implement custom scrollbar for DataTable component. Refactor structure to include a scrollable wrapper and synchronize scroll positions between the table and the custom scrollbar. Update styles for improved visibility and functionality of the scrollbar.
This commit is contained in:
@ -1246,6 +1246,44 @@ section {
|
|||||||
padding: 10px;
|
padding: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.list-wrapper-container {
|
||||||
|
width: 100%;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.list-wrapper-scrollbar-top {
|
||||||
|
width: 100%;
|
||||||
|
overflow-x: auto;
|
||||||
|
overflow-y: hidden;
|
||||||
|
height: 17px;
|
||||||
|
margin-bottom: 0;
|
||||||
|
display: none;
|
||||||
|
position: relative;
|
||||||
|
z-index: 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
.list-wrapper-scrollbar-top .scrollbar-content {
|
||||||
|
height: 1px;
|
||||||
|
width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.list-wrapper-scrollbar-top::-webkit-scrollbar {
|
||||||
|
height: 17px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.list-wrapper-scrollbar-top::-webkit-scrollbar-track {
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.list-wrapper-scrollbar-top::-webkit-scrollbar-thumb {
|
||||||
|
background: rgba(0, 0, 0, 0.2);
|
||||||
|
border-radius: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.list-wrapper-scrollbar-top::-webkit-scrollbar-thumb:hover {
|
||||||
|
background: rgba(0, 0, 0, 0.3);
|
||||||
|
}
|
||||||
|
|
||||||
.list-wrapper {
|
.list-wrapper {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
position: relative;
|
position: relative;
|
||||||
@ -2374,6 +2412,14 @@ section {
|
|||||||
width: 100% !important;
|
width: 100% !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.list-wrapper-container {
|
||||||
|
overflow: visible;
|
||||||
|
}
|
||||||
|
|
||||||
|
.list-wrapper-scrollbar-top {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
.list-wrapper {
|
.list-wrapper {
|
||||||
overflow: visible;
|
overflow: visible;
|
||||||
overflow-x: visible;
|
overflow-x: visible;
|
||||||
|
|||||||
@ -1,6 +1,10 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="list-wrapper">
|
<div class="list-wrapper-container">
|
||||||
<table class="table-border table-colored table-list">
|
<div class="list-wrapper-scrollbar-top" ref="scrollbarTopRef">
|
||||||
|
<div class="scrollbar-content"></div>
|
||||||
|
</div>
|
||||||
|
<div class="list-wrapper" ref="listWrapperRef" @scroll="onScroll">
|
||||||
|
<table class="table-border table-colored table-list">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th class="table-head-row-number" v-if="rowNumber">
|
<th class="table-head-row-number" v-if="rowNumber">
|
||||||
@ -135,6 +139,7 @@
|
|||||||
</template>
|
</template>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<data-table-pagination
|
<data-table-pagination
|
||||||
v-if="pagination !== undefined && showPagination && !isPreview"
|
v-if="pagination !== undefined && showPagination && !isPreview"
|
||||||
@ -142,7 +147,7 @@
|
|||||||
:isUseRoute="isUseRoute" />
|
:isUseRoute="isUseRoute" />
|
||||||
</template>
|
</template>
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, reactive, computed, watch } from 'vue'
|
import { ref, reactive, computed, watch, onMounted, onUnmounted, nextTick } from 'vue'
|
||||||
import type { Ref } from 'vue'
|
import type { Ref } from 'vue'
|
||||||
import { useGlobalStore } from '@/stores/globalStore'
|
import { useGlobalStore } from '@/stores/globalStore'
|
||||||
import icourl from '@/assets/images/icons.svg'
|
import icourl from '@/assets/images/icons.svg'
|
||||||
@ -172,7 +177,7 @@
|
|||||||
}
|
}
|
||||||
export interface Props {
|
export interface Props {
|
||||||
tableHeader: ITableHead[]
|
tableHeader: ITableHead[]
|
||||||
tableData: Record<string, any>
|
tableData: Record<string, any>[]
|
||||||
rowAction?: Function | string
|
rowAction?: Function | string
|
||||||
pagination?: IPagination
|
pagination?: IPagination
|
||||||
sortData?: ISort
|
sortData?: ISort
|
||||||
@ -285,6 +290,95 @@
|
|||||||
},
|
},
|
||||||
{ deep: true }
|
{ deep: true }
|
||||||
)
|
)
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => props.tableData,
|
||||||
|
() => {
|
||||||
|
// Tablo verisi değiştiğinde scrollbar'ı güncelle
|
||||||
|
setTimeout(() => {
|
||||||
|
syncScrollbars()
|
||||||
|
}, 100)
|
||||||
|
},
|
||||||
|
{ deep: true }
|
||||||
|
)
|
||||||
|
|
||||||
|
const listWrapperRef = ref<HTMLElement | null>(null)
|
||||||
|
const scrollbarTopRef = ref<HTMLElement | null>(null)
|
||||||
|
|
||||||
|
const onScroll = (e: Event) => {
|
||||||
|
const target = e.target as HTMLElement
|
||||||
|
if (scrollbarTopRef.value) {
|
||||||
|
scrollbarTopRef.value.scrollLeft = target.scrollLeft
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const syncScrollbars = () => {
|
||||||
|
if (listWrapperRef.value && scrollbarTopRef.value) {
|
||||||
|
const table = listWrapperRef.value.querySelector('table')
|
||||||
|
if (table) {
|
||||||
|
const tableWidth = table.scrollWidth
|
||||||
|
const wrapperWidth = listWrapperRef.value.clientWidth
|
||||||
|
|
||||||
|
if (tableWidth > wrapperWidth) {
|
||||||
|
scrollbarTopRef.value.style.display = 'block'
|
||||||
|
scrollbarTopRef.value.scrollLeft = listWrapperRef.value.scrollLeft
|
||||||
|
// Scrollbar wrapper'ın içeriğinin genişliğini tablo genişliğine eşitle
|
||||||
|
const scrollbarContent = scrollbarTopRef.value.querySelector('.scrollbar-content') as HTMLElement | null
|
||||||
|
if (scrollbarContent) {
|
||||||
|
scrollbarContent.style.width = tableWidth + 'px'
|
||||||
|
scrollbarContent.style.height = '1px'
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
scrollbarTopRef.value.style.display = 'none'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const onTopScroll = (e: Event) => {
|
||||||
|
const target = e.target as HTMLElement
|
||||||
|
if (listWrapperRef.value) {
|
||||||
|
listWrapperRef.value.scrollLeft = target.scrollLeft
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(async () => {
|
||||||
|
await nextTick()
|
||||||
|
|
||||||
|
if (scrollbarTopRef.value) {
|
||||||
|
scrollbarTopRef.value.addEventListener('scroll', onTopScroll)
|
||||||
|
}
|
||||||
|
|
||||||
|
// İlk scrollbar senkronizasyonu
|
||||||
|
setTimeout(() => {
|
||||||
|
syncScrollbars()
|
||||||
|
}, 100)
|
||||||
|
|
||||||
|
const resizeObserver = new ResizeObserver(() => {
|
||||||
|
syncScrollbars()
|
||||||
|
})
|
||||||
|
|
||||||
|
if (listWrapperRef.value) {
|
||||||
|
resizeObserver.observe(listWrapperRef.value)
|
||||||
|
}
|
||||||
|
|
||||||
|
const tableObserver = new MutationObserver(() => {
|
||||||
|
syncScrollbars()
|
||||||
|
})
|
||||||
|
|
||||||
|
if (listWrapperRef.value) {
|
||||||
|
const table = listWrapperRef.value.querySelector('table')
|
||||||
|
if (table) {
|
||||||
|
tableObserver.observe(table, { childList: true, subtree: true, attributes: true })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
onUnmounted(() => {
|
||||||
|
if (scrollbarTopRef.value) {
|
||||||
|
scrollbarTopRef.value.removeEventListener('scroll', onTopScroll)
|
||||||
|
}
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.action-fixed {
|
.action-fixed {
|
||||||
|
|||||||
Reference in New Issue
Block a user