Skip to content

Commit

Permalink
feat: ✨add column resize
Browse files Browse the repository at this point in the history
  • Loading branch information
jeremyjone committed Apr 21, 2023
1 parent 4cd6dec commit 6e756df
Show file tree
Hide file tree
Showing 5 changed files with 195 additions and 70 deletions.
32 changes: 5 additions & 27 deletions src/components/common/TableHeader.vue
Original file line number Diff line number Diff line change
Expand Up @@ -7,38 +7,17 @@
</colgroup>
<thead>
<tr v-for="(r, trIndex) in $slotsBox.tableHeaders.headers" :key="trIndex">
<th
v-for="(c, i) in r"
ref="headerRef"
:key="i"
:class="['xg-table-header-cell', 'cell-resizable']"
:style="{ ...$styleBox.getBorderColor() }"
:colspan="c.colSpan"
:rowspan="c.rowSpan"
>
{{ c.label }}
</th>
<TableHeaderTh v-for="(c, i) in r" :key="i" :column="c" />
</tr>
</thead>
</table>
</template>

<script lang="ts" setup>
import TableHeaderTh from './TableHeaderTh.vue';
import useSlotsBox from '@/composables/useSlotsBox';
import useStyle from '@/composables/useStyle';
import { onMounted, ref } from 'vue';
const { $slotsBox } = useSlotsBox();
const { $styleBox } = useStyle();
const headerRef = ref<HTMLElement[]>([]);
onMounted(() => {
headerRef.value.forEach(item => {
item.addEventListener('pointerdown', e => {
console.log(e);
});
});
});
const { $slotsBox } = useSlotsBox();
</script>

