From 18e929a27549db8ce59923564cbef703f30d4550 Mon Sep 17 00:00:00 2001 From: sealday Date: Thu, 21 Mar 2024 16:08:32 +0800 Subject: [PATCH] fix: excel scroll bugs --- .../component/{element.js => element.ts} | 76 +++-------- .../component/{scrollbar.js => scrollbar.ts} | 28 ++-- .../components/x-sheet/component/sheet.js | 128 +++++++----------- .../src/client/components/x-sheet/demo.ts | 2 +- 4 files changed, 83 insertions(+), 151 deletions(-) rename packages/plugins/@hera/plugin-core/src/client/components/x-sheet/component/{element.js => element.ts} (79%) rename packages/plugins/@hera/plugin-core/src/client/components/x-sheet/component/{scrollbar.js => scrollbar.ts} (53%) diff --git a/packages/plugins/@hera/plugin-core/src/client/components/x-sheet/component/element.js b/packages/plugins/@hera/plugin-core/src/client/components/x-sheet/component/element.ts similarity index 79% rename from packages/plugins/@hera/plugin-core/src/client/components/x-sheet/component/element.js rename to packages/plugins/@hera/plugin-core/src/client/components/x-sheet/component/element.ts index aa89752b6..cd3be5855 100644 --- a/packages/plugins/@hera/plugin-core/src/client/components/x-sheet/component/element.js +++ b/packages/plugins/@hera/plugin-core/src/client/components/x-sheet/component/element.ts @@ -1,31 +1,29 @@ -/* global document */ -/* global window */ class Element { - constructor(tag, className = '') { + private el: HTMLElement; + private _data: Record; + constructor(tag: HTMLElement | string, className = '') { if (typeof tag === 'string') { this.el = document.createElement(tag); this.el.className = className; } else { this.el = tag; } - this.data = {}; + this._data = {}; } data(key, value) { if (value !== undefined) { - this.data[key] = value; + this._data[key] = value; return this; } - return this.data[key]; + return this._data[key]; } on(eventNames, handler) { const [fen, ...oen] = eventNames.split('.'); - let eventName = fen; - if (eventName === 'mousewheel' && /Firefox/i.test(window.navigator.userAgent)) { - eventName = 'DOMMouseScroll'; - } + const eventName = fen; this.el.addEventListener(eventName, (evt) => { + console.log('excel debug', eventName, eventNames, oen, evt); handler(evt); for (let i = 0; i < oen.length; i += 1) { const k = oen[i]; @@ -37,6 +35,7 @@ class Element { } if (k === 'stop') { evt.stopPropagation(); + evt.preventDefault(); } } }); @@ -50,9 +49,7 @@ class Element { }); return this; } - const { - offsetTop, offsetLeft, offsetHeight, offsetWidth, - } = this.el; + const { offsetTop, offsetLeft, offsetHeight, offsetWidth } = this.el; return { top: offsetTop, left: offsetLeft, @@ -61,7 +58,7 @@ class Element { }; } - scroll(v) { + scroll(v?) { const { el } = this; if (v !== undefined) { if (v.left !== undefined) { @@ -79,14 +76,14 @@ class Element { } parent() { - return new Element(this.el.parentNode); + return new Element(this.el.parentElement); } children(...eles) { if (arguments.length === 0) { return this.el.childNodes; } - eles.forEach(ele => this.child(ele)); + eles.forEach((ele) => this.child(ele)); return this; } @@ -94,38 +91,6 @@ class Element { this.el.removeChild(el); } - /* - first() { - return this.el.firstChild; - } - - last() { - return this.el.lastChild; - } - - remove(ele) { - return this.el.removeChild(ele); - } - - prepend(ele) { - const { el } = this; - if (el.children.length > 0) { - el.insertBefore(ele, el.firstChild); - } else { - el.appendChild(ele); - } - return this; - } - - prev() { - return this.el.previousSibling; - } - - next() { - return this.el.nextSibling; - } - */ - child(arg) { let ele = arg; if (typeof arg === 'string') { @@ -219,6 +184,9 @@ class Element { } val(v) { + if (!('value' in this.el)) { + return; + } if (v !== undefined) { this.el.value = v; return this; @@ -231,14 +199,17 @@ class Element { } cssRemoveKeys(...keys) { - keys.forEach(k => this.el.style.removeProperty(k)); + keys.forEach((k) => this.el.style.removeProperty(k)); return this; } + css(name: string): string; + css(properties: any): Element; + css(name: string, value: any): Element; // css( propertyName ) // css( propertyName, value ) // css( properties ) - css(name, value) { + css(name, value?): Element | string { if (value === undefined && typeof name !== 'string') { Object.keys(name).forEach((k) => { this.el.style[k] = name[k]; @@ -269,7 +240,4 @@ class Element { const h = (tag, className = '') => new Element(tag, className); -export { - Element, - h, -}; +export { Element, h }; diff --git a/packages/plugins/@hera/plugin-core/src/client/components/x-sheet/component/scrollbar.js b/packages/plugins/@hera/plugin-core/src/client/components/x-sheet/component/scrollbar.ts similarity index 53% rename from packages/plugins/@hera/plugin-core/src/client/components/x-sheet/component/scrollbar.js rename to packages/plugins/@hera/plugin-core/src/client/components/x-sheet/component/scrollbar.ts index 99b045c0f..8d479db69 100644 --- a/packages/plugins/@hera/plugin-core/src/client/components/x-sheet/component/scrollbar.js +++ b/packages/plugins/@hera/plugin-core/src/client/components/x-sheet/component/scrollbar.ts @@ -1,23 +1,27 @@ -import { h } from './element'; +import { h, Element } from './element'; import { cssPrefix } from '../config'; export default class Scrollbar { - constructor(vertical) { - this.vertical = vertical; - this.moveFn = null; + private el: Element; + private contentEl: Element; + private _moveFn: (type: string, handler: (e: Event) => void) => void; + constructor(private vertical: 'vertical' | 'horizontal') { + this._moveFn = null; this.el = h('div', `${cssPrefix}-scrollbar ${vertical ? 'vertical' : 'horizontal'}`) - .child(this.contentEl = h('div', '')) + .child((this.contentEl = h('div', ''))) .on('mousemove.stop', () => {}) .on('scroll.stop', (evt) => { const { scrollTop, scrollLeft } = evt.target; - // console.log('scrollTop:', scrollTop); - if (this.moveFn) { - this.moveFn(this.vertical ? scrollTop : scrollLeft, evt); + if (this._moveFn) { + this._moveFn(this.vertical ? scrollTop : scrollLeft, evt); } - // console.log('evt:::', evt); }); } + set moveFn(fn: (type: string, handler: (e: Event) => void) => void) { + this._moveFn = fn; + } + move(v) { this.el.scroll(v); return this; @@ -29,14 +33,10 @@ export default class Scrollbar { set(distance, contentDistance) { const d = distance - 1; - // console.log('distance:', distance, ', contentDistance:', contentDistance); if (contentDistance > d) { const cssKey = this.vertical ? 'height' : 'width'; - // console.log('d:', d); this.el.css(cssKey, `${d - 15}px`).show(); - this.contentEl - .css(this.vertical ? 'width' : 'height', '1px') - .css(cssKey, `${contentDistance}px`); + this.contentEl.css(this.vertical ? 'width' : 'height', '1px').css(cssKey, `${contentDistance}px`); } else { this.el.hide(); } diff --git a/packages/plugins/@hera/plugin-core/src/client/components/x-sheet/component/sheet.js b/packages/plugins/@hera/plugin-core/src/client/components/x-sheet/component/sheet.js index 0c40164fb..20625210b 100644 --- a/packages/plugins/@hera/plugin-core/src/client/components/x-sheet/component/sheet.js +++ b/packages/plugins/@hera/plugin-core/src/client/components/x-sheet/component/sheet.js @@ -1,11 +1,6 @@ /* global window */ import { h } from './element'; -import { - bind, - mouseMoveUp, - bindTouch, - createEventEmitter, -} from './event'; +import { bind, mouseMoveUp, bindTouch, createEventEmitter } from './event'; import Resizer from './resizer'; import Scrollbar from './scrollbar'; import Selector from './selector'; @@ -40,12 +35,8 @@ function throttle(func, wait) { } function scrollbarMove() { - const { - data, verticalScrollbar, horizontalScrollbar, - } = this; - const { - l, t, left, top, width, height, - } = data.getSelectedRect(); + const { data, verticalScrollbar, horizontalScrollbar } = this; + const { l, t, left, top, width, height } = data.getSelectedRect(); const tableOffset = this.getTableOffset(); // console.log(',l:', l, ', left:', left, ', tOffset.left:', tableOffset.width); if (Math.abs(left) + width > tableOffset.width) { @@ -69,10 +60,7 @@ function scrollbarMove() { function selectorSet(multiple, ri, ci, indexesUpdated = true, moving = false) { if (ri === -1 && ci === -1) return; - const { - table, selector, toolbar, data, - contextMenu, - } = this; + const { table, selector, toolbar, data, contextMenu } = this; const cell = data.getCell(ri, ci); if (multiple) { selector.setEnd(ri, ci, moving); @@ -82,7 +70,7 @@ function selectorSet(multiple, ri, ci, indexesUpdated = true, moving = false) { selector.set(ri, ci, indexesUpdated); this.trigger('cell-selected', cell, ri, ci); } - contextMenu.setMode((ri === -1 || ci === -1) ? 'row-col' : 'range'); + contextMenu.setMode(ri === -1 || ci === -1 ? 'row-col' : 'range'); toolbar.reset(); table.render(); } @@ -90,9 +78,7 @@ function selectorSet(multiple, ri, ci, indexesUpdated = true, moving = false) { // multiple: boolean // direction: left | right | up | down | row-first | row-last | col-first | col-last function selectorMove(multiple, direction) { - const { - selector, data, - } = this; + const { selector, data } = this; const { rows, cols } = data; let [ri, ci] = selector.indexes; const { eri, eci } = selector.range; @@ -132,9 +118,7 @@ function overlayerMousemove(evt) { if (evt.buttons !== 0) return; if (evt.target.className === `${cssPrefix}-resizer-hover`) return; const { offsetX, offsetY } = evt; - const { - rowResizer, colResizer, tableEl, data, - } = this; + const { rowResizer, colResizer, tableEl, data } = this; const { rows, cols } = data; if (offsetX > cols.indexWidth && offsetY > rows.height) { rowResizer.hide(); @@ -202,14 +186,14 @@ function overlayerMousescroll(evt) { // up const ri = data.scroll.ri + 1; if (ri < rows.len) { - const rh = loopValue(ri, i => rows.getHeight(i)); + const rh = loopValue(ri, (i) => rows.getHeight(i)); verticalScrollbar.move({ top: top + rh - 1 }); } } else { // down const ri = data.scroll.ri - 1; if (ri >= 0) { - const rh = loopValue(ri, i => rows.getHeight(i)); + const rh = loopValue(ri, (i) => rows.getHeight(i)); verticalScrollbar.move({ top: ri === 0 ? 0 : top - rh }); } } @@ -221,14 +205,14 @@ function overlayerMousescroll(evt) { // left const ci = data.scroll.ci + 1; if (ci < cols.len) { - const cw = loopValue(ci, i => cols.getWidth(i)); + const cw = loopValue(ci, (i) => cols.getWidth(i)); horizontalScrollbar.move({ left: left + cw - 1 }); } } else { // right const ci = data.scroll.ci - 1; if (ci >= 0) { - const cw = loopValue(ci, i => cols.getWidth(i)); + const cw = loopValue(ci, (i) => cols.getWidth(i)); horizontalScrollbar.move({ left: ci === 0 ? 0 : left - cw }); } } @@ -272,9 +256,7 @@ function horizontalScrollbarSet() { } function sheetFreeze() { - const { - selector, data, editor, - } = this; + const { selector, data, editor } = this; const [ri, ci] = data.freeze; if (ri > 0 || ci > 0) { const fwidth = data.freezeTotalWidth(); @@ -285,15 +267,7 @@ function sheetFreeze() { } function sheetReset() { - const { - tableEl, - overlayerEl, - overlayerCEl, - table, - toolbar, - selector, - el, - } = this; + const { tableEl, overlayerEl, overlayerCEl, table, toolbar, selector, el } = this; const tOffset = this.getTableOffset(); const vRect = this.getRect(); tableEl.attr(vRect); @@ -340,7 +314,7 @@ function paste(what, evt) { // pastFromSystemClipboard is async operation, need to tell it how to reset sheet and trigger event after it finishes // pasting content from system clipboard data.pasteFromSystemClipboard(resetSheet, eventTrigger); - } else if (data.paste(what, msg => xtoast('Tip', msg))) { + } else if (data.paste(what, (msg) => xtoast('Tip', msg))) { sheetReset.call(this); } else if (evt) { const cdata = evt.clipboardData.getData('text/plain'); @@ -377,15 +351,11 @@ function toolbarChangePaintformatPaste() { function overlayerMousedown(evt) { // console.log(':::::overlayer.mousedown:', evt.detail, evt.button, evt.buttons, evt.shiftKey); // console.log('evt.target.className:', evt.target.className); - const { - selector, data, table, sortFilter, - } = this; + const { selector, data, table, sortFilter } = this; const { offsetX, offsetY } = evt; const isAutofillEl = evt.target.className === `${cssPrefix}-selector-corner`; const cellRect = data.getCellRectByXY(offsetX, offsetY); - const { - left, top, width, height, - } = cellRect; + const { left, top, width, height } = cellRect; let { ri, ci } = cellRect; // sort or filter const { autoFilter } = data; @@ -409,23 +379,27 @@ function overlayerMousedown(evt) { } // mouse move up - mouseMoveUp(window, (e) => { - // console.log('mouseMoveUp::::'); - ({ ri, ci } = data.getCellRectByXY(e.offsetX, e.offsetY)); - if (isAutofillEl) { - selector.showAutofill(ri, ci); - } else if (e.buttons === 1 && !e.shiftKey) { - selectorSet.call(this, true, ri, ci, true, true); - } - }, () => { - if (isAutofillEl && selector.arange && data.settings.mode !== 'read') { - if (data.autofill(selector.arange, 'all', msg => xtoast('Tip', msg))) { - table.render(); + mouseMoveUp( + window, + (e) => { + // console.log('mouseMoveUp::::'); + ({ ri, ci } = data.getCellRectByXY(e.offsetX, e.offsetY)); + if (isAutofillEl) { + selector.showAutofill(ri, ci); + } else if (e.buttons === 1 && !e.shiftKey) { + selectorSet.call(this, true, ri, ci, true, true); } - } - selector.hideAutofill(); - toolbarChangePaintformatPaste.call(this); - }); + }, + () => { + if (isAutofillEl && selector.arange && data.settings.mode !== 'read') { + if (data.autofill(selector.arange, 'all', (msg) => xtoast('Tip', msg))) { + table.render(); + } + } + selector.hideAutofill(); + toolbarChangePaintformatPaste.call(this); + }, + ); } if (!isAutofillEl && evt.buttons === 1) { @@ -633,7 +607,7 @@ function sheetInitEvents() { overlayerMousedown.call(this, evt); } }) - .on('mousewheel.stop', (evt) => { + .on('wheel.stop', (evt) => { overlayerMousescroll.call(this, evt); }) .on('mouseout', (evt) => { @@ -739,9 +713,7 @@ function sheetInitEvents() { bind(window, 'keydown', (evt) => { if (!this.focusing) return; const keyCode = evt.keyCode || evt.which; - const { - key, ctrlKey, shiftKey, metaKey, - } = evt; + const { key, ctrlKey, shiftKey, metaKey } = evt; // console.log('keydown.evt: ', keyCode); if (ctrlKey || metaKey) { // const { sIndexes, eIndexes } = selector; @@ -870,10 +842,11 @@ function sheetInitEvents() { if (key === 'Delete') { insertDeleteRowColumn.call(this, 'delete-cell-text'); evt.preventDefault(); - } else if ((keyCode >= 65 && keyCode <= 90) - || (keyCode >= 48 && keyCode <= 57) - || (keyCode >= 96 && keyCode <= 105) - || evt.key === '=' + } else if ( + (keyCode >= 65 && keyCode <= 90) || + (keyCode >= 48 && keyCode <= 57) || + (keyCode >= 96 && keyCode <= 105) || + evt.key === '=' ) { dataSetCellText.call(this, evt.key, 'input'); editorSet.call(this); @@ -903,24 +876,15 @@ export default class Sheet { this.verticalScrollbar = new Scrollbar(true); this.horizontalScrollbar = new Scrollbar(false); // editor - this.editor = new Editor( - formulas, - () => this.getTableOffset(), - data.rows.height, - ); + this.editor = new Editor(formulas, () => this.getTableOffset(), data.rows.height); // data validation this.modalValidation = new ModalValidation(); // contextMenu this.contextMenu = new ContextMenu(() => this.getRect(), !showContextmenu); // selector this.selector = new Selector(data); - this.overlayerCEl = h('div', `${cssPrefix}-overlayer-content`) - .children( - this.editor.el, - this.selector.el, - ); - this.overlayerEl = h('div', `${cssPrefix}-overlayer`) - .child(this.overlayerCEl); + this.overlayerCEl = h('div', `${cssPrefix}-overlayer-content`).children(this.editor.el, this.selector.el); + this.overlayerEl = h('div', `${cssPrefix}-overlayer`).child(this.overlayerCEl); // sortFilter this.sortFilter = new SortFilter(); // root element diff --git a/packages/plugins/@hera/plugin-core/src/client/components/x-sheet/demo.ts b/packages/plugins/@hera/plugin-core/src/client/components/x-sheet/demo.ts index 1ea8f9545..68c03489c 100644 --- a/packages/plugins/@hera/plugin-core/src/client/components/x-sheet/demo.ts +++ b/packages/plugins/@hera/plugin-core/src/client/components/x-sheet/demo.ts @@ -589,7 +589,7 @@ export const demoData = [ '1': { width: 200, }, - len: 10, + len: 50, }, validations: [], autofilter: {},