fix: excel scroll bugs
This commit is contained in:
parent
6dab16e437
commit
18e929a275
@ -1,31 +1,29 @@
|
||||
/* global document */
|
||||
/* global window */
|
||||
class Element {
|
||||
constructor(tag, className = '') {
|
||||
private el: HTMLElement;
|
||||
private _data: Record<string, any>;
|
||||
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 };
|
@ -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();
|
||||
}
|
@ -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,7 +379,9 @@ function overlayerMousedown(evt) {
|
||||
}
|
||||
|
||||
// mouse move up
|
||||
mouseMoveUp(window, (e) => {
|
||||
mouseMoveUp(
|
||||
window,
|
||||
(e) => {
|
||||
// console.log('mouseMoveUp::::');
|
||||
({ ri, ci } = data.getCellRectByXY(e.offsetX, e.offsetY));
|
||||
if (isAutofillEl) {
|
||||
@ -417,15 +389,17 @@ function overlayerMousedown(evt) {
|
||||
} 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))) {
|
||||
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
|
||||
|
@ -589,7 +589,7 @@ export const demoData = [
|
||||
'1': {
|
||||
width: 200,
|
||||
},
|
||||
len: 10,
|
||||
len: 50,
|
||||
},
|
||||
validations: [],
|
||||
autofilter: {},
|
||||
|
Loading…
Reference in New Issue
Block a user