<style lang="scss" scoped>
Expand All @@ -63,11 +42,10 @@ onMounted(() => {
border-bottom: 1px solid;
border-right: 1px solid;
padding: 0 20px;
}
.xg-table-header-cell.cell-resizable {
pointer-events: none;
}
.cell-resizable {
&::after {
content: '';
position: absolute;
Expand Down
98 changes: 98 additions & 0 deletions src/components/common/TableHeaderTh.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
<template>
<th
ref="headerRef"
:class="[
'xg-table-header-cell',
{
'cell-resizable': !props.column.isLast
}
]"
:style="{ ...$styleBox.getBorderColor() }"
:colspan="props.column.colSpan"
:rowspan="props.column.rowSpan"
>
{{ props.column.label }}
</th>
</template>

<script lang="ts" setup>
import useDrag from '@/composables/useDrag';
import useSlotsBox from '@/composables/useSlotsBox';
import useStyle from '@/composables/useStyle';
import { ref } from 'vue';
const props = defineProps({
column: {
type: Object,
required: true
}
});
const { $slotsBox } = useSlotsBox();
const { $styleBox } = useStyle();
const { onResizeTableColumn } = useDrag();
const column = ref(props.column);
// 如果有children,取最后一个。children 可能有多层
while (column.value.children?.length > 0) {
column.value = column.value.children[column.value.children.length - 1];
}
const index = column.value.node.props.__index;
const headerRef = ref<HTMLElement | null>(null);
onResizeTableColumn(headerRef, {
onEnd: x => {
$slotsBox.tableHeaders.leafs[index].width = Math.max(
$slotsBox.tableHeaders.leafs[index].width + x,
20
);
},
preMove: (x, clientX) => {
if ($slotsBox.tableHeaders.leafs[index].width + x < 20) return false;
return true;
}
});
</script>

<style lang="scss" scoped>
.xg-table-header {
width: 100%;
height: 80px;
background-color: blueviolet;
table-layout: fixed;
border-collapse: separate;
top: 0;
position: sticky;
z-index: 10;
overflow: hidden;
.xg-table-header-cell {
overflow: hidden;
text-overflow: ellipsis;
vertical-align: middle;
text-align: left;
position: relative;
box-sizing: border-box;
border-bottom: 1px solid;
border-right: 1px solid;
padding: 0 20px;
pointer-events: none;
}
.cell-resizable {
&::after {
content: '';
position: absolute;
right: -5px;
top: 0;
bottom: 0;
width: 10px;
cursor: col-resize;
pointer-events: auto;
}
}
}
</style>
71 changes: 33 additions & 38 deletions src/components/root/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,7 @@
</sync-scroll-container>

<!-- 中分线 -->
<div
ref="midLineRef"
class="xg-mid-separate-line"
@pointerdown="onDownMidLine"
/>
<div ref="midLineRef" class="xg-mid-separate-line" />
<!-- 移动示意线 -->
<div
v-show="showLine"
Expand Down Expand Up @@ -142,44 +138,43 @@ onMounted(() => useResizeObserver(ganttRef.value?.$el, setGanttHeaders));
// #endregion
// #region 加载示意线
const { showLine, onDrag, lineLeft } = useDrag();
function onDownMidLine(e: PointerEvent) {
const rootRect = rootRef.value?.getBoundingClientRect();
lineLeft.value = e.clientX - (rootRect?.left ?? 0);
$param.showMoveLine = true;
}
const { showLine, lineLeft, onResizeTableColumn } = useDrag();
const midLineRef = ref<HTMLElement | null>(null);
onMounted(() => {
onDrag(midLineRef, {
reset: true,
onMove: (x, e) => {
const tableRect = tableRef.value?.$el.getBoundingClientRect();
const ganttRect = ganttRef.value?.$el.getBoundingClientRect();
if (e.clientX < tableRect.left) {
return;
}
if (e.clientX > ganttRect.right - 100) {
return;
}
lineLeft.value = e.clientX;
},
onEnd: x => {
$param.showMoveLine = false;
onResizeTableColumn(midLineRef, {
onEnd: x => {
$slotsBox.tableHeaders.leafs[
$slotsBox.tableHeaders.leafs.length - 1
].width = Math.max(
$slotsBox.tableHeaders.leafs[$slotsBox.tableHeaders.leafs.length - 1]
.width + x,
20
);
},
preMove: (x, clientX) => {
const tableRect = tableRef.value?.$el.getBoundingClientRect();
const ganttRect = ganttRef.value?.$el.getBoundingClientRect();
if (
$slotsBox.tableHeaders.leafs[$slotsBox.tableHeaders.leafs.length - 1]
.width +
x <
20
)
return false;
if (clientX < tableRect.left) {
return false;
}
$slotsBox.tableHeaders.leafs[
$slotsBox.tableHeaders.leafs.length - 1
].width += x;
if (clientX > ganttRect.right - 100) {
return false;
}
});
return true;
}
});
// #endregion
console.log('.....root', getCurrentInstance());
</script>
Expand Down
47 changes: 45 additions & 2 deletions src/composables/useDrag.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
import { useDraggable } from '@vueuse/core';
import { type Ref, ref, computed } from 'vue';
import { type Ref, ref, computed, onMounted } from 'vue';
import useElement from './useElement';
import useParam from './useParam';

const lineLeft = ref(0);

interface DragOptions {
onMove?: (x: number, e: MouseEvent) => void;
onEnd?: (x: number, e: MouseEvent) => void;
onFinally?: () => void;
target?: El;
reset?: boolean;
}
Expand Down Expand Up @@ -42,16 +44,57 @@ export default () => {

onEnd: (pos, e) => {
if (isMove.value) options?.onEnd?.(left.value, e);

options?.onFinally?.();
}
});
}

const { $param } = useParam();
const { rootRef } = useElement();

function onResizeTableColumn(
el: Ref<El>,
options: {
onEnd?: (x: number) => void;
preMove?: (x: number, clientX: number) => boolean;
} = {}
) {
onMounted(() => {
const rootRect = rootRef.value?.getBoundingClientRect();

(el.value as HTMLElement)?.addEventListener('pointerdown', e => {
lineLeft.value = e.clientX - (rootRect?.left ?? 0);
$param.showMoveLine = true;
});

onDrag(el, {
reset: true,

onMove: (x, e) => {
const clientX = e.clientX - (rootRect?.left ?? 0);
if (options?.preMove && !options?.preMove(x, clientX)) return;

lineLeft.value = clientX;
},

onEnd: x => {
options?.onEnd?.(x);
},

onFinally: () => {
$param.showMoveLine = false;
}
});
});
}

const showLine = computed(() => $param.showMoveLine);

return {
onDrag,
showLine,
lineLeft
lineLeft,
onResizeTableColumn
};
};
17 changes: 14 additions & 3 deletions src/models/param/header.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ class TableColumn extends Column {
declare children?: TableColumn[];
parent?: TableColumn;
width: number = Variables.default.tableColumnWidth;
isLast: boolean = false;

/**
*
Expand Down Expand Up @@ -80,6 +81,7 @@ class Header {
maxLevel = column.level;
}
}

if (column.children) {
let colSpan = 0;
column.children.forEach(subColumn => {
Expand Down Expand Up @@ -159,12 +161,21 @@ class TableHeader extends Header {
/**
* This function idea from https://github.com/elemefe/element
*/
private getAllColumns(columns: TableColumn[]) {
private getAllColumns(columns: TableColumn[], isLast?: boolean) {
const result: TableColumn[] = [];
columns.forEach(column => {
columns.forEach((column, index) => {
if (index === columns.length - 1) {
if (isLast === undefined || isLast) {
column.isLast = true;
}
}

if (column.children) {
result.push(column);
result.push.apply(result, this.getAllColumns(column.children));
result.push.apply(
result,
this.getAllColumns(column.children, !!column.isLast)
);
} else {
// 非叶子结点只接收 label 参数作为展示。叶子结点还可以展示 prop 参数值
if (!column.label) column.label = column.node.props?.prop ?? '';
Expand Down

0 comments on commit 6e756df

Please sign in to comment.