UNPKG

e-virt-table

Version:

A powerful data table based on canvas. You can use it as data grid、Microsoft Excel or Google sheets. It supports virtual scroll、cell edit etc.

962 lines 36.7 kB
import { throttle, decodeSpreadsheetStr, encodeToSpreadsheetStr } from './util'; export default class Selector { constructor(ctx) { Object.defineProperty(this, "isCut", { enumerable: true, configurable: true, writable: true, value: false }); Object.defineProperty(this, "isMultipleRow", { enumerable: true, configurable: true, writable: true, value: false }); Object.defineProperty(this, "mousedownHeader", { enumerable: true, configurable: true, writable: true, value: false }); Object.defineProperty(this, "ctx", { enumerable: true, configurable: true, writable: true, value: void 0 }); Object.defineProperty(this, "adjustPositionX", { enumerable: true, configurable: true, writable: true, value: '' }); Object.defineProperty(this, "adjustPositionY", { enumerable: true, configurable: true, writable: true, value: '' }); Object.defineProperty(this, "timerX", { enumerable: true, configurable: true, writable: true, value: 0 }); // 水平滚动定时器 Object.defineProperty(this, "timerY", { enumerable: true, configurable: true, writable: true, value: 0 }); // 垂直滚动定时器 this.ctx = ctx; this.init(); } init() { // 鼠标移动fixed边界时,调整滚动条位置 this.ctx.on('mousemove', throttle((e) => { const { offsetY, offsetX } = this.ctx.getOffset(e); const isInsideBody = this.ctx.isTarget() && offsetX > 0 && offsetX < this.ctx.body.visibleWidth && offsetY > this.ctx.header.visibleHeight && offsetY < this.ctx.header.visibleHeight + this.ctx.body.visibleHeight; if (this.ctx.selectorMove || this.ctx.autofillMove) { // 如果是body外部就调整位置 if (!isInsideBody && !this.mousedownHeader) { this.startAdjustPosition(e); } else { this.stopAdjustPosition(); } } }, 100)); this.ctx.on('cellHoverChange', (cell) => { // 如果是自动填充移动就不处理 if (this.ctx.autofillMove) { return; } if (cell.operation) { this.selectRows(cell, false); // 如果是自动填充就不处理 return; } // 多选行 if (this.isMultipleRow) { return; } this.mouseenter(); }); this.ctx.on('cellMousedown', (cell, e) => { if (!this.ctx.isTarget()) { return; } // 如果是选中就不处理,比如chexkbox if (this.ctx.stageElement.style.cursor === 'pointer') { return; } if (this.ctx.isPointer) { return; } // 如果是填充返回 if (this.ctx.stageElement.style.cursor === 'crosshair') { return; } if (cell.operation) { this.isMultipleRow = true; this.selectRows(cell); return; } e.preventDefault(); this.isMultipleRow = false; this.click(e.shiftKey); this.ctx.emit('selectorClick', cell); }); this.ctx.on('mouseup', () => { this.mousedownHeader = false; // mousedown销毁dom会导致click事件清除 // 加个setTimeout小延迟一下,使得editor cellClick 判断adjustPositioning正常 const timer = setTimeout(() => { this.ctx.adjustPositioning = false; clearTimeout(timer); }, 0); }); this.ctx.on('cellHeaderHoverChange', (cell) => { if (this.ctx.mousedown) { this.selectCols(cell); } }); this.ctx.on('cellHeaderMousedown', (cell, e) => { // 如果是选中就不处理,比如chexkbox if (this.ctx.stageElement.style.cursor === 'pointer') { return; } if (this.ctx.isPointer) { return; } e.preventDefault(); this.mousedownHeader = true; this.selectCols(cell); }); this.ctx.on('keydown', (e) => { if (this.ctx.editing) { return; } // CTRL+C/Command+C if ((e.ctrlKey && e.code === 'KeyV') || (e.metaKey && e.code === 'KeyV')) { e.preventDefault(); this.paste(); return; } if ((e.ctrlKey && e.code === 'KeyC') || (e.metaKey && e.code === 'KeyC')) { e.preventDefault(); this.copy(); this.isCut = false; return; } // CTRL+X/Command+X if ((e.ctrlKey && e.code === 'KeyX') || (e.metaKey && e.code === 'KeyX')) { e.preventDefault(); this.isCut = true; this.copy(); return; } // CTRL+A/Command+A if ((e.ctrlKey && e.code === 'KeyA') || (e.metaKey && e.code === 'KeyA')) { e.preventDefault(); this.selectAll(); } if (e.code === 'ArrowLeft') { e.preventDefault(); this.moveFocus('LEFT'); return; } if (e.code === 'ArrowUp') { e.preventDefault(); this.moveFocus('TOP'); return; } if (e.code === 'ArrowRight' || e.code === 'Tab') { e.preventDefault(); this.moveFocus('RIGHT'); return; } if (e.code === 'ArrowDown') { e.preventDefault(); this.moveFocus('BOTTOM'); return; } if (e.code === 'Delete' || e.code === 'Backspace') { e.preventDefault(); const { xArr, yArr } = this.ctx.selector; this.clearSelectedData(xArr, yArr); return; } }); this.ctx.on('contextMenuClearSelected', () => { const { xArr, yArr } = this.ctx.selector; this.clearSelectedData(xArr, yArr); }); this.ctx.on('contextMenuCopy', () => { this.copy(); }); this.ctx.on('contextMenuPaste', () => { this.paste(); }); this.ctx.on('contextMenuCut', () => { this.isCut = true; this.copy(); }); //解耦:外部调用选择单元格 this.ctx.on('setSelectorCell', (cell) => { this.ctx.setFocusCell(cell); this.click(); }); this.ctx.on('mouseup', () => { this.ctx.selectorMove = false; this.stopAdjustPosition(); }); } setSelector(xArr, yArr) { const { ENABLE_SELECTOR_SPAN_COL, ENABLE_SELECTOR_SPAN_ROW } = this.ctx.config; let _xArr = xArr; let _yArr = yArr; if (!ENABLE_SELECTOR_SPAN_ROW) { const [rowstart] = _yArr; _yArr = [rowstart, rowstart]; } if (!ENABLE_SELECTOR_SPAN_COL) { const [colstart] = _xArr; _xArr = [colstart, colstart]; } // 减少渲染 if (JSON.stringify(this.ctx.selector.xArr) !== JSON.stringify(_xArr) || JSON.stringify(this.ctx.selector.yArr) !== JSON.stringify(_yArr)) { if (this.ctx.mousedown) { this.ctx.selectorMove = true; } this.ctx.selector.enable = true; const { SELECTOR_AREA_MIN_X, SELECTOR_AREA_MAX_X, SELECTOR_AREA_MIN_Y, SELECTOR_AREA_MAX_Y, SELECTOR_AREA_MAX_X_OFFSET, SELECTOR_AREA_MAX_Y_OFFSET, } = this.ctx.config; const areaMinX = SELECTOR_AREA_MIN_X; const areaMaxX = SELECTOR_AREA_MAX_X || this.ctx.maxColIndex - SELECTOR_AREA_MAX_X_OFFSET; const areaMinY = SELECTOR_AREA_MIN_Y; const areaMaxY = SELECTOR_AREA_MAX_Y || this.ctx.maxRowIndex - SELECTOR_AREA_MAX_Y_OFFSET; let [minX, maxX] = _xArr; let [minY, maxY] = _yArr; if (minX < areaMinX) { return; } if (maxX > areaMaxX) { return; } if (minY < areaMinY) { return; } if (maxY > areaMaxY) { return; } // 聚焦,解决iframe键盘事件不触发 this.ctx.stageElement.focus(); // 启用合并单元格关联 if (this.ctx.config.ENABLE_MERGE_CELL_LINK) { const adjustMerge = this.adjustMergeCells(_xArr, _yArr); // 合并单元格时,调整选择器的位置 minY = adjustMerge.yArr[0]; maxY = adjustMerge.yArr[1]; minX = adjustMerge.xArr[0]; maxX = adjustMerge.xArr[1]; // 只有一个合并单元格时 this.ctx.onlyMergeCell = adjustMerge.onlyMergeCell; } if (minX === maxX && minY === maxY) { this.ctx.selectOnlyOne = true; } else { this.ctx.selectOnlyOne = false; } _xArr = [Math.max(areaMinX, minX), Math.min(areaMaxX, maxX)]; _yArr = [Math.max(areaMinY, minY), Math.min(areaMaxY, maxY)]; // 调整选择器的位置前回调 const { BEFORE_SET_SELECTOR_METHOD } = this.ctx.config; if (typeof BEFORE_SET_SELECTOR_METHOD === 'function') { const beforeSetSelectorMethod = BEFORE_SET_SELECTOR_METHOD; const res = beforeSetSelectorMethod({ focusCell: this.ctx.focusCell, xArr: _xArr, yArr: _yArr, }); if (!res) { return; } _xArr = res.xArr; _yArr = res.yArr; } this.ctx.selector.xArr = _xArr; this.ctx.selector.yArr = _yArr; this.ctx.emit('setSelector', this.ctx.selector); this.ctx.emit('drawView'); } } adjustMergeCells(xArr, yArr) { const [minY, maxY] = yArr; const [minX, maxX] = xArr; let topBottomCells = []; let leftRightCells = []; // 遍历选择中的单元格 for (let ri = 0; ri <= yArr[1] - yArr[0]; ri++) { for (let ci = 0; ci <= xArr[1] - xArr[0]; ci++) { const rowIndex = ri + yArr[0]; const colIndex = ci + xArr[0]; const cell = this.ctx.database.getVirtualBodyCell(rowIndex, colIndex); if (cell) { // 顶部和底部的单元格 if (rowIndex === minY || rowIndex === maxY) { topBottomCells.push(cell); } // 左右的单元格 if (colIndex === minX || colIndex === maxX) { leftRightCells.push(cell); } } } } const topBottomBoundary = topBottomCells.reduce((prev, cell) => { const { yArr } = cell.getSpanInfo(); const [topIndex, bottomIndex] = yArr; prev.minY = Math.min(prev.minY, topIndex); prev.maxY = Math.max(prev.maxY, bottomIndex); return prev; }, { minY, maxY, }); const leftRightBoundary = leftRightCells.reduce((prev, cell) => { const { xArr } = cell.getSpanInfo(); const [leftIndex, rightIndex] = xArr; prev.minX = Math.min(prev.minX, leftIndex); prev.maxX = Math.max(prev.maxX, rightIndex); return prev; }, { minX, maxX, }); const _xArr = [leftRightBoundary.minX, leftRightBoundary.maxX]; const _yArr = [topBottomBoundary.minY, topBottomBoundary.maxY]; let onlyMergeCell = false; // Check if the selected area is a single merged cell if (leftRightBoundary.minX !== leftRightBoundary.maxX || topBottomBoundary.minY !== topBottomBoundary.maxY) { const selectorStr = JSON.stringify(_xArr) + JSON.stringify(_yArr); const spanInfo = this.ctx.focusCell?.getSpanInfo(); const spanStr = spanInfo && JSON.stringify(spanInfo.xArr) + JSON.stringify(spanInfo.yArr); onlyMergeCell = spanStr === selectorStr; } return { xArr: _xArr, yArr: _yArr, onlyMergeCell, }; } selectCols(cell) { // 启用单选就不能批量选中 if (this.ctx.config.ENABLE_SELECTOR_SINGLE) { return; } if (!this.ctx.config.ENABLE_SELECTOR_ALL_ROWS) { return; } if (this.ctx.autofillMove) { return; } // 如果是拖拽改变列宽就不处理 if (this.ctx.columnResizing) { return; } // 编辑中 if (this.ctx.editing) { return; } // 是可操作列就全选 if (cell.operation) { this.selectAll(); return; } const { SELECTOR_AREA_MIN_Y, SELECTOR_AREA_MAX_Y, SELECTOR_AREA_MAX_Y_OFFSET } = this.ctx.config; const minY = SELECTOR_AREA_MIN_Y; const maxY = SELECTOR_AREA_MAX_Y || this.ctx.maxRowIndex - SELECTOR_AREA_MAX_Y_OFFSET; if (this.ctx.mousedown && this.ctx.focusCellHeader) { const { colIndex } = this.ctx.focusCellHeader; // this.ctx.clearSelector(); if (cell.colIndex >= colIndex) { const xArr = [colIndex, cell.colIndex + cell.colspan - 1]; const yArr = [minY, maxY]; this.setSelector(xArr, yArr); } else { const xArr = [cell.colIndex, colIndex]; const yArr = [minY, maxY]; this.setSelector(xArr, yArr); } } else { // this.setFocusCellHeader(cell); const xArr = [cell.colIndex, cell.colIndex + cell.colspan - 1]; const yArr = [minY, maxY]; this.setSelector(xArr, yArr); } } selectAll() { if (this.ctx.autofillMove) { return; } // 编辑中 if (this.ctx.editing) { return; } // 只有两个全选启用了才能全选 const { ENABLE_SELECTOR_ALL_ROWS, ENABLE_SELECTOR_ALL_COLS } = this.ctx.config; if (ENABLE_SELECTOR_ALL_ROWS && ENABLE_SELECTOR_ALL_COLS) { const { SELECTOR_AREA_MIN_X, SELECTOR_AREA_MAX_X, SELECTOR_AREA_MIN_Y, SELECTOR_AREA_MAX_Y, SELECTOR_AREA_MAX_X_OFFSET, SELECTOR_AREA_MAX_Y_OFFSET, } = this.ctx.config; const minX = SELECTOR_AREA_MIN_X; const maxX = SELECTOR_AREA_MAX_X || this.ctx.maxColIndex - SELECTOR_AREA_MAX_X_OFFSET; const minY = SELECTOR_AREA_MIN_Y; const maxY = SELECTOR_AREA_MAX_Y || this.ctx.maxRowIndex - SELECTOR_AREA_MAX_Y_OFFSET; const xArr = [minX, maxX]; const yArr = [minY, maxY]; this.setSelector(xArr, yArr); } } selectRows(cell, isSetFocus = true) { // 启用单选就不能批量选中 if (this.ctx.config.ENABLE_SELECTOR_SINGLE) { return; } if (!this.ctx.config.ENABLE_SELECTOR_ALL_COLS) { return; } if (this.ctx.autofillMove) { return; } // 编辑中 if (this.ctx.editing) { return; } const { SELECTOR_AREA_MIN_X, SELECTOR_AREA_MAX_X, SELECTOR_AREA_MAX_X_OFFSET } = this.ctx.config; const maxX = SELECTOR_AREA_MAX_X || this.ctx.maxColIndex - SELECTOR_AREA_MAX_X_OFFSET; const minX = SELECTOR_AREA_MIN_X; if (isSetFocus) { this.ctx.setFocusCell(cell); const xArr = [minX, maxX]; const yArr = [cell.rowIndex, cell.rowIndex]; this.setSelector(xArr, yArr); } if (this.ctx.focusCell && this.ctx.mousedown) { const { rowIndex } = this.ctx.focusCell; if (cell.rowIndex >= rowIndex) { const xArr = [minX, maxX]; const yArr = [rowIndex, cell.rowIndex]; this.setSelector(xArr, yArr); } else { const xArr = [minX, maxX]; const yArr = [cell.rowIndex, rowIndex]; this.setSelector(xArr, yArr); } } } mouseenter() { if (this.ctx.config.ENABLE_SELECTOR_SINGLE) { return; } // 编辑中 if (this.ctx.editing) { return; } const { mousedown, focusCell, hoverCell } = this.ctx; if (mousedown && focusCell && hoverCell) { const { rowIndex, colIndex } = focusCell; const minX = Math.min(hoverCell.colIndex, colIndex); const maxX = Math.max(hoverCell.colIndex, colIndex); const minY = Math.min(hoverCell.rowIndex, rowIndex); const maxY = Math.max(hoverCell.rowIndex, rowIndex); const xArr = [minX, maxX]; const yArr = [minY, maxY]; this.setSelector(xArr, yArr); } } click(shiftKey = false) { const { focusCell, clickCell } = this.ctx; if (!focusCell) { return; } // 超过范围值就不处理 if (!this.isInSettingRange(focusCell.rowIndex, focusCell.colIndex)) { return; } this.ctx.selector.enable = true; if (clickCell && shiftKey) { // shiftKey快捷选中 const { colIndex, rowIndex } = clickCell; const { colIndex: oldX, rowIndex: oldY } = focusCell; const minX = Math.min(oldX, colIndex); const maxX = Math.max(oldX, colIndex); const minY = Math.min(oldY, rowIndex); const maxY = Math.max(oldY, rowIndex); const xArr = [minX, maxX]; const yArr = [minY, maxY]; this.setSelector(xArr, yArr); } else { this.ctx.emit('cellSelectedClick', focusCell); const xArr = [focusCell.colIndex, focusCell.colIndex]; const yArr = [focusCell.rowIndex, focusCell.rowIndex]; this.setSelector(xArr, yArr); this.adjustBoundaryPosition(); } } clearCopyLine() { this.ctx.selector.xArrCopy = [-1, -1]; this.ctx.selector.yArrCopy = [-1, -1]; } /** * 获取选中单元格 * @param rowIndex * @param colIndex * @returns */ getCell(rowIndex, colIndex) { // 设置选中FocusCell const row = this.ctx.body.renderRows.find((row) => row.rowIndex === rowIndex); const cell = row?.cells.find((cell) => cell.colIndex === colIndex); return cell; } /** * 复制 * @returns */ copy() { if (!this.ctx.config.ENABLE_COPY) { return; } let { value, xArr, yArr } = this.ctx.getSelectedData(); if (this.ctx.config.ENABLE_MERGE_CELL_LINK && this.ctx.database.hasMergeCell(xArr, yArr)) { if (this.ctx.onlyMergeCell && this.ctx.focusCell) { const cell = this.ctx.focusCell; value = [[cell.getValue()]]; xArr = [cell.colIndex, cell.colIndex]; yArr = [cell.rowIndex, cell.rowIndex]; } else { const err = { code: 'ERR_MERGED_CELLS_COPY', message: 'Merged cells cannot span copy data', }; if (this.ctx.hasEvent('error')) { this.ctx.emit('error', err); } else { alert(err.message); } return; } } // 复制前回调 const { BEFORE_COPY_METHOD } = this.ctx.config; if (typeof BEFORE_COPY_METHOD === 'function') { const beforeCopyMethod = BEFORE_COPY_METHOD; const res = beforeCopyMethod({ focusCell: this.ctx.focusCell, data: value, xArr, yArr, }); if (!res) { return; } value = res.data; } const text = encodeToSpreadsheetStr(value); if (navigator.clipboard) { navigator.clipboard .writeText(text) .then(() => { // 处理复制线 this.ctx.selector.xArrCopy = this.ctx.selector.xArr.slice(); this.ctx.selector.yArrCopy = this.ctx.selector.yArr.slice(); this.ctx.emit('copyChange', { xArr: this.ctx.selector.xArrCopy, yArr: this.ctx.selector.yArrCopy, data: value, }); this.ctx.emit('draw'); }) .catch((error) => console.error('Copy Failure:', error)); } else { console.error('current browser does not support the Clipboard API'); } } clearSelectedData(xArr, yArr, ignoreSet = false) { let changeList = []; const rowKeyList = new Set(); for (let ri = 0; ri <= yArr[1] - yArr[0]; ri++) { for (let ci = 0; ci <= xArr[1] - xArr[0]; ci++) { const _rowIndex = ri + yArr[0]; const _colIndex = ci + xArr[0]; const itemValue = this.ctx.database.getItemValueForRowIndexAndColIndex(_rowIndex, _colIndex); if (itemValue) { const { rowKey, key } = itemValue; // 只读就跳过 if (!this.ctx.database.getReadonly(rowKey, key)) { rowKeyList.add(rowKey); changeList.push({ rowKey, key, value: null, row: {}, //内部有设置 }); } } } } // 没有变化就返回 if (!changeList.length) { return []; } // 忽略设置,只返回数据,用于cut if (ignoreSet) { return changeList; } // 批量设置数据,并记录历史 this.ctx.database.batchSetItemValue(changeList, true); let rows = []; rowKeyList.forEach((rowKey) => { rows.push(this.ctx.database.getRowDataItemForRowKey(rowKey)); }); this.ctx.emit('clearSelectedDataChange', changeList, rows); return changeList; } paste() { if (!navigator.clipboard) { console.error('current browser does not support the Clipboard API'); return; } const { ENABLE_PASTER } = this.ctx.config; if (this.ctx.selector.enable && ENABLE_PASTER) { const rowIndex = this.ctx.selector.yArr[0]; const colIndex = this.ctx.selector.xArr[0]; const rowKeyList = new Set(); navigator.clipboard .readText() .then(async (val) => { let textArr = decodeSpreadsheetStr(val); const _xArr = [colIndex, colIndex + textArr[0].length - 1]; const _yArr = [rowIndex, rowIndex + textArr.length - 1]; // textArr只有一个 const isOneData = textArr.length === 1 && textArr[0].length === 1; // 启用合并时禁用粘贴填充 if (this.ctx.config.ENABLE_MERGE_CELL_LINK && this.ctx.database.hasMergeCell(_xArr, _yArr) && !isOneData) { const err = { code: 'ERR_MERGED_CELLS_PASTE', message: 'Merged cells cannot span paste data', }; if (this.ctx.hasEvent('error')) { this.ctx.emit('error', err); } else { alert(err.message); } return; } let changeList = []; for (let ri = 0; ri <= textArr.length - 1; ri++) { const len = textArr[ri].length; for (let ci = 0; ci <= len - 1; ci++) { const _rowIndex = ri + rowIndex; const _colIndex = ci + colIndex; const value = textArr[ri][ci]; const itemValue = this.ctx.database.getItemValueForRowIndexAndColIndex(_rowIndex, _colIndex); if (itemValue) { const { rowKey, key } = itemValue; // 只读就跳过 if (!this.ctx.database.getReadonly(rowKey, key)) { rowKeyList.add(rowKey); changeList.push({ rowKey, key, value, row: {}, //内部有设置 }); } } } } // 剪切时清除选中数据 if (this.isCut) { const cutList = this.clearSelectedData(this.ctx.selector.xArrCopy, this.ctx.selector.yArrCopy, true); const changeListRowkeys = changeList.map((item) => `${item.rowKey}-${item.key}`); // 剔除剪切的数据 cutList.forEach((item) => { if (!changeListRowkeys.includes(`${item.rowKey}-${item.key}`)) { // 剪切的数据放在最前面 changeList.unshift(item); } }); this.isCut = false; } // 没有变化就返回 if (!changeList.length) { return; } // 剪贴板内容改变前回调 const { BEFORE_PASTE_DATA_METHOD } = this.ctx.config; if (typeof BEFORE_PASTE_DATA_METHOD === 'function') { const beforePasteDataMethod = BEFORE_PASTE_DATA_METHOD; const _changeList = changeList.map((item) => ({ rowKey: item.rowKey, key: item.key, value: item.value, oldValue: this.ctx.database.getItemValue(item.rowKey, item.key), row: this.ctx.database.getRowDataItemForRowKey(item.rowKey), })); changeList = await beforePasteDataMethod(_changeList, _xArr, _yArr); if (changeList && !changeList.length) { return; } } // 清除复制线 this.clearCopyLine(); // 批量设置数据,并记录历史 this.ctx.batchSetItemValueByEditor(changeList, true); let rows = []; rowKeyList.forEach((rowKey) => { rows.push(this.ctx.database.getRowDataItemForRowKey(rowKey)); }); this.ctx.emit('pasteChange', changeList, rows); }) .catch((error) => { console.error('Failed to get the clipboard content:', error); }); } } /**键盘上下左右切换 * @param dir */ moveFocus(dir) { // 编辑状态不处理 if (this.ctx.editing) { return; } const { focusCell } = this.ctx; if (!focusCell) { return; } let { colIndex = 0, rowIndex = 0 } = focusCell; const minX = 0; const minY = 0; const maxX = this.ctx.maxColIndex; const maxY = this.ctx.maxRowIndex; switch (dir) { case 'LEFT': if (colIndex > minX) { colIndex--; } break; case 'TOP': if (rowIndex > minY) { rowIndex--; } break; case 'RIGHT': if (colIndex < maxX) { colIndex++; } break; case 'BOTTOM': if (rowIndex < maxY) { rowIndex++; } break; default: } const xArr = [colIndex, colIndex]; const yArr = [rowIndex, rowIndex]; const cell = this.getCell(rowIndex, colIndex); if (!cell) { return; } // 操作列不处理 if (cell.operation) { return; } // 超过范围值就不处理,防止跳动 if (!this.isInSettingRange(cell.rowIndex, cell.colIndex)) { return; } this.ctx.setFocusCell(cell); this.setSelector(xArr, yArr); this.adjustBoundaryPosition(); this.ctx.emit('draw'); } stopAdjustPosition() { this.adjustPositionX = ''; this.adjustPositionY = ''; if (this.timerX) { clearInterval(this.timerX); this.timerX = 0; } if (this.timerY) { clearInterval(this.timerY); this.timerY = 0; } } // 判断是否在设置范围内 isInSettingRange(rowIndex, colIndex) { const { SELECTOR_AREA_MIN_X, SELECTOR_AREA_MAX_X, SELECTOR_AREA_MIN_Y, SELECTOR_AREA_MAX_Y, SELECTOR_AREA_MAX_X_OFFSET, SELECTOR_AREA_MAX_Y_OFFSET, } = this.ctx.config; const areaMinX = SELECTOR_AREA_MIN_X; const areaMaxX = SELECTOR_AREA_MAX_X || this.ctx.maxColIndex - SELECTOR_AREA_MAX_X_OFFSET; const areaMinY = SELECTOR_AREA_MIN_Y; const areaMaxY = SELECTOR_AREA_MAX_Y || this.ctx.maxRowIndex - SELECTOR_AREA_MAX_Y_OFFSET; if (colIndex < areaMinX) { return false; } if (colIndex > areaMaxX) { return false; } if (rowIndex < areaMinY) { return false; } if (rowIndex > areaMaxY) { return false; } return true; } /** * 调整滚动条位置,让到达边界时自动滚动 */ startAdjustPosition(e) { const { offsetX, offsetY } = this.ctx.getOffset(e); let positionX = ''; let positionY = ''; if (offsetX < 0) { positionX = 'left'; } else if (offsetX > this.ctx.body.visibleWidth) { positionX = 'right'; } if (offsetY < this.ctx.header.visibleHeight) { positionY = 'top'; } else if (offsetY > this.ctx.header.visibleHeight + this.ctx.body.visibleHeight) { positionY = 'bottom'; } if (positionX && this.adjustPositionX !== positionX) { this.adjustPositionX = positionX; const position = positionX === 'left' ? -1 : 1; let scrollSpeedX = 10 * position; // 滚动速度 if (this.timerX) { clearInterval(this.timerX); this.timerX = 0; } this.timerX = setInterval(() => { // 增加滚动速度 scrollSpeedX *= 1.5; // 加速因子 const { scrollX } = this.ctx; const num = scrollX + scrollSpeedX; if (num < 0 || num > this.ctx.body.width) { clearInterval(this.timerX); this.timerX = 0; } this.ctx.setScrollX(num); }, 100); // 每100毫秒执行一次 } if (positionY && this.adjustPositionY !== positionY) { this.adjustPositionY = positionY; const position = positionY === 'top' ? -1 : 1; let scrollSpeedY = 10 * position; // 滚动速度 if (this.timerY) { clearInterval(this.timerY); this.timerY = 0; } this.timerY = setInterval(() => { // 增加滚动速度 scrollSpeedY *= 1.5; // 加速因子 const { scrollY } = this.ctx; const num = scrollY + scrollSpeedY; if (num < 0 || num > this.ctx.body.height) { clearInterval(this.timerY); this.timerY = 0; } this.ctx.setScrollY(num); }, 100); // 每100毫秒执行一次 } } /** * 调整滚动条位置,让焦点单元格始终出现在可视区域内 */ adjustBoundaryPosition() { const { stageHeight, stageWidth, focusCell, fixedRightWidth, fixedLeftWidth, header, footer, body, scrollX, scrollY, config: { SCROLLER_TRACK_SIZE, FOOTER_FIXED, FOOTER_POSITION, ENABLE_MERGE_CELL_LINK }, } = this.ctx; if (!focusCell) { return; } // 处理合并的单元格的移动 if (ENABLE_MERGE_CELL_LINK && this.ctx.onlyMergeCell) { focusCell.updateSpanInfo(); } const { drawX, drawY, width, height, fixed } = focusCell; // 加1补选中框的边框,且可以移动滚动,以为getCell是获取渲染的cell const diffLeft = fixedLeftWidth - drawX + 1; const diffRight = focusCell.drawX + width - (stageWidth - fixedRightWidth) + 1; let diffTop = header.height - drawY; // 格子大于可视高度就取可视高度,防止上下跳动 let cellheight = height; if (cellheight > body.visibleHeight) { cellheight = body.visibleHeight; } let footerHeight = 0; if (FOOTER_FIXED) { if (FOOTER_POSITION === 'top') { diffTop = header.height + footer.height - drawY; } else { footerHeight = footer.visibleHeight; } } const diffBottom = drawY + cellheight - (stageHeight - footerHeight - SCROLLER_TRACK_SIZE); let _scrollX = scrollX; let _scrollY = scrollY; // fixed禁用左右横向移动 if (diffRight > 0 && !fixed) { _scrollX = Math.floor(scrollX + diffRight); } else if (diffLeft > 0 && !fixed) { _scrollX = Math.floor(scrollX - diffLeft); } if (diffTop > 0) { _scrollY = Math.floor(scrollY - diffTop); } else if (diffBottom > 0) { _scrollY = Math.floor(scrollY + diffBottom); } // >1是因为上面为了可移动加1,所以这里要大于2(保险一点) if (Math.abs(scrollX - _scrollX) > 2 || Math.abs(scrollY - _scrollY) > 2) { this.ctx.adjustPositioning = true; this.ctx.setScroll(_scrollX, _scrollY); // fix:处理移动后编辑器,需要再点击一次,编辑器那边有监听 this.ctx.emit('adjustBoundaryPosition', focusCell); } } destroy() { if (this.timerX) { clearTimeout(this.timerX); this.timerX = 0; } if (this.timerY) { clearTimeout(this.timerY); this.timerY = 0; } } } //# sourceMappingURL=Selector.js.map