vue-fantable
Version:
Vue table component for huge data
1,621 lines (1,441 loc) • 45.4 kB
JSX
import {
clsName,
isLastColumnByColKey,
isLastRowByRowKey,
getColKeysByRangeColKeys,
isExistGivenFixedColKey,
isExistNotFixedColKey,
getLeftmostColKey,
getRightmostColKey,
getColKeysByFixedTypeWithinColKeys,
getTotalWidthByColKeys,
getPreviewColKey,
getNextColKey,
} from '../util/index'
import {
HOOKS_NAME,
AUTOFILLING_DIRECTION,
CURRENT_CELL_SELECTION_TYPES,
COLUMN_FIXED_TYPE,
} from '../util/constant'
import { INSTANCE_METHODS } from './constant'
import { isEmptyValue, isBoolean } from '../../../src/utils/index.js'
import { debounce } from '@P/src/utils/index.js'
// import eventCenter from '@P/events/event-center'
import { GLOBAL_EVENT } from '@P/events/global-events'
export default {
// name: COMPS_NAME.FAN_TABLE_SELECTION,
name: 'FanTableSelection',
inject: ['eventCenter'],
props: {
tableEl: {
type: HTMLTableElement,
default: null,
},
allRowKeys: {
type: Array,
required: true,
},
colgroups: {
type: Array,
required: true,
},
parentRendered: {
type: Boolean,
required: true,
},
hooks: {
type: Object,
required: true,
},
// cell autofill option
cellAutofillOption: {
type: [Object, Boolean],
default: function () {
return null
},
},
cellSelectionData: {
type: Object,
required: true,
},
cellSelectionRangeData: {
type: Object,
required: true,
},
isAutofillStarting: {
type: Boolean,
required: true,
},
currentCellSelectionType: {
type: String,
default: '',
},
// is scrolling
showVirtualScrollingPlaceholder: {
type: Boolean,
default: false,
},
isVirtualScroll: {
type: Boolean,
default: false,
},
virtualScrollVisibleIndexs: {
type: Object,
required: true,
},
isCellEditing: {
type: Boolean,
default: false,
},
},
emits: ['cellSelectionRangeDataChange'],
data() {
return {
// current cell
currentCellEl: null,
normalEndCellEl: null,
autoFillEndCellEl: null,
// cell selection rect
cellSelectionRect: {
// current cell element rect
currentCellRect: {
left: 0,
top: 0,
width: 0,
height: 0,
},
// normal end cell element rect
normalEndCellRect: {
left: 0,
top: 0,
width: 0,
height: 0,
},
// auto fill end cell element rect
autoFillEndCellRect: {
left: 0,
top: 0,
width: 0,
height: 0,
},
},
}
},
computed: {
// selection borders visibility
selectionBordersVisibility() {
let result = true
if (this.isVirtualScroll) {
const {
showVirtualScrollingPlaceholder,
cellSelectionData,
virtualScrollVisibleIndexs,
currentCellSelectionType,
} = this
if (showVirtualScrollingPlaceholder) {
result = false
} else {
const { currentCell, normalEndCell } = cellSelectionData
if (
currentCellSelectionType ===
CURRENT_CELL_SELECTION_TYPES.SINGLE
) {
if (
currentCell.rowIndex <
virtualScrollVisibleIndexs.start ||
currentCell.rowIndex >
virtualScrollVisibleIndexs.end
) {
result = false
}
}
if (
currentCellSelectionType ===
CURRENT_CELL_SELECTION_TYPES.RANGE
) {
if (
(currentCell.rowIndex <
virtualScrollVisibleIndexs.start &&
normalEndCell.rowIndex <
virtualScrollVisibleIndexs.start) ||
(currentCell.rowIndex >
virtualScrollVisibleIndexs.end &&
normalEndCell.rowIndex >
virtualScrollVisibleIndexs.end)
) {
result = false
}
}
}
}
return result
},
// show corner
showCorner() {
let result = true
const { cellAutofillOption } = this
if (cellAutofillOption) {
const { directionX, directionY } = this.cellAutofillOption
if (
isBoolean(directionY) &&
!directionY &&
isBoolean(directionX) &&
!directionX
) {
result = false
}
} else {
result = false
}
return result
},
// corner cell info
cornerCellInfo() {
const { allRowKeys, colgroups, cellSelectionRangeData } = this
const { rightColKey, bottomRowKey } = cellSelectionRangeData
let isLastColumn = false
if (isLastColumnByColKey(rightColKey, colgroups)) {
isLastColumn = true
} else {
const index = colgroups.findIndex((x) => x.key === rightColKey)
// right col is right fixed and current col is not right fixed
if (
colgroups[index + 1].fixed === COLUMN_FIXED_TYPE.RIGHT &&
colgroups[index].fixed !== COLUMN_FIXED_TYPE.RIGHT
) {
isLastColumn = true
}
}
let isLastRow = false
if (isLastRowByRowKey(bottomRowKey, allRowKeys)) {
isLastRow = true
}
return {
isLastColumn,
isLastRow,
}
},
// is first selection row
isFirstSelectionRow() {
const { allRowKeys, cellSelectionRangeData } = this
return allRowKeys[0] === cellSelectionRangeData.topRowKey
},
// is first selection column
isFirstSelectionCol() {
const { colgroups, cellSelectionRangeData } = this
return colgroups[0].key === cellSelectionRangeData.leftColKey
},
// is first not fixed selection column
isFirstNotFixedSelectionCol() {
let result = false
const { colgroups, cellSelectionRangeData } = this
if (colgroups.find((x) => x.fixed === 'left')) {
const col = colgroups.find((x) => !x.fixed)
if (col && col.field === cellSelectionRangeData.leftColKey) {
result = true
}
}
return result
},
},
watch: {
parentRendered: {
handler: function (val) {
if (val) {
// add table container scroll hook
this.hooks.addHook(
HOOKS_NAME.TABLE_CONTAINER_SCROLL,
() => {
this.setCellEls()
this.debounceSetCellEls()
this.resetCellPositions()
// debounce reset cell positions
this.debounceResetCellPositions()
},
)
// add table size change hook
this.hooks.addHook(HOOKS_NAME.TABLE_SIZE_CHANGE, () => {
// debounce reset cell positions
this.debounceResetCellPositions()
})
// add table td width change hook
this.hooks.addHook(
HOOKS_NAME.TABLE_CELL_WIDTH_CHANGE,
() => {
this.$nextTick(() => {
this.resetCellPositions()
})
},
)
// add clipboard cell value change hook
this.hooks.addHook(
HOOKS_NAME.CLIPBOARD_CELL_VALUE_CHANGE,
() => {
this.$nextTick(() => {
this.resetCellPositions()
})
},
)
}
},
immediate: true,
},
// watch current cell
'cellSelectionData.currentCell': {
handler: function (val) {
const { rowKey, colKey } = val
if (!isEmptyValue(rowKey) && !isEmptyValue(colKey)) {
this.setCurrentCellEl()
this.setSelectionPositions({ type: 'currentCell' })
} else {
this[INSTANCE_METHODS.CLEAR_CURRENT_CELL_RECT]()
}
this.setCellSelectionRangeData()
},
deep: true,
immediate: true,
},
// watch normal end cell
'cellSelectionData.normalEndCell': {
handler: function (val) {
const { rowKey, colKey } = val
if (!isEmptyValue(rowKey) && !isEmptyValue(colKey)) {
// set normal end cell el
this.setNormalEndCellEl()
this.setSelectionPositions({ type: 'normalEndCell' })
} else {
this[INSTANCE_METHODS.CLEAR_NORMAL_END_CELL_RECT]()
}
this.setCellSelectionRangeData()
},
deep: true,
immediate: true,
},
// watch autofill cell
'cellSelectionData.autoFillEndCell': {
handler: function (val) {
const { rowKey, colKey } = val
if (!isEmptyValue(rowKey) && !isEmptyValue(colKey)) {
this.setAutofillEndCellEl()
this.setSelectionPositions({ type: 'autoFillEndCell' })
} else {
this.clearAutofillEndCellRect()
}
},
deep: true,
immediate: true,
},
},
created() {
// debounce reset cell positions
this.debounceResetCellPositions = debounce(
this.resetCellPositions,
210,
)
// debounce set cell els
this.debounceSetCellEls = debounce(this.setCellEls, 200)
},
methods: {
// reset cell position
resetCellPositions() {
const { currentCell, normalEndCell } = this.cellSelectionData
if (
!isEmptyValue(currentCell.rowKey) &&
!isEmptyValue(currentCell.colKey)
) {
this.setSelectionPositions({
type: 'currentCell',
})
}
if (
!isEmptyValue(normalEndCell.rowKey) &&
!isEmptyValue(normalEndCell.colKey)
) {
this.setSelectionPositions({
type: 'normalEndCell',
})
}
},
// set cell els
setCellEls() {
if (this.isVirtualScroll && this.selectionBordersVisibility) {
this.setCurrentCellEl()
this.setNormalEndCellEl()
}
},
// set cell selection range data
setCellSelectionRangeData() {
const { currentCellSelectionType } = this
const { currentCell, normalEndCell } = this.cellSelectionData
let result = {}
if (
currentCellSelectionType === CURRENT_CELL_SELECTION_TYPES.SINGLE
) {
result = {
leftColKey: currentCell.colKey,
rightColKey: currentCell.colKey,
topRowKey: currentCell.rowKey,
bottomRowKey: currentCell.rowKey,
}
} else if (
currentCellSelectionType === CURRENT_CELL_SELECTION_TYPES.RANGE
) {
const leftmostColKey = getLeftmostColKey({
colgroups: this.colgroups,
colKeys: [currentCell.colKey, normalEndCell.colKey],
})
/*
current cell col key is leftmost colKey
需要用 colKey 的位置进行判断,不能根据当前单元格 left 值判断(固定列时)
*/
if (leftmostColKey === currentCell.colKey) {
result.leftColKey = currentCell.colKey
result.rightColKey = normalEndCell.colKey
} else {
result.leftColKey = normalEndCell.colKey
result.rightColKey = currentCell.colKey
}
if (currentCell.rowIndex < normalEndCell.rowIndex) {
result.topRowKey = currentCell.rowKey
result.bottomRowKey = normalEndCell.rowKey
} else {
result.topRowKey = normalEndCell.rowKey
result.bottomRowKey = currentCell.rowKey
}
} else {
// clear
result = {
leftColKey: '',
rightColKey: '',
topRowKey: '',
bottomRowKey: '',
}
}
// this.$emit(EMIT_EVENTS.CELL_SELECTION_RANGE_DATA_CHANGE, result)
this.$emit('cellSelectionRangeDataChange', result)
},
// get cell position
getCellPosition({ cellEl, tableLeft, tableTop }) {
if (!this.selectionBordersVisibility) {
return false
}
const {
left: cellLeft,
top: cellTop,
height: cellHeight,
width: cellWidth,
} = cellEl.getBoundingClientRect()
if (cellHeight && cellWidth) {
return {
left: cellLeft - tableLeft,
top: cellTop - tableTop,
width: cellWidth,
height: cellHeight,
}
}
},
// get cell position by column key
getCellPositionByColKey({
tableLeft,
tableTop,
colKey,
isFirstRow,
isLastRow,
}) {
if (!this.selectionBordersVisibility) {
return false
}
let cellEl
if (isFirstRow) {
cellEl = this.getTableFirstRowCellByColKey(colKey)
} else if (isLastRow) {
cellEl = this.getTableLastRowCellByColKey(colKey)
}
if (!cellEl) {
return
}
const {
left: cellLeft,
top: cellTop,
// height: cellHeight,
width: cellWidth,
} = cellEl.getBoundingClientRect()
if (cellWidth) {
return {
left: cellLeft - tableLeft,
top: cellTop - tableTop,
width: cellWidth,
}
}
},
// set selection positions
setSelectionPositions({ type }) {
const {
allRowKeys,
tableEl,
currentCellEl,
normalEndCellEl,
autoFillEndCellEl,
cellSelectionData,
virtualScrollVisibleIndexs,
} = this
// table empty
if (allRowKeys.length === 0) {
return false
}
if (!tableEl) {
return false
}
const { left: tableLeft, top: tableTop } =
tableEl.getBoundingClientRect()
let isCurrentCellOverflow = false
let isNormalEndCellOverflow = false
// set current cell position
if (type === 'currentCell') {
isCurrentCellOverflow = true
if (currentCellEl) {
const rect = this.getCellPosition({
cellEl: currentCellEl,
tableLeft,
tableTop,
})
if (rect) {
isCurrentCellOverflow = false
this.cellSelectionRect.currentCellRect = rect
}
}
}
// set nromal end cell position`
if (type === 'normalEndCell') {
isNormalEndCellOverflow = true
if (normalEndCellEl) {
const rect = this.getCellPosition({
cellEl: normalEndCellEl,
tableLeft,
tableTop,
})
if (rect) {
isNormalEndCellOverflow = false
this.cellSelectionRect.normalEndCellRect = rect
}
}
}
// current cell overflow or normal end cell overflow && is virtual scroll
if (
(isCurrentCellOverflow || isNormalEndCellOverflow) &&
this.isVirtualScroll
) {
const { currentCell, normalEndCell } = cellSelectionData
// 弥补的
let mackUpColKey
let mackUpRowIndex
if (isCurrentCellOverflow) {
mackUpColKey = currentCell.colKey
mackUpRowIndex = currentCell.rowIndex
} else {
mackUpColKey = normalEndCell.colKey
mackUpRowIndex = normalEndCell.rowIndex
}
let mackUpRect
/*
当没有 currentCellRect 或 normalCellRect 时 进行纠正,否则只更新top值
*/
if (
(isCurrentCellOverflow &&
!this.cellSelectionRect.currentCellRect.height) ||
(isNormalEndCellOverflow &&
!this.cellSelectionRect.normalEndCellRect.height)
) {
const mackUpRectParams = {
tableLeft,
tableTop,
colKey: mackUpColKey,
}
// 上方超出
if (mackUpRowIndex < virtualScrollVisibleIndexs.start) {
mackUpRect = this.getCellPositionByColKey({
...mackUpRectParams,
isFirstRow: true,
})
} else if (mackUpRowIndex > virtualScrollVisibleIndexs.end) {
// 下方超出
mackUpRect = this.getCellPositionByColKey({
...mackUpRectParams,
isLastRow: true,
})
}
} else {
// 仅更新 top 值
// 上方超出
if (mackUpRowIndex < virtualScrollVisibleIndexs.start) {
mackUpRect = {
top: 0,
}
} else if (mackUpRowIndex > virtualScrollVisibleIndexs.end) {
// 下方超出
mackUpRect = {
top: tableEl.clientHeight,
}
}
}
if (isCurrentCellOverflow) {
Object.assign(
this.cellSelectionRect.currentCellRect,
mackUpRect,
)
} else {
Object.assign(
this.cellSelectionRect.normalEndCellRect,
mackUpRect,
)
}
}
if (autoFillEndCellEl && type === 'autoFillEndCell') {
const rect = this.getCellPosition({
cellEl: autoFillEndCellEl,
tableLeft,
tableTop,
})
if (rect) {
this.cellSelectionRect.autoFillEndCellRect = rect
}
}
},
/*
get selection current
1、selection current
2、auto fill area
*/
getSelectionCurrent({ fixedType }) {
const result = {
selectionCurrent: null,
autoFillArea: null,
}
const { cellSelectionRect, colgroups, cellSelectionData } = this
const { currentCellRect, normalEndCellRect } = cellSelectionRect
if (!currentCellRect.width) {
return result
}
const borders = {
borderWidth: currentCellRect.width + 1,
borderHeight: currentCellRect.height,
topBorder: {
show: true,
width: 0,
height: 2,
top: currentCellRect.top - 1,
left: currentCellRect.left - 1,
},
rightBorder: {
show: true,
width: 2,
height: 0,
top: currentCellRect.top,
left: currentCellRect.left + currentCellRect.width - 2,
},
bottomBorder: {
show: true,
width: 0,
height: 2,
top: currentCellRect.top + currentCellRect.height - 2,
left: currentCellRect.left - 1,
},
leftBorder: {
show: true,
width: 2,
height: 0,
top: currentCellRect.top,
left: currentCellRect.left - 1,
},
corner: {
show: !normalEndCellRect.width,
top: 0,
left: 0,
},
}
borders.corner.top = borders.bottomBorder.top - 3
borders.corner.left = borders.rightBorder.left - 3
// cell selection single autofill
if (!normalEndCellRect.width) {
result.autoFillArea = this.getSelectionAutofillArea({
areaPostions: borders,
fixedType,
})
}
const totalColKeys = [cellSelectionData.currentCell.colKey]
const fixedColKeys = getColKeysByFixedTypeWithinColKeys({
colKeys: totalColKeys,
fixedType,
colgroups,
})
result.selectionCurrent = this.getBorders({
...borders,
showCorner: !normalEndCellRect.width,
className: 'selection-current',
fixedType,
totalColKeys,
fixedColKeys,
})
return result
},
/*
get selection areas
1、normal area
2、auto fill area
*/
getSelectionAreas({ fixedType }) {
const result = {
normalArea: null,
autoFillArea: null,
}
const { currentCell, normalEndCell } = this.cellSelectionData
const { cellSelectionRect, cellSelectionRangeData, colgroups } = this
const { currentCellRect, normalEndCellRect } = cellSelectionRect
if (!currentCellRect.width || !normalEndCellRect.width) {
return result
}
const borders = {
borderWidth: 0,
borderHeight: 0,
topBorder: {
show: true,
width: 0,
height: 1,
top: 0,
left: 0,
},
rightBorder: {
show: true,
width: 1,
height: 0,
top: 0,
left: 0,
},
bottomBorder: {
show: true,
width: 0,
height: 1,
top: 0,
left: 0,
},
leftBorder: {
show: true,
width: 1,
height: 0,
top: 0,
left: 0,
},
corner: {
show: true,
top: 0,
left: 0,
},
}
const leftmostColKey = getLeftmostColKey({
colgroups: this.colgroups,
colKeys: [currentCell.colKey, normalEndCell.colKey],
})
// end cell column key right
if (leftmostColKey === currentCell.colKey) {
borders.borderWidth =
normalEndCellRect.left -
currentCellRect.left +
normalEndCellRect.width +
1
borders.topBorder.left = currentCellRect.left - 1
borders.bottomBorder.left = currentCellRect.left - 1
borders.leftBorder.left = currentCellRect.left - 1
borders.rightBorder.left =
normalEndCellRect.left + normalEndCellRect.width - 1
} else if (leftmostColKey === normalEndCell.colKey) {
// end cell column key left or equal
borders.borderWidth =
currentCellRect.left -
normalEndCellRect.left +
currentCellRect.width +
1
borders.topBorder.left = normalEndCellRect.left - 1
borders.rightBorder.left =
currentCellRect.left + currentCellRect.width - 1
borders.bottomBorder.left = normalEndCellRect.left - 1
borders.leftBorder.left = normalEndCellRect.left - 1
}
// end cell below
if (normalEndCellRect.top > currentCellRect.top) {
borders.borderHeight =
normalEndCellRect.top -
currentCellRect.top +
normalEndCellRect.height
borders.topBorder.top = currentCellRect.top - 1
borders.rightBorder.top = currentCellRect.top
borders.bottomBorder.top =
normalEndCellRect.top + normalEndCellRect.height - 1
borders.leftBorder.top = currentCellRect.top
} else if (normalEndCellRect.top <= currentCellRect.top) {
// end cell above or equal
borders.borderHeight =
currentCellRect.top -
normalEndCellRect.top +
currentCellRect.height
borders.topBorder.top = normalEndCellRect.top - 1
borders.rightBorder.top = normalEndCellRect.top
borders.bottomBorder.top =
currentCellRect.top + currentCellRect.height - 1
borders.leftBorder.top = normalEndCellRect.top
}
borders.corner.top = borders.bottomBorder.top - 4
borders.corner.left = borders.rightBorder.left - 4
if (normalEndCellRect.width) {
result.autoFillArea = this.getSelectionAutofillArea({
areaPostions: borders,
fixedType,
})
}
const { leftColKey, rightColKey } = cellSelectionRangeData
const totalColKeys = getColKeysByRangeColKeys({
colKey1: leftColKey,
colKey2: rightColKey,
colgroups,
})
const fixedColKeys = getColKeysByFixedTypeWithinColKeys({
colKeys: totalColKeys,
fixedType,
colgroups,
})
result.normalArea = this.getBorders({
...borders,
className: 'selection-normal-area',
fixedType,
totalColKeys,
fixedColKeys,
})
result.normalAreaLayer = this.getAreaLayer({
...borders,
className: 'selection-normal-area-layer',
fixedType,
totalColKeys,
fixedColKeys,
})
return result
},
// get selection auto fill
getSelectionAutofillArea({ areaPostions, fixedType }) {
let result = null
const {
cellAutofillOption,
cellSelectionRangeData,
cellSelectionRect,
cellSelectionData,
isAutofillStarting,
currentCellSelectionType,
colgroups,
} = this
if (!isAutofillStarting) {
return result
}
const { currentCellRect, autoFillEndCellRect } = cellSelectionRect
if (!currentCellRect.width || !autoFillEndCellRect.width) {
return result
}
if (!areaPostions) {
return result
}
const borders = {
borderWidth: 0,
borderHeight: 0,
topBorder: {
show: true,
width: 0,
height: 1,
top: 0,
left: 0,
},
rightBorder: {
show: true,
width: 1,
height: 0,
top: 0,
left: 0,
},
bottomBorder: {
show: true,
width: 0,
height: 1,
top: 0,
left: 0,
},
leftBorder: {
show: true,
width: 1,
height: 0,
top: 0,
left: 0,
},
corner: {
show: false,
top: 0,
left: 0,
},
}
const { currentCell, autoFillEndCell } = cellSelectionData
let { leftColKey, rightColKey } = cellSelectionRangeData
if (
currentCellSelectionType === CURRENT_CELL_SELECTION_TYPES.SINGLE
) {
leftColKey = currentCell.colKey
rightColKey = currentCell.colKey
}
let leftmostColKey
if (leftColKey !== autoFillEndCell.colKey) {
leftmostColKey = getLeftmostColKey({
colgroups,
colKeys: [leftColKey, autoFillEndCell.colKey],
})
}
let rightmostColKey
if (rightColKey !== autoFillEndCell.colKey) {
rightmostColKey = getRightmostColKey({
colgroups,
colKeys: [rightColKey, autoFillEndCell.colKey],
})
}
// autofilling direction
let autofillingDirection
let rangeColKey1
let rangeColKey2
// auto fill end cell below
if (autoFillEndCellRect.top > areaPostions.bottomBorder.top) {
autofillingDirection = AUTOFILLING_DIRECTION.DOWN
rangeColKey1 = leftColKey
rangeColKey2 = rightColKey
borders.topBorder.show = false
borders.borderWidth = areaPostions.borderWidth
borders.borderHeight =
autoFillEndCellRect.top -
areaPostions.bottomBorder.top +
autoFillEndCellRect.height
borders.rightBorder.top = areaPostions.bottomBorder.top
borders.rightBorder.left = areaPostions.rightBorder.left
if (
currentCellSelectionType ===
CURRENT_CELL_SELECTION_TYPES.SINGLE
) {
borders.rightBorder.left++
}
borders.leftBorder.top = areaPostions.bottomBorder.top
borders.leftBorder.left = areaPostions.leftBorder.left
borders.bottomBorder.top =
autoFillEndCellRect.top + autoFillEndCellRect.height - 1
borders.bottomBorder.left = areaPostions.bottomBorder.left
} else if (autoFillEndCellRect.top < areaPostions.topBorder.top) {
// end cell above
autofillingDirection = AUTOFILLING_DIRECTION.UP
rangeColKey1 = leftColKey
rangeColKey2 = rightColKey
borders.bottomBorder.show = false
borders.borderWidth = areaPostions.borderWidth
borders.borderHeight =
areaPostions.topBorder.top - autoFillEndCellRect.top
borders.topBorder.top = autoFillEndCellRect.top - 1
borders.topBorder.left = areaPostions.topBorder.left
borders.rightBorder.top = autoFillEndCellRect.top
borders.rightBorder.left = areaPostions.rightBorder.left
if (
currentCellSelectionType ===
CURRENT_CELL_SELECTION_TYPES.SINGLE
) {
borders.rightBorder.left++
}
borders.leftBorder.top = autoFillEndCellRect.top
borders.leftBorder.left = areaPostions.leftBorder.left
} else if (rightmostColKey === autoFillEndCell.colKey && !isEmptyValue(rightmostColKey)) {
// auto fill end cell right
autofillingDirection = AUTOFILLING_DIRECTION.RIGHT
rangeColKey1 = getNextColKey({
colgroups,
currentColKey: rightColKey,
})
rangeColKey2 = autoFillEndCell.colKey
borders.leftBorder.show = false
borders.borderWidth =
autoFillEndCellRect.left -
areaPostions.rightBorder.left +
autoFillEndCellRect.width +
1
borders.borderHeight = areaPostions.borderHeight
borders.topBorder.top = areaPostions.topBorder.top
borders.topBorder.left = areaPostions.rightBorder.left - 1
borders.rightBorder.top = areaPostions.topBorder.top
borders.rightBorder.left =
autoFillEndCellRect.left + autoFillEndCellRect.width - 1
borders.bottomBorder.top = areaPostions.bottomBorder.top
borders.bottomBorder.left = areaPostions.rightBorder.left - 1
} else if (leftmostColKey === autoFillEndCell.colKey && !isEmptyValue(leftmostColKey)) {
// auto fill end cell left
autofillingDirection = AUTOFILLING_DIRECTION.LEFT
rangeColKey1 = getPreviewColKey({
colgroups,
currentColKey: leftColKey,
})
rangeColKey2 = autoFillEndCell.colKey
borders.rightBorder.show = false
borders.borderWidth = areaPostions.leftBorder.left - autoFillEndCellRect.left + 1
borders.borderHeight = areaPostions.borderHeight
borders.topBorder.top = areaPostions.topBorder.top
borders.topBorder.left = autoFillEndCellRect.left
borders.rightBorder.left = areaPostions.topBorder.left
borders.bottomBorder.top = areaPostions.bottomBorder.top
borders.bottomBorder.left = autoFillEndCellRect.left
borders.leftBorder.top = areaPostions.topBorder.top
borders.leftBorder.left = autoFillEndCellRect.left
} else {
return result
}
const { directionX, directionY } = cellAutofillOption
if (isBoolean(directionX) && !directionX) {
if (
autofillingDirection === AUTOFILLING_DIRECTION.LEFT ||
autofillingDirection === AUTOFILLING_DIRECTION.RIGHT
) {
return false
}
}
if (isBoolean(directionY) && !directionY) {
if (
autofillingDirection === AUTOFILLING_DIRECTION.UP ||
autofillingDirection === AUTOFILLING_DIRECTION.DOWN
) {
return false
}
}
const totalColKeys = getColKeysByRangeColKeys({
colKey1: rangeColKey1,
colKey2: rangeColKey2,
colgroups,
})
const fixedColKeys = getColKeysByFixedTypeWithinColKeys({
colKeys: totalColKeys,
fixedType,
colgroups,
})
result = this.getBorders({
className: 'selection-autofill-area',
...borders,
fixedType,
totalColKeys,
fixedColKeys,
})
if (result) {
this.eventCenter.emit(
GLOBAL_EVENT.AUTOFILLING_DIRECTION_CHANGE,
autofillingDirection,
)
}
return result
},
// get borders
getBorders({
borderWidth,
borderHeight,
topBorder,
rightBorder,
bottomBorder,
leftBorder,
corner,
className,
fixedType,
totalColKeys,
fixedColKeys,
}) {
const {
cornerCellInfo,
colgroups,
isFirstSelectionRow,
isFirstSelectionCol,
isFirstNotFixedSelectionCol,
showCorner,
} = this
let isRender = true
if (fixedType) {
isRender = isExistGivenFixedColKey({
fixedType,
colKeys: totalColKeys,
colgroups,
})
} else { // middle normal area
isRender = isExistNotFixedColKey({
colKeys: totalColKeys,
colgroups,
})
}
if (!isRender) {
return null
}
// fixed columns total width
let fixedColsTotalWidth = 0
if (fixedColKeys.length) {
fixedColsTotalWidth = getTotalWidthByColKeys({
colKeys: fixedColKeys,
colgroups,
})
console.log('🚀 ~ fixedColsTotalWidth:', fixedColsTotalWidth)
}
if (fixedType) {
borderWidth = fixedColsTotalWidth
if (fixedType === COLUMN_FIXED_TYPE.LEFT) {
borderWidth += 1
}
}
if (fixedType === COLUMN_FIXED_TYPE.LEFT) {
if (totalColKeys.length !== fixedColKeys.length) {
rightBorder.show = false
corner.show = false
}
}
if (fixedType === COLUMN_FIXED_TYPE.RIGHT) {
if (totalColKeys.length !== fixedColKeys.length) {
leftBorder.show = false
}
topBorder.left = rightBorder.left - borderWidth + 1
bottomBorder.left = rightBorder.left - borderWidth + 1
}
// solved first row、first column、first not fixed column selection border hidden
if (isFirstSelectionRow) {
topBorder.top += 1
}
if (isFirstSelectionCol) {
leftBorder.left += 1
}
if (isFirstNotFixedSelectionCol) {
leftBorder.left += 1
}
let cornerTop = corner.top
let cornerLeft = corner.left
let cornerBorderRightWidth = '1px'
let cornerBorderBottomtWidth = '1px'
if (cornerCellInfo.isLastRow) {
cornerTop -= 3
cornerBorderBottomtWidth = '0px'
}
if (cornerCellInfo.isLastColumn) {
cornerLeft -= 3
cornerBorderRightWidth = '0px'
}
if (!showCorner) {
corner.show = false
}
// corner props
const cornerProps = {
class: clsName('selection-corner'),
style: {
display: corner.show ? 'block' : 'none',
top: cornerTop + 'px',
left: cornerLeft + 'px',
borderWidth: `1px ${cornerBorderRightWidth} ${cornerBorderBottomtWidth} 1px`,
},
onMousedown: (e) => {
this.eventCenter.emit(GLOBAL_EVENT.SELECTION_CORNER_MOUSEDOWN,
{
event: e,
},
)
},
onMouseup: (e) => {
this.eventCenter.emit(GLOBAL_EVENT.SELECTION_CORNER_MOUSEUP,
{
event: e,
},
)
},
}
const calcBorderWidth = rightBorder.left - leftBorder.left + 2
return (
<div class={clsName(className)}>
{/* top */}
<div
style={{
display: topBorder.show ? 'block' : 'none',
width: calcBorderWidth + 'px',
height: topBorder.height + 'px',
top: topBorder.top + 'px',
left: topBorder.left + 'px',
}}
class={clsName('selection-border')}
></div>
{/* right */}
<div
style={{
display: rightBorder.show ? 'block' : 'none',
width: rightBorder.width + 'px',
height: borderHeight + 'px',
top: rightBorder.top + 'px',
left: rightBorder.left + 'px',
}}
class={clsName('selection-border')}
></div>
{/* bottom */}
<div
style={{
display: bottomBorder.show ? 'block' : 'none',
width: calcBorderWidth + 'px',
height: bottomBorder.height + 'px',
top: bottomBorder.top + 'px',
left: bottomBorder.left + 'px',
}}
class={clsName('selection-border')}
></div>
{/* left */}
<div
style={{
display: leftBorder.show ? 'block' : 'none',
width: leftBorder.width + 'px',
height: borderHeight + 'px',
top: leftBorder.top + 'px',
left: leftBorder.left + 'px',
}}
class={clsName('selection-border')}
></div>
{/* corner */}
{<div {...cornerProps}></div>}
</div>
)
},
// get area rect
getAreaLayer({
borderWidth,
borderHeight,
topBorder,
className,
fixedType,
totalColKeys,
fixedColKeys,
}) {
const { colgroups } = this
let isRender = true
if (fixedType) {
isRender = isExistGivenFixedColKey({
fixedType,
colKeys: totalColKeys,
colgroups,
})
} else {
// middle normal area
isRender = isExistNotFixedColKey({
colKeys: totalColKeys,
colgroups,
})
}
if (!isRender) {
return null
}
// fixed columns total width
let fixedColsTotalWidth = 0
if (fixedColKeys.length) {
fixedColsTotalWidth = getTotalWidthByColKeys({
colKeys: fixedColKeys,
colgroups,
})
}
if (fixedType) {
borderWidth = fixedColsTotalWidth
if (fixedType === COLUMN_FIXED_TYPE.LEFT) {
borderWidth += 1
}
}
return (
<div
class={clsName(className)}
style={{
top: topBorder.top + 'px',
left: topBorder.left + 'px',
width: borderWidth + 'px',
height: borderHeight + 'px',
}}
></div>
)
},
/*
get table first row cell by col key
用作跨页单元格选择,表格大小变化或者存在横向滚动条时,区域选择位置自动校准
*/
getTableFirstRowCellByColKey(colKey) {
let result = null
const { tableEl } = this
if (tableEl) {
result = tableEl.querySelector(
`tbody.fan-table-body tr td[col-key="${colKey}"]`,
)
}
return result
},
/*
get table last row cell by col key
用作跨页单元格选择,表格大小变化或者存在横向滚动条时,区域选择位置自动校准
*/
getTableLastRowCellByColKey(colKey) {
let result = null
const { tableEl } = this
if (tableEl) {
result = tableEl.querySelector(
`tbody.fan-table-body tr:last-child td[col-key="${colKey}"]`,
)
}
return result
},
// get table el
getTableCellEl({ rowKey, colKey }) {
let result = null
const { tableEl } = this
if (tableEl) {
result = tableEl.querySelector(
`tbody.fan-table-body tr[row-key="${rowKey}"] td[col-key="${colKey}"]`,
)
}
return result
},
// set current cell el
setCurrentCellEl() {
const { cellSelectionData } = this
const { rowKey, colKey } = cellSelectionData.currentCell
if (!isEmptyValue(rowKey) && !isEmptyValue(colKey)) {
const cellEl = this.getTableCellEl({
rowKey,
colKey,
})
if (cellEl) {
this.currentCellEl = cellEl
}
}
},
// set normal end cell el
setNormalEndCellEl() {
const { cellSelectionData } = this
const { rowKey, colKey } = cellSelectionData.normalEndCell
if (!isEmptyValue(rowKey) && !isEmptyValue(colKey)) {
const cellEl = this.getTableCellEl({
rowKey,
colKey,
})
if (cellEl) {
this.normalEndCellEl = cellEl
}
}
},
// set auto fill cell el
setAutofillEndCellEl() {
const { cellSelectionData, tableEl } = this
const { rowKey, colKey } = cellSelectionData.autoFillEndCell
if (tableEl) {
const autoFillEndCellEl = tableEl.querySelector(
`tbody.fan-table-body tr[row-key="${rowKey}"] td[col-key="${colKey}"]`,
)
if (autoFillEndCellEl) {
this.autoFillEndCellEl = autoFillEndCellEl
}
}
},
// clear auto fill end cell rect
clearAutofillEndCellRect() {
this.autoFillEndCellEl = null
this.cellSelectionRect.autoFillEndCellRect = {
left: 0,
top: 0,
width: 0,
height: 0,
}
},
// clear current cell rect
[INSTANCE_METHODS.CLEAR_CURRENT_CELL_RECT]() {
this.currentCellEl = null
this.cellSelectionRect.currentCellRect = {
left: 0,
top: 0,
width: 0,
height: 0,
}
},
// clear normal end cell rect
[INSTANCE_METHODS.CLEAR_NORMAL_END_CELL_RECT]() {
this.normalEndCellEl = null
this.cellSelectionRect.normalEndCellRect = {
left: 0,
top: 0,
width: 0,
height: 0,
}
},
},
render() {
if (!this.selectionBordersVisibility) {
return null
}
// fixed left
const fixedLeftSelectionCurrent = this.getSelectionCurrent({
fixedType: COLUMN_FIXED_TYPE.LEFT,
})
const fixedLeftSelectionArea = this.getSelectionAreas({
fixedType: COLUMN_FIXED_TYPE.LEFT,
})
const fixedLeftAutoFillArea =
fixedLeftSelectionCurrent.autoFillArea ||
fixedLeftSelectionArea.autoFillArea
// middle
const middleSelectionCurrent = this.getSelectionCurrent({
fixedType: '',
})
const middleSelectionArea = this.getSelectionAreas({
fixedType: '',
})
const middleAutoFillArea =
middleSelectionCurrent.autoFillArea ||
middleSelectionArea.autoFillArea
// fixed right
const fixedRightSelectionCurrent = this.getSelectionCurrent({
fixedType: COLUMN_FIXED_TYPE.RIGHT,
})
const fixedRightSelectionArea = this.getSelectionAreas({
fixedType: COLUMN_FIXED_TYPE.RIGHT,
})
const fixedRightAutoFillArea =
fixedRightSelectionCurrent.autoFillArea ||
fixedRightSelectionArea.autoFillArea
return (
<div
class={clsName('selection-wrapper')}
style={{ visibility: this.isCellEditing ? 'hidden' : '' }}
>
<div class={clsName('selection-fixed-left')}>
{/* current */}
{fixedLeftSelectionCurrent.selectionCurrent}
{/* area */}
{fixedLeftSelectionArea.normalArea}
{/* auto fill */}
{fixedLeftAutoFillArea}
{/* area layer */}
{fixedLeftSelectionArea.normalAreaLayer}
</div>
<div class={clsName('selection-middle')}>
{/* current */}
{middleSelectionCurrent.selectionCurrent}
{/* area */}
{middleSelectionArea.normalArea}
{/* auto fill */}
{middleAutoFillArea}
{/* area layer */}
{middleSelectionArea.normalAreaLayer}
</div>
<div class={clsName('selection-fixed-right')}>
{/* current */}
{fixedRightSelectionCurrent.selectionCurrent}
{/* area */}
{fixedRightSelectionArea.normalArea}
{/* auto fill */}
{fixedRightAutoFillArea}
{/* area layer */}
{fixedRightSelectionArea.normalAreaLayer}
</div>
</div>
)
},
}