feat: excel (#1004)

Co-authored-by: sealday <sealday@gmail.com>
Reviewed-on: https://git.daoyoucloud.com:8443/daoyoucloud/tachybase/pulls/1004
This commit is contained in:
sealday 2024-06-25 05:38:00 +08:00
parent a3dad189fe
commit e772d9ad2b
104 changed files with 883 additions and 603 deletions

View File

@ -1,5 +1,9 @@
import { DrawBox } from './DrawBox';
import { dpr, drawFontLine, npx, npxLine, thinLineWidth } from './utils';
import { npxLine } from './utils';
import { drawFontLine } from './utils';
import { npx } from './utils';
import { thinLineWidth } from './utils';
import { dpr } from './utils';
interface Attr {
align?: 'left' | 'center' | 'right';
@ -159,7 +163,6 @@ export class Draw {
const { ctx } = this;
ctx.lineWidth = thinLineWidth();
ctx.strokeStyle = color;
// console.log('style:', style);
if (style === 'medium') {
ctx.lineWidth = npx(2) - 0.5;
} else if (style === 'thick') {

View File

@ -1,8 +1,8 @@
import { cssPrefix } from '../config';
import DropdownColor from './dropdown_color';
import DropdownLineType from './dropdown_linetype';
import { h } from './element';
import Icon from './icon';
import DropdownColor from './dropdown_color';
import DropdownLineType from './dropdown_linetype';
import { cssPrefix } from '../config';
function buildTable(...trs) {
return h('table', '').child(h('tbody', '').children(...trs));

View File

@ -1,12 +1,12 @@
import { h } from './element';
import { bindClickoutside, unbindClickoutside } from './event';
import { cssPrefix } from '../config';
import Icon from './icon';
import FormInput from './form_input';
import Dropdown from './dropdown';
// Record: temp not used
// import { xtoast } from './message';
import { tf } from '../locale/locale';
import Dropdown from './dropdown';
import { h } from './element';
import { bindClickoutside, unbindClickoutside } from './event';
import FormInput from './form_input';
import Icon from './icon';
class DropdownMore extends Dropdown {
constructor(click) {
@ -32,7 +32,10 @@ class DropdownMore extends Dropdown {
setTitle() {}
}
const menuItems = [{ key: 'delete', title: tf('contextmenu.deleteSheet') }];
const menuItems = [
{ key: 'delete', title: tf('contextmenu.deleteSheet') },
{ key: 'rename', title: tf('contextmenu.renameSheet') },
];
function buildMenuItem(item) {
return h('div', `${cssPrefix}-item`)

View File

@ -1,6 +1,6 @@
import { HComponent } from './element';
import { cssPrefix } from '../config';
import { t } from '../locale/locale';
import { HComponent } from './element';
export default class Button extends HComponent {
// type: primary

View File

@ -1,6 +1,6 @@
import { t } from '../locale/locale';
import { h } from './element';
import Icon from './icon';
import { t } from '../locale/locale';
function addMonth(date, step) {
date.setMonth(date.getMonth() + step);
@ -21,7 +21,7 @@ function monthDays(year, month, cdate) {
const index = i * 7 + j;
const d = weekday(startDate, index);
const disabled = d.getMonth() !== month;
// console.log('d:', d, ', cdate:', cdate);
const active = d.getMonth() === cdate.getMonth() && d.getDate() === cdate.getDate();
datess[i][j] = { d, disabled, active };
}

View File

@ -1,5 +1,5 @@
import { cssPrefix } from '../config';
import { h } from './element';
import { cssPrefix } from '../config';
const themeColorPlaceHolders = [
'#ffffff',

View File

@ -1,7 +1,7 @@
import { cssPrefix } from '../config';
import { tf } from '../locale/locale';
import { h } from './element';
import { bindClickoutside, unbindClickoutside } from './event';
import { cssPrefix } from '../config';
import { tf } from '../locale/locale';
const menuItems = [
{ key: 'copy', title: tf('contextmenu.copy'), label: 'Ctrl+C' },

View File

@ -1,6 +1,6 @@
import { cssPrefix } from '../config';
import Calendar from './calendar';
import { h } from './element';
import { cssPrefix } from '../config';
export default class Datepicker {
constructor() {
@ -9,10 +9,8 @@ export default class Datepicker {
}
setValue(date) {
// console.log(':::::::', date, typeof date, date instanceof string);
const { calendar } = this;
if (typeof date === 'string') {
// console.log(/^\d{4}-\d{1,2}-\d{1,2}$/.test(date));
if (/^\d{4}-\d{1,2}-\d{1,2}$/.test(date)) {
calendar.setValue(new Date(date.replace(new RegExp('-', 'g'), '/')));
}

View File

@ -1,6 +1,6 @@
import { cssPrefix } from '../config';
import { h, HComponent } from './element';
import { HComponent, h } from './element';
import { bindClickoutside, unbindClickoutside } from './event';
import { cssPrefix } from '../config';
export default class Dropdown extends HComponent {
constructor(title, width, showArrow, placement, ...children) {

View File

@ -1,7 +1,7 @@
import { cssPrefix } from '../config';
import Dropdown from './dropdown';
import { h } from './element';
import Icon from './icon';
import { cssPrefix } from '../config';
function buildItemWithIcon(iconName) {
return h('div', `${cssPrefix}-item`).child(new Icon(iconName));

View File

@ -1,6 +1,6 @@
import BorderPalette from './border_palette';
import Dropdown from './dropdown';
import Icon from './icon';
import BorderPalette from './border_palette';
export default class DropdownBorder extends Dropdown {
constructor() {

View File

@ -1,6 +1,6 @@
import ColorPalette from './color_palette';
import Dropdown from './dropdown';
import Icon from './icon';
import ColorPalette from './color_palette';
export default class DropdownColor extends Dropdown {
constructor(iconName, color) {

View File

@ -1,7 +1,7 @@
import { cssPrefix } from '../config';
import { baseFonts } from '../core/font';
import Dropdown from './dropdown';
import { h } from './element';
import { baseFonts } from '../core/font';
import { cssPrefix } from '../config';
export default class DropdownFont extends Dropdown {
constructor() {

View File

@ -1,7 +1,7 @@
import { cssPrefix } from '../config';
import { fontSizes } from '../core/font';
import Dropdown from './dropdown';
import { h } from './element';
import { fontSizes } from '../core/font';
import { cssPrefix } from '../config';
export default class DropdownFontSize extends Dropdown {
constructor() {

View File

@ -1,7 +1,7 @@
import { cssPrefix } from '../config';
import { baseFormats } from '../core/format';
import Dropdown from './dropdown';
import { h } from './element';
import { baseFormats } from '../core/format';
import { cssPrefix } from '../config';
export default class DropdownFormat extends Dropdown {
constructor() {

View File

@ -1,8 +1,8 @@
import { cssPrefix } from '../config';
import { baseFormulas } from '../core/formula';
import Dropdown from './dropdown';
import { h } from './element';
import Icon from './icon';
import { h } from './element';
import { baseFormulas } from '../core/formula';
import { cssPrefix } from '../config';
export default class DropdownFormula extends Dropdown {
constructor() {

View File

@ -1,7 +1,7 @@
import { cssPrefix } from '../config';
import Dropdown from './dropdown';
import { h } from './element';
import Icon from './icon';
import { cssPrefix } from '../config';
const lineTypes = [
[

View File

@ -1,10 +1,7 @@
//* global window */
import { cssPrefix } from '../config';
import Datepicker from './datepicker';
import { h } from './element';
import Suggest from './suggest';
// import { mouseMoveUp } from '../event';
import Datepicker from './datepicker';
import { cssPrefix } from '../config';
function resetTextareaSize() {
const { inputText } = this;
@ -56,7 +53,7 @@ function keydownEventHandler(evt) {
function inputEventHandler(evt) {
const v = evt.target.value;
// console.log(evt, 'v:', v);
const { suggest, textlineEl, validator } = this;
const { cell } = this;
if (cell !== null) {
@ -138,7 +135,7 @@ function suggestItemClick(it) {
eit = '';
}
this.inputText = `${sit + it.key}(`;
// console.log('inputText:', this.inputText);
position = this.inputText.length;
this.inputText += `)${eit}`;
}
@ -167,7 +164,6 @@ export default class Editor {
});
this.datepicker = new Datepicker();
this.datepicker.change((d) => {
// console.log('d:', d);
this.setText(dateFormat(d));
this.clear();
});
@ -219,7 +215,7 @@ export default class Editor {
if (offset) {
this.areaOffset = offset;
const { left, top, width, height, l, t } = offset;
// console.log('left:', left, ',top:', top, ', freeze:', freeze);
const elOffset = { left: 0, top: 0 };
// top left
if (freeze.w > l && freeze.h > t) {
@ -245,7 +241,6 @@ export default class Editor {
setCell(cell, validator) {
if (cell && cell.editable === false) return;
// console.log('::', validator);
const { el, datepicker, suggest } = this;
el.show();
this.cell = cell;
@ -270,7 +265,7 @@ export default class Editor {
setText(text) {
this.inputText = text;
// console.log('text>>:', text);
setText.call(this, text, text.length);
resetTextareaSize.call(this);
}

View File

@ -1,5 +1,4 @@
import _ from 'lodash';
class Element {
el: HTMLElement;
private _data: Record<string, any>;
@ -25,7 +24,6 @@ class Element {
const [fen, ...oen] = eventNames.split('.');
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];
@ -60,7 +58,7 @@ class Element {
};
}
scroll = _.throttle((v?) => {
scroll = (v?) => {
const { el } = this;
if (v !== undefined) {
if (v.left !== undefined) {
@ -71,7 +69,7 @@ class Element {
}
}
return { left: el.scrollLeft, top: el.scrollTop };
}, 10);
};
box() {
return this.el.getBoundingClientRect();
@ -81,6 +79,9 @@ class Element {
return new Element(this.el.parentElement);
}
children(): NodeListOf<ChildNode>;
children(...others: (string | Element)[]): this;
children(...eles) {
if (arguments.length === 0) {
return this.el.childNodes;
@ -155,11 +156,11 @@ class Element {
return this;
}
// key, value
// key
// {k, v}...
attr(key, value) {
if (value !== undefined) {
attr(key: string, value: string): this;
attr(key: string, value: undefined): string;
attr(key: Record<string, string>, value: undefined): this;
attr(key: string | Record<string, string>, value: string | undefined): string | this {
if (value !== undefined && typeof key === 'string') {
this.el.setAttribute(key, value);
} else {
if (typeof key === 'string') {
@ -172,12 +173,15 @@ class Element {
return this;
}
removeAttr(key) {
removeAttr(key: string): this {
this.el.removeAttribute(key);
return this;
}
html(content) {
html(content: undefined): string;
html(content: string): this;
html(content: string | undefined): string | this {
if (content !== undefined) {
this.el.innerHTML = content;
return this;

View File

@ -1,4 +1,3 @@
/* global window */
export function bind(target, name, fn) {
target.addEventListener(name, fn);
}
@ -18,7 +17,7 @@ export function unbindClickoutside(el) {
export function bindClickoutside(el, cb) {
el.xclickoutside = (evt) => {
// ignore double click
// console.log('evt:', evt);
if (evt.detail === 2 || el.contains(evt.target)) return;
if (cb) cb(el);
else {
@ -32,7 +31,6 @@ export function mouseMoveUp(target, movefunc, upfunc) {
bind(target, 'mousemove', movefunc);
const t = target;
t.xEvtUp = (evt) => {
// console.log('mouseup>>>');
unbind(target, 'mousemove', movefunc);
unbind(target, 'mouseup', target.xEvtUp);
upfunc(evt);
@ -42,7 +40,7 @@ export function mouseMoveUp(target, movefunc, upfunc) {
function calTouchDirection(spanx, spany, evt, cb) {
let direction = '';
// console.log('spanx:', spanx, ', spany:', spany);
if (Math.abs(spanx) > Math.abs(spany)) {
// horizontal
direction = spanx > 0 ? 'right' : 'left';
@ -68,7 +66,6 @@ export function bindTouch(target, { move, end }) {
const spanx = pageX - startx;
const spany = pageY - starty;
if (Math.abs(spanx) > 10 || Math.abs(spany) > 10) {
// console.log('spanx:', spanx, ', spany:', spany);
calTouchDirection(spanx, spany, evt, move);
startx = pageX;
starty = pageY;

View File

@ -1,6 +1,6 @@
import { h } from './element';
import { cssPrefix } from '../config';
import { t } from '../locale/locale';
import { h } from './element';
const patterns = {
number: /(^\d+$)|(^\d+(\.\d{0,4})?$)/,

View File

@ -1,5 +1,5 @@
import { cssPrefix } from '../config';
import { h } from './element';
import { cssPrefix } from '../config';
export default class FormInput {
constructor(width, hint) {

View File

@ -1,6 +1,6 @@
import { cssPrefix } from '../config';
import { h } from './element';
import Suggest from './suggest';
import { cssPrefix } from '../config';
export default class FormSelect {
constructor(key, items, width, getTitle = (it) => it, change = () => {}) {

View File

@ -1,5 +1,5 @@
import { HComponent, h } from './element';
import { cssPrefix } from '../config';
import { h, HComponent } from './element';
export default class Icon extends HComponent {
constructor(name) {

View File

@ -1,7 +1,6 @@
/* global document */
import { cssPrefix } from '../config';
import { h } from './element';
import Icon from './icon';
import { cssPrefix } from '../config';
export function xtoast(title, content) {
const el = h('div', `${cssPrefix}-toast`);

View File

@ -1,13 +1,15 @@
/* global document */
/* global window */
import { cssPrefix } from '../config';
import { h } from './element';
import { bind, unbind } from './event';
import { h, HComponent } from './element';
import Icon from './icon';
import { cssPrefix } from '../config';
import { bind, unbind } from './event';
export default class Modal {
constructor(title, content, width = '600px') {
this.title = title;
el: HComponent;
constructor(
private title,
content,
width = '600px',
) {
this.el = h('div', `${cssPrefix}-modal`)
.css('width', width)
.children(

View File

@ -1,11 +1,11 @@
import { cssPrefix } from '../config';
import { t } from '../locale/locale';
import Button from './button';
import { h } from './element';
import FormField from './form_field';
import Modal from './modal';
import FormInput from './form_input';
import FormSelect from './form_select';
import Modal from './modal';
import FormField from './form_field';
import Button from './button';
import { t } from '../locale/locale';
import { h } from './element';
import { cssPrefix } from '../config';
const fieldLabelWidth = 100;
@ -146,9 +146,8 @@ export default class ModalValidation extends Modal {
const attrs = ['mf', 'rf', 'cf', 'of', 'svf', 'vf', 'minvf', 'maxvf'];
for (let i = 0; i < attrs.length; i += 1) {
const field = this[attrs[i]];
// console.log('field:', field);
if (field.isShow()) {
// console.log('it:', it);
if (!field.validate()) return;
}
}
@ -165,7 +164,7 @@ export default class ModalValidation extends Modal {
value = this.vf.val();
}
}
// console.log(mode, ref, type, operator, value);
this.change('save', mode, ref, {
type,
operator,

View File

@ -1,10 +1,9 @@
/* global window document */
import { Draw } from '../canvas';
import { cssPrefix } from '../config';
import { t } from '../locale/locale';
import Button from './button';
import { h } from './element';
import { cssPrefix } from '../config';
import Button from './button';
import { Draw } from '../canvas';
import { renderCell } from './table';
import { t } from '../locale/locale';
// resolution: 72 => 595 x 842
// 150 => 1240 x 1754
@ -22,8 +21,8 @@ const PAGER_SIZES = [
const PAGER_ORIENTATIONS = ['landscape', 'portrait'];
function inches2px(inc) {
return parseInt(96 * inc, 10);
function inches2px(inc: number) {
return parseInt(String(96 * inc), 10);
}
function btnClick(type) {
@ -38,9 +37,8 @@ function pagerSizeChange(evt) {
const { paper } = this;
const { value } = evt.target;
const ps = PAGER_SIZES[value];
paper.w = inches2px(ps[1]);
paper.h = inches2px(ps[2]);
// console.log('paper:', ps, paper);
paper.w = inches2px(ps[1] as number);
paper.h = inches2px(ps[2] as number);
this.preview();
}
function pagerOrientationChange(evt) {
@ -52,11 +50,15 @@ function pagerOrientationChange(evt) {
}
export default class Print {
constructor(data) {
paper;
el;
contentEl;
canvases;
constructor(private data) {
this.paper = {
w: inches2px(PAGER_SIZES[0][1]),
h: inches2px(PAGER_SIZES[0][2]),
padding: 50,
w: inches2px(PAGER_SIZES[0][1] as number),
h: inches2px(PAGER_SIZES[0][2] as number),
padding: 0,
orientation: PAGER_ORIENTATIONS[0],
get width() {
return this.orientation === 'landscape' ? this.h : this.w;
@ -65,7 +67,6 @@ export default class Print {
return this.orientation === 'landscape' ? this.w : this.h;
},
};
this.data = data;
this.el = h('div', `${cssPrefix}-print`)
.children(
h('div', `${cssPrefix}-print-bar`).children(
@ -120,7 +121,7 @@ export default class Print {
const iwidth = width - padding * 2;
const iheight = height - padding * 2;
const cr = data.contentRange();
const pages = parseInt(cr.h / iheight, 10) + 1;
const pages = parseInt(String(cr.h / iheight), 10) + 1;
const scale = iwidth / cr.w;
let left = padding;
const top = padding;
@ -143,12 +144,12 @@ export default class Print {
const wrap = h('div', `${cssPrefix}-canvas-card`);
const canvas = h('canvas', `${cssPrefix}-canvas`);
this.canvases.push(canvas.el);
const draw = new Draw(canvas.el, width, height);
// FIXME 可以用泛型
const draw = new Draw(canvas.el as HTMLCanvasElement, width, height);
// cell-content
draw.save();
draw.translate(left, top);
if (scale < 1) draw.scale(scale, scale);
// console.log('ri:', ri, cr.eri, yoffset);
for (; ri <= cr.eri; ri += 1) {
const rh = data.rows.getHeight(ri);
th += rh;
@ -188,13 +189,14 @@ export default class Print {
const iframe = h('iframe', '').hide();
const { el } = iframe;
window.document.body.appendChild(el);
const { contentWindow } = el;
// FIXME 可以用泛型
const { contentWindow } = el as HTMLIFrameElement;
const idoc = contentWindow.document;
const style = document.createElement('style');
style.innerHTML = `
@page { size: ${paper.width}px ${paper.height}px; };
canvas {
page-break-before: auto;
page-break-before: auto;
page-break-after: always;
image-rendering: pixelated;
};

View File

@ -1,7 +1,6 @@
/* global window */
import { cssPrefix } from '../config';
import { h } from './element';
import { mouseMoveUp } from './event';
import { cssPrefix } from '../config';
export default class Resizer {
constructor(vertical = false, minDistance) {
@ -80,14 +79,13 @@ export default class Resizer {
let startEvt = evt;
const { el, lineEl, cRect, vertical, minDistance } = this;
let distance = vertical ? cRect.width : cRect.height;
// console.log('distance:', distance);
lineEl.show();
mouseMoveUp(
window,
(e) => {
this.moving = true;
if (startEvt !== null && e.buttons === 1) {
// console.log('top:', top, ', left:', top, ', cRect:', cRect);
if (vertical) {
distance += e.movementX;
if (distance > minDistance) {

View File

@ -1,11 +1,11 @@
import { cssPrefix } from '../config';
import { h, HComponent } from './element';
import { cssPrefix } from '../config';
export default class Scrollbar {
private el: HComponent;
private contentEl: HComponent;
private _moveFn: (type: string, handler: (e: Event) => void) => void;
constructor(private vertical: 'vertical' | 'horizontal') {
constructor(private vertical: boolean) {
this._moveFn = null;
this.el = h('div', `${cssPrefix}-scrollbar ${vertical ? 'vertical' : 'horizontal'}`)
.child((this.contentEl = h('div', '')))

View File

@ -1,6 +1,6 @@
import { h } from './element';
import { cssPrefix } from '../config';
import { CellRange } from '../core/cell_range';
import { h } from './element';
const selectorHeightBorderWidth = 2 * 2 - 1;
let startZIndex = 10;
@ -128,7 +128,7 @@ function calLAreaOffset(offset) {
const { top, width, height, l, t, scroll } = offset;
const ftheight = data.freezeTotalHeight();
let top0 = top - ftheight;
// console.log('ftheight:', ftheight, ', t:', t);
if (ftheight > t) top0 -= scroll.y;
return {
left: l,
@ -247,7 +247,6 @@ export default class Selector {
}
resetAreaOffset() {
// console.log('offset:', offset);
const offset = this.data.getSelectedRect();
const coffset = this.data.getClipboardRect();
setAllAreaOffset.call(this, offset);
@ -307,14 +306,13 @@ export default class Selector {
}
reset() {
// console.log('::::', this.data);
const { eri, eci } = this.data.selector.range;
this.setEnd(eri, eci);
}
showAutofill(ri, ci) {
if (ri === -1 && ci === -1) return;
// console.log('ri:', ri, ', ci:', ci);
// const [sri, sci] = this.sIndexes;
// const [eri, eci] = this.eIndexes;
const { sri, sci, eri, eci } = this.range;
@ -327,41 +325,39 @@ export default class Selector {
const ecn = eci - ci;
if (scn > 0) {
// left
// console.log('left');
this.arange = new CellRange(sri, nci, eri, sci - 1);
// this.saIndexes = [sri, nci];
// this.eaIndexes = [eri, sci - 1];
// data.calRangeIndexes2(
} else if (srn > 0) {
// top
// console.log('top');
// nri = sri;
this.arange = new CellRange(nri, sci, sri - 1, eci);
// this.saIndexes = [nri, sci];
// this.eaIndexes = [sri - 1, eci];
} else if (ecn < 0) {
// right
// console.log('right');
// nci = eci;
this.arange = new CellRange(sri, eci + 1, eri, nci);
// this.saIndexes = [sri, eci + 1];
// this.eaIndexes = [eri, nci];
} else if (ern < 0) {
// bottom
// console.log('bottom');
// nri = eri;
this.arange = new CellRange(eri + 1, sci, nri, eci);
// this.saIndexes = [eri + 1, sci];
// this.eaIndexes = [nri, eci];
} else {
// console.log('else:');
this.arange = null;
// this.saIndexes = null;
// this.eaIndexes = null;
return;
}
if (this.arange !== null) {
// console.log(this.saIndexes, ':', this.eaIndexes);
const offset = this.data.getRect(this.arange);
offset.width += 2;
offset.height += 2;

View File

@ -1,44 +1,25 @@
/* global window */
import { cssPrefix } from '../config';
import { formulas } from '../core/formula';
import ContextMenu from './contextmenu';
import Editor from './editor';
import { h } from './element';
import { bind, bindTouch, createEventEmitter, mouseMoveUp } from './event';
import { xtoast } from './message';
import ModalValidation from './modal_validation';
import Print from './print';
import { bind, mouseMoveUp, bindTouch, createEventEmitter } from './event';
import Resizer from './resizer';
import Scrollbar from './scrollbar';
import Selector from './selector';
import SortFilter from './sort_filter';
import Editor from './editor';
import Print from './print';
import ContextMenu from './contextmenu';
import Table from './table';
import Toolbar from './toolbar/index';
/**
* @desc throttle fn
* @param func function
* @param wait Delay in milliseconds
*/
function throttle(func, wait) {
let timeout;
return (...arg) => {
const that = this;
const args = arg;
if (!timeout) {
timeout = setTimeout(() => {
timeout = null;
func.apply(that, args);
}, wait);
}
};
}
import ModalValidation from './modal_validation';
import SortFilter from './sort_filter';
import { xtoast } from './message';
import { cssPrefix } from '../config';
import { formulas } from '../core/formula';
import { throttle } from '../core/helper';
function scrollbarMove() {
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) {
horizontalScrollbar.move({ left: l + width - tableOffset.width });
} else {
@ -47,7 +28,7 @@ function scrollbarMove() {
horizontalScrollbar.move({ left: l - 1 - fsw });
}
}
// console.log('top:', top, ', height:', height, ', tof.height:', tableOffset.height);
if (Math.abs(top) + height > tableOffset.height) {
verticalScrollbar.move({ top: t + height - tableOffset.height - 1 });
} else {
@ -85,7 +66,7 @@ function selectorMove(multiple, direction) {
if (multiple) {
[ri, ci] = selector.moveIndexes;
}
// console.log('selector.move:', ri, ci);
if (direction === 'left') {
if (ci > 0) ci -= 1;
} else if (direction === 'right') {
@ -114,7 +95,6 @@ function selectorMove(multiple, direction) {
// private methods
function overlayerMousemove(evt) {
// console.log('x:', evt.offsetX, ', y:', evt.offsetY);
if (evt.buttons !== 0) return;
if (evt.target.className === `${cssPrefix}-resizer-hover`) return;
const { offsetX, offsetY } = evt;
@ -164,7 +144,6 @@ function overlayerMousescroll(evt) {
const { verticalScrollbar, horizontalScrollbar, data } = this;
const { top } = verticalScrollbar.scroll();
const { left } = horizontalScrollbar.scroll();
// console.log('evt:::', evt.wheelDelta, evt.detail * 40);
const { rows, cols } = data;
@ -179,7 +158,7 @@ function overlayerMousescroll(evt) {
} while (v <= 0);
return v;
};
// console.log('deltaX', deltaX, 'evt.detail', evt.detail);
// if (evt.detail) deltaY = evt.detail * 40;
const moveY = (vertical) => {
if (vertical > 0) {
@ -220,7 +199,7 @@ function overlayerMousescroll(evt) {
const tempY = Math.abs(deltaY);
const tempX = Math.abs(deltaX);
const temp = Math.max(tempY, tempX);
// console.log('event:', evt);
// detail for windows/mac firefox vertical scroll
if (/Firefox/i.test(window.navigator.userAgent)) throttle(moveY(evt.detail), 50);
if (temp === tempX) throttle(moveX(deltaX), 50);
@ -243,7 +222,7 @@ function verticalScrollbarSet() {
const { data, verticalScrollbar } = this;
const { height } = this.getTableOffset();
const erth = data.exceptRowTotalHeight(0, -1);
// console.log('erth:', erth);
verticalScrollbar.set(height, data.rows.totalHeight() - erth);
}
@ -349,8 +328,6 @@ 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 { offsetX, offsetY } = evt;
const isAutofillEl = evt.target.className === `${cssPrefix}-selector-corner`;
@ -369,9 +346,7 @@ function overlayerMousedown(evt) {
}
}
// console.log('ri:', ri, ', ci:', ci);
if (!evt.shiftKey) {
// console.log('selectorSetStart:::');
if (isAutofillEl) {
selector.showAutofill(ri, ci);
} else {
@ -382,7 +357,6 @@ function overlayerMousedown(evt) {
mouseMoveUp(
window,
(e) => {
// console.log('mouseMoveUp::::');
({ ri, ci } = data.getCellRectByXY(e.offsetX, e.offsetY));
if (isAutofillEl) {
selector.showAutofill(ri, ci);
@ -404,7 +378,6 @@ function overlayerMousedown(evt) {
if (!isAutofillEl && evt.buttons === 1) {
if (evt.shiftKey) {
// console.log('shiftKey::::');
selectorSet.call(this, true, ri, ci);
}
}
@ -415,7 +388,7 @@ function editorSetOffset() {
const sOffset = data.getSelectedRect();
const tOffset = this.getTableOffset();
let sPosition = 'top';
// console.log('sOffset:', sOffset, ':', tOffset);
if (sOffset.top > tOffset.height / 2) {
sPosition = 'bottom';
}
@ -564,7 +537,6 @@ function toolbarChange(type, value) {
}
function sortFilterChange(ci, order, operator, value) {
// console.log('sort:', sortDesc, operator, value);
this.data.setAutoFilter(ci, order, operator, value);
sheetReset.call(this);
}
@ -669,7 +641,6 @@ function sheetInitEvents() {
};
// contextmenu
contextMenu.itemClick = (type) => {
// console.log('type:', type);
if (type === 'validation') {
modalValidation.setValue(this.data.getSelectedValidation());
} else if (type === 'copy') {
@ -714,7 +685,7 @@ function sheetInitEvents() {
if (!this.focusing) return;
const keyCode = evt.keyCode || evt.which;
const { key, ctrlKey, shiftKey, metaKey } = evt;
// console.log('keydown.evt: ', keyCode);
if (ctrlKey || metaKey) {
// const { sIndexes, eIndexes } = selector;
// let what = 'all';
@ -789,7 +760,6 @@ function sheetInitEvents() {
break;
}
} else {
// console.log('evt.keyCode:', evt.keyCode);
switch (keyCode) {
case 32:
if (shiftKey) {

View File

@ -1,8 +1,8 @@
import { h } from './element';
import Button from './button';
import { bindClickoutside, unbindClickoutside } from './event';
import { cssPrefix } from '../config';
import { t } from '../locale/locale';
import Button from './button';
import { h } from './element';
import { bindClickoutside, unbindClickoutside } from './event';
function buildMenu(clsName) {
return h('div', `${cssPrefix}-item ${clsName}`);
@ -51,7 +51,6 @@ export default class SortFilter {
),
)
.hide();
// this.setFilters(['test1', 'test2', 'text3']);
this.ci = null;
this.sortDesc = null;
this.values = null;
@ -69,7 +68,6 @@ export default class SortFilter {
}
itemClick(it) {
// console.log('it:', it);
this.sort = it;
const { sortAscEl, sortDescEl } = this;
sortAscEl.checked(it === 'asc');
@ -77,7 +75,6 @@ export default class SortFilter {
}
filterClick(index, it) {
// console.log('index:', index, it);
const { filterbEl, filterValues, values } = this;
const children = filterbEl.children();
if (it === 'all') {

View File

@ -1,6 +1,6 @@
import { cssPrefix } from '../config';
import { h } from './element';
import { bindClickoutside, unbindClickoutside } from './event';
import { cssPrefix } from '../config';
function inputMovePrev(evt) {
evt.preventDefault();

View File

@ -1,11 +1,12 @@
import { Draw, DrawBox, npx, thinLineWidth } from '../canvas';
import { stringAt } from '../core/alphabet';
import _cell from '../core/cell';
import { getFontSizePxByPt } from '../core/font';
import { formatm } from '../core/format';
import _cell from '../core/cell';
import { formulam } from '../core/formula';
import { formatm } from '../core/format';
import { Draw, DrawBox, thinLineWidth, npx } from '../canvas';
import DataProxy from '../core/data_proxy';
// gobal var
const cellPaddingWidth = 5;
const tableFixedHeaderCleanStyle = { fillStyle: '#f4f5f8' };
const tableGridStyle = {
@ -33,7 +34,7 @@ function renderCellBorders(bboxes, translateFunc) {
const { draw } = this;
if (bboxes) {
const rset = new Set();
// console.log('bboxes:', bboxes);
bboxes.forEach(({ ri, ci, box }) => {
if (!rset.has(ri)) {
rset.add(ri);
@ -77,12 +78,11 @@ export function renderCell(draw, data, rindex, cindex, yoffset = 0) {
cellText = cell.text || '';
}
if (style.format) {
// console.log(data.formatm, '>>', cell.format);
cellText = formatm[style.format].render(cellText);
}
const font = Object.assign({}, style.font);
font.size = getFontSizePxByPt(font.size);
// console.log('style:', style);
draw.text(
cellText,
dbox,
@ -99,7 +99,6 @@ export function renderCell(draw, data, rindex, cindex, yoffset = 0) {
// error
const error = data.validations.getError(rindex, cindex);
if (error) {
// console.log('error:', rindex, cindex, error);
draw.error(dbox);
}
if (frozen) {
@ -199,7 +198,7 @@ function renderFixedHeaders(type, viewRange, w, h, tx, ty) {
if (type === 'all' || type === 'top') draw.fillRect(ntx, 0, sumWidth, h);
const { sri, sci, eri, eci } = data.selector.range;
// console.log(data.selectIndexes);
// draw text
// text font, align...
draw.attr(tableFixedHeaderStyle());
@ -262,15 +261,14 @@ function renderContentGrid({ sri, sci, eri, eci, w, h }, fw, fh, tx, ty) {
draw.attr(tableGridStyle).translate(fw + tx, fh + ty);
// const sumWidth = cols.sumWidth(sci, eci + 1);
// const sumHeight = rows.sumHeight(sri, eri + 1);
// console.log('sumWidth:', sumWidth);
// draw.clearRect(0, 0, w, h);
if (!settings.showGrid) {
draw.restore();
return;
}
// console.log('rowStart:', rowStart, ', rowLen:', rowLen);
data.rowEach(sri, eri, (i, y, ch) => {
// console.log('y:', y);
if (i !== sri) draw.line([0, y], [w, y]);
if (i === eri) draw.line([0, y + ch], [w, y + ch]);
});
@ -293,10 +291,12 @@ function renderFreezeHighlightLine(fw, fh, ftw, fth) {
/** end */
class Table {
constructor(el, data) {
this.el = el;
this.draw = new Draw(el, data.viewWidth(), data.viewHeight());
this.data = data;
constructor(
public el: HTMLElement,
public data: DataProxy,
public draw?: Draw,
) {
this.draw = !draw ? new Draw(el, data.viewWidth(), data.viewHeight()) : draw;
}
resetData(data) {

View File

@ -1,18 +1,17 @@
/* global window */
import { cssPrefix } from '../config';
import { t } from '../locale/locale';
import Dropdown from './dropdown';
import DropdownAlign from './dropdown_align';
import DropdownBorder from './dropdown_border';
import DropdownColor from './dropdown_color';
import { h, HComponent } from './element';
import { bind } from './event';
import tooltip from './tooltip';
import DropdownFont from './dropdown_font';
import DropdownFontSize from './dropdown_fontsize';
import DropdownFormat from './dropdown_format';
import DropdownFormula from './dropdown_formula';
import { h } from './element';
import { bind } from './event';
import DropdownColor from './dropdown_color';
import DropdownAlign from './dropdown_align';
import DropdownBorder from './dropdown_border';
import Dropdown from './dropdown';
import Icon from './icon';
import tooltip from './tooltip';
import { cssPrefix } from '../config';
import { t } from '../locale/locale';
function buildIcon(name) {
return new Icon(name);
@ -63,6 +62,7 @@ function toggleChange(type) {
}
class DropdownMore extends Dropdown {
moreBtns: HComponent;
constructor() {
const icon = new Icon('ellipsis');
const moreBtns = h('div', `${cssPrefix}-toolbar-more`);
@ -115,7 +115,6 @@ export default class Toolbar {
this.change = () => {};
this.widthFn = widthFn;
const style = data.defaultStyle();
// console.log('data:', data);
this.ddFormat = new DropdownFormat();
this.ddFont = new DropdownFont();
this.ddFormula = new DropdownFormula();
@ -211,13 +210,10 @@ export default class Toolbar {
const { data } = this;
const style = data.getSelectedCellStyle();
const cell = data.getSelectedCell();
// console.log('canUndo:', data.canUndo());
this.undoEl.disabled(!data.canUndo());
this.redoEl.disabled(!data.canRedo());
this.mergeEl.active(data.canUnmerge()).disabled(!data.selector.multiple());
this.autofilterEl.active(!data.canAutofilter());
// this.mergeEl.disabled();
// console.log('selectedCell:', style, cell);
const { font } = style;
this.ddFont.setTitle(font.name);
this.ddFontSize.setTitle(font.size);
@ -230,7 +226,6 @@ export default class Toolbar {
this.ddAlign.setTitle(style.align);
this.ddVAlign.setTitle(style.valign);
this.textwrapEl.active(style.textwrap);
// console.log('freeze is Active:', data.freezeIsActive());
this.freezeEl.active(data.freezeIsActive());
if (cell) {
if (cell.format) {

View File

@ -1,5 +1,5 @@
import DropdownAlign from '../dropdown_align';
import DropdownItem from './dropdown_item';
import DropdownAlign from '../dropdown_align';
export default class Align extends DropdownItem {
constructor(value) {

View File

@ -1,5 +1,5 @@
import DropdownBorder from '../dropdown_border';
import DropdownItem from './dropdown_item';
import DropdownBorder from '../dropdown_border';
export default class Border extends DropdownItem {
constructor() {

View File

@ -1,5 +1,5 @@
import DropdownColor from '../dropdown_color';
import DropdownItem from './dropdown_item';
import DropdownColor from '../dropdown_color';
export default class FillColor extends DropdownItem {
constructor(color) {

View File

@ -1,5 +1,5 @@
import DropdownFont from '../dropdown_font';
import DropdownItem from './dropdown_item';
import DropdownFont from '../dropdown_font';
export default class Font extends DropdownItem {
constructor() {

View File

@ -1,5 +1,5 @@
import DropdownFontsize from '../dropdown_fontsize';
import DropdownItem from './dropdown_item';
import DropdownFontsize from '../dropdown_fontsize';
export default class Format extends DropdownItem {
constructor() {

View File

@ -1,5 +1,5 @@
import DropdownFormat from '../dropdown_format';
import DropdownItem from './dropdown_item';
import DropdownFormat from '../dropdown_format';
export default class Format extends DropdownItem {
constructor() {

View File

@ -1,5 +1,5 @@
import DropdownFormula from '../dropdown_formula';
import DropdownItem from './dropdown_item';
import DropdownFormula from '../dropdown_formula';
export default class Format extends DropdownItem {
constructor() {

View File

@ -1,5 +1,5 @@
import Icon from '../icon';
import Item from './item';
import Icon from '../icon';
export default class IconItem extends Item {
element() {

View File

@ -1,32 +1,33 @@
/* global window */
import { cssPrefix } from '../../config';
import { h } from '../element';
import { bind } from '../event';
import Align from './align';
import Valign from './valign';
import Autofilter from './autofilter';
import Bold from './bold';
import Italic from './italic';
import Strike from './strike';
import Underline from './underline';
import Border from './border';
import Clearformat from './clearformat';
import Paintformat from './paintformat';
import TextColor from './text_color';
import FillColor from './fill_color';
import Font from './font';
import FontSize from './font_size';
import Font from './font';
import Format from './format';
import Formula from './formula';
import Freeze from './freeze';
import Italic from './italic';
import Item from './item';
import Merge from './merge';
import More from './more';
import Paintformat from './paintformat';
import Print from './print';
import Redo from './redo';
import Strike from './strike';
import TextColor from './text_color';
import Textwrap from './textwrap';
import Underline from './underline';
import Undo from './undo';
import Valign from './valign';
import Print from './print';
import Textwrap from './textwrap';
import More from './more';
import Item from './item';
import { h } from '../element';
import { cssPrefix } from '../../config';
import { bind } from '../event';
function buildDivider() {
return h('div', `${cssPrefix}-toolbar-divider`);
@ -207,13 +208,13 @@ export default class Toolbar {
if (this.isHide) return;
const { data } = this;
const style = data.getSelectedCellStyle();
// console.log('canUndo:', data.canUndo());
this.undoEl.setState(!data.canUndo());
this.redoEl.setState(!data.canRedo());
this.mergeEl.setState(data.canUnmerge(), !data.selector.multiple());
this.autofilterEl.setState(!data.canAutofilter());
// this.mergeEl.disabled();
// console.log('selectedCell:', style, cell);
const { font, format } = style;
this.formatEl.setState(format);
this.fontEl.setState(font.name);
@ -227,7 +228,7 @@ export default class Toolbar {
this.alignEl.setState(style.align);
this.valignEl.setState(style.valign);
this.textwrapEl.setState(style.textwrap);
// console.log('freeze is Active:', data.freezeIsActive());
this.freezeEl.setState(data.freezeIsActive());
}
}

View File

@ -1,7 +1,7 @@
import { cssPrefix } from '../../config';
import { t } from '../../locale/locale';
import { h } from '../element';
import tooltip from '../tooltip';
import { h } from '../element';
import { t } from '../../locale/locale';
export default class Item {
// tooltip

View File

@ -1,8 +1,9 @@
import { cssPrefix } from '../../config';
import Dropdown from '../dropdown';
import DropdownItem from './dropdown_item';
import { cssPrefix } from '../../config';
import { h } from '../element';
import Icon from '../icon';
import DropdownItem from './dropdown_item';
class DropdownMore extends Dropdown {
constructor() {

View File

@ -1,5 +1,5 @@
import DropdownColor from '../dropdown_color';
import DropdownItem from './dropdown_item';
import DropdownColor from '../dropdown_color';
export default class TextColor extends DropdownItem {
constructor(color) {

View File

@ -1,5 +1,5 @@
import Icon from '../icon';
import Item from './item';
import Icon from '../icon';
export default class ToggleItem extends Item {
element() {

View File

@ -1,5 +1,5 @@
import DropdownAlign from '../dropdown_align';
import DropdownItem from './dropdown_item';
import DropdownAlign from '../dropdown_align';
export default class Valign extends DropdownItem {
constructor(value) {

View File

@ -1,9 +1,8 @@
/* global document */
import { cssPrefix } from '../config';
import { h } from './element';
import { bind } from './event';
import { cssPrefix } from '../config';
export default function tooltip(html, target) {
export default function tooltip(html: string, target) {
if (target.classList.contains('active')) {
return;
}
@ -11,7 +10,6 @@ export default function tooltip(html, target) {
const el = h('div', `${cssPrefix}-tooltip`).html(html).show();
document.body.appendChild(el.el);
const elBox = el.box();
// console.log('elBox:', elBox);
el.css('left', `${left + width / 2 - elBox.width / 2}px`).css('top', `${top + height + 2}px`);
bind(target, 'mouseleave', () => {

View File

@ -0,0 +1,63 @@
import React, { useState } from 'react';
import { cssPrefix } from '../config';
import { tf } from '../locale/locale';
import { Icon } from './Icon';
// this.el = h('div', `${cssPrefix}-bottombar`).children(
// this.contextMenu.el,
// (this.menuEl = h('ul', `${cssPrefix}-menu`).child(
// h('li', '').children(
// new Icon('add').on('click', () => {
// addFunc();
// }),
// h('span', '').child(this.moreEl),
// ),
// )),
// );
// this.el = h('div', `${cssPrefix}-contextmenu`)
// .css('width', '160px')
// .children(...buildMenu.call(this))
// .hide();
// this.itemClick = () => {};
const menuItems = [
{ key: 'delete', title: tf('contextmenu.deleteSheet') },
{ key: 'rename', title: tf('contextmenu.renameSheet') },
];
// return h('div', `${cssPrefix}-item`)
// .child(item.title())
// .on('click', () => {
// this.itemClick(item.key);
// this.hide();
// });
export const ContextMenu = () => {
const [visible, setVisible] = useState(false);
return (
<div className={`${cssPrefix}-contextmenu`} style={{ width: '160px' }}>
{visible ? menuItems.map((item) => <div key={item.key}>{item.title()}</div>) : null}
</div>
);
};
export const Bottombar = () => {
const addSheet = () => {};
return (
<div className={`${cssPrefix}-bottombar`}>
<ContextMenu />
<ul className={`${cssPrefix}-menu`}>
<li>
<Icon name="add" onClick={addSheet} />
<Icon name="ellipsis" onClick={() => {}} />
</li>
<li className="active">sheet1</li>
<li>sheet2</li>
<li>sheet3</li>
<li>sheet4</li>
</ul>
</div>
);
};

View File

@ -0,0 +1,33 @@
import React from 'react';
// super('div', `${cssPrefix}-dropdown ${placement}`);
// this.title = title;
// this.change = () => {};
// this.headerClick = () => {};
// if (typeof title === 'string') {
// this.title = h('div', `${cssPrefix}-dropdown-title`).child(title);
// } else if (showArrow) {
// this.title.addClass('arrow-left');
// }
// this.contentEl = h('div', `${cssPrefix}-dropdown-content`).css('width', width).hide();
// this.setContentChildren(...children);
// this.headerEl = h('div', `${cssPrefix}-dropdown-header`);
// this.headerEl
// .on('click', () => {
// if (this.contentEl.css('display') !== 'block') {
// this.show();
// } else {
// this.hide();
// }
// })
// .children(
// this.title,
// showArrow ? h('div', `${cssPrefix}-icon arrow-right`).child(h('div', `${cssPrefix}-icon-img arrow-down`)) : '',
// );
// this.children(this.headerEl, this.contentEl);
export const Dropdown = () => {
return <div></div>;
};

View File

@ -0,0 +1,29 @@
import React from 'react';
// class DropdownMore extends Dropdown {
// constructor(click) {
// const icon = new Icon('ellipsis');
// super(icon, 'auto', false, 'top-left');
// this.contentClick = click;
// }
// reset(items) {
// const eles = items.map((it, i) =>
// h('div', `${cssPrefix}-item`)
// .css('width', '150px')
// .css('font-weight', 'normal')
// .on('click', () => {
// this.contentClick(i);
// this.hide();
// })
// .child(it),
// );
// this.setContentChildren(...eles);
// }
// setTitle() {}
// }
export const DropdownMore = () => {
return <div>Dropdown</div>;
};

View File

@ -0,0 +1,25 @@
import React from 'react';
// import { HComponent, h } from './element';
import { cssPrefix } from '../config';
// export default class Icon extends HComponent {
// constructor(name) {
// super('div', `${cssPrefix}-icon`);
// this.iconNameEl = h('div', ``);
// this.child(this.iconNameEl);
// }
// FIXME setName Icon
// setName(name) {
// this.iconNameEl.className(`${cssPrefix}-icon-img ${name}`);
// }
// }
export const Icon = ({ name, onClick }: { name: string; onClick: () => void }) => {
return (
<div className={`${cssPrefix}-icon`} onClick={onClick}>
<div className={`${cssPrefix}-icon-img ${name}`} />
</div>
);
};

View File

@ -0,0 +1,5 @@
import React from 'react';
export const Print = () => {
return <div>Print</div>;
};

View File

@ -0,0 +1,43 @@
import React, { createContext, useContext, useEffect, useState } from 'react';
import { cssPrefix } from '../config';
import { Bottombar } from './Bottombar';
import { Toolbar } from './Toolbar';
import { Table } from './Table';
import { Print } from './Print';
import { Options } from '..';
import DataProxy from '../core/data_proxy';
const SheetContext = createContext<DataProxy>(null);
export const useSheetData = () => {
return useContext(SheetContext);
};
export const SheetRoot = ({ options }: { options: Options }) => {
const [data, setData] = useState<DataProxy>(null);
useEffect(() => {
setData(new DataProxy('sheet1', options));
}, [options]);
if (!data) {
return null;
}
return (
<SheetContext.Provider value={data}>
<div
className={`${cssPrefix}`}
onContextMenu={(e) => {
e.preventDefault();
e.stopPropagation();
}}
>
<Toolbar />
<Table />
{/* <Print /> */}
</div>
<Bottombar />
</SheetContext.Provider>
);
};

View File

@ -0,0 +1,22 @@
import React, { useEffect, useRef } from 'react';
import { cssPrefix } from '../config';
import { useSheetData } from './SheetRoot';
import { Draw } from '../canvas';
import CanvasTable from '../component/table';
export const Table = () => {
const data = useSheetData();
const ref = useRef();
useEffect(() => {
const draw = new Draw(ref.current, data.viewWidth(), data.viewHeight());
const canvasTable = new CanvasTable(ref.current, data, draw);
canvasTable.render();
}, [data, ref]);
return (
<div className={`${cssPrefix}-sheet`}>
<canvas ref={ref} className={`${cssPrefix}-table`} />
</div>
);
};

View File

@ -0,0 +1,39 @@
import React from 'react';
import { cssPrefix } from '../config';
import { Icon } from './Icon';
const btns = [
'undo',
'redo',
'print',
'paintformat',
'clearformat',
'divider',
'bold',
'italic',
'underline',
'strike',
'divider',
'merge',
'textwrap',
'freeze',
'autofilter',
];
export const Toolbar = () => {
return (
<div className={`${cssPrefix}-toolbar`}>
<div className={`${cssPrefix}-toolbar-btns`}>
{btns.map((btn, i) =>
btn === 'divider' ? (
<div key={btn + i} className={`${cssPrefix}-toolbar-divider`} />
) : (
<div key={btn} className={`${cssPrefix}-toolbar-btn`}>
<Icon name={btn} onClick={() => {}} />
</div>
),
)}
</div>
</div>
);
};

View File

@ -1,4 +1,3 @@
/* global window */
export const cssPrefix = 'x-spreadsheet';
export const dpr = window.devicePixelRatio || 1;
export default {

View File

@ -1,28 +0,0 @@
// font.js
/**
* @typedef {number} fontsizePX px for fontSize
*/
/**
* @typedef {number} fontsizePT pt for fontSize
*/
/**
* @typedef {object} BaseFont
* @property {string} key inner key
* @property {string} title title for display
*/
/**
* @typedef {object} FontSize
* @property {fontsizePT} pt
* @property {fontsizePX} px
*/
// alphabet.js
/**
* @typedef {string} tagA1 A1 tag for XY-tag (0, 0)
* @example "A1"
*/
/**
* @typedef {[number, number]} tagXY
* @example [0, 0]
*/

View File

@ -1,4 +1,5 @@
import './_.prototypes';
export type TagA1 = string;
export type TagXY = [number, number];
const alphabets = [
'A',
@ -36,13 +37,13 @@ const alphabets = [
* @param {number} index
* @returns {string}
*/
export function stringAt(index) {
export function stringAt(index: number): string {
let str = '';
let cindex = index;
while (cindex >= alphabets.length) {
cindex /= alphabets.length;
cindex -= 1;
str += alphabets[parseInt(cindex, 10) % alphabets.length];
str += alphabets[parseInt(String(cindex), 10) % alphabets.length];
}
const last = index % alphabets.length;
str += alphabets[last];
@ -55,20 +56,19 @@ export function stringAt(index) {
* @param {string} str "AA" in A1-tag "AA1"
* @returns {number}
*/
export function indexAt(str) {
export function indexAt(str: TagA1) {
let ret = 0;
for (let i = 0; i !== str.length; ++i) ret = 26 * ret + str.charCodeAt(i) - 64;
return ret - 1;
}
// B10 => x,y
/** translate A1-tag to XY-tag
* @date 2019-10-10
* @export
* @param {tagA1} src
* @returns {tagXY}
* @param {TagA1} src
* @returns {TagXY}
*/
export function expr2xy(src) {
export function expr2xy(src: TagA1): TagXY {
let x = '';
let y = '';
for (let i = 0; i < src.length; i += 1) {
@ -87,21 +87,21 @@ export function expr2xy(src) {
* @export
* @param {number} x
* @param {number} y
* @returns {tagA1}
* @returns {TagA1}
*/
export function xy2expr(x, y) {
export function xy2expr(x: number, y: number): TagA1 {
return `${stringAt(x)}${y + 1}`;
}
/** translate A1-tag src by (xn, yn)
* @date 2019-10-10
* @export
* @param {tagA1} src
* @param {TagA1} src
* @param {number} xn
* @param {number} yn
* @returns {tagA1}
* @returns {TagA1}
*/
export function expr2expr(src, xn, yn, condition = () => true) {
export function expr2expr(src: TagA1, xn: number, yn: number, condition = (a, b) => true): TagA1 {
if (xn === 0 && yn === 0) return src;
const [x, y] = expr2xy(src);
if (!condition(x, y)) return src;

View File

@ -1,27 +1,39 @@
import { CellRange } from './cell_range';
// operator: all|eq|neq|gt|gte|lt|lte|in|be
// value:
// in => []
// be => [min, max]
export const enum FilterOperator {
ALL = 'all',
IN = 'in',
EQ = 'eq',
NEQ = 'neq',
GT = 'gt',
GTE = 'gte',
LT = 'lt',
LTE = 'lte',
BE = 'be',
NBE = 'nbe',
}
class Filter {
constructor(ci, operator, value) {
this.ci = ci;
constructor(
public ci: unknown,
public operator: FilterOperator,
public value: unknown[],
) {}
set(operator: FilterOperator, value) {
this.operator = operator;
this.value = value;
}
set(operator, value) {
this.operator = operator;
this.value = value;
}
includes(v) {
includes(v: unknown[]) {
const { operator, value } = this;
if (operator === 'all') {
if (operator === FilterOperator.ALL) {
return true;
}
if (operator === 'in') {
if (operator === FilterOperator.IN) {
return value.includes(v);
}
return false;
@ -29,7 +41,7 @@ class Filter {
vlength() {
const { operator, value } = this;
if (operator === 'in') {
if (operator === FilterOperator.IN) {
return value.length;
}
return 0;
@ -42,10 +54,10 @@ class Filter {
}
class Sort {
constructor(ci, order) {
this.ci = ci;
this.order = order;
}
constructor(
public ci: unknown,
public order: 'asc' | 'desc',
) {}
asc() {
return this.order === 'asc';
@ -57,6 +69,9 @@ class Sort {
}
export default class AutoFilter {
ref: string | null;
filters: Filter[];
sort: Sort | null;
constructor() {
this.ref = null;
this.filters = [];

View File

@ -30,7 +30,6 @@ const infixExprToSuffixExpr = (src) => {
} else if (c === '-' && /[+\-*/,(]/.test(oldc)) {
subStrs.push(c);
} else {
// console.log('subStrs:', subStrs.join(''), stack);
if (c !== '(' && subStrs.length > 0) {
stack.push(subStrs.join(''));
}
@ -41,7 +40,7 @@ const infixExprToSuffixExpr = (src) => {
try {
const [ex, ey] = expr2xy(stack.pop());
const [sx, sy] = expr2xy(stack.pop());
// console.log('::', sx, sy, ex, ey);
let rangelen = 0;
for (let x = sx; x <= ex; x += 1) {
for (let y = sy; y <= ey; y += 1) {
@ -51,7 +50,7 @@ const infixExprToSuffixExpr = (src) => {
}
stack.push([c1, rangelen]);
} catch (e) {
// console.log(e);
console.error(e);
}
} else if (fnArgType === 1 || fnArgType === 3) {
if (fnArgType === 3) stack.push(fnArgOperator);
@ -59,7 +58,6 @@ const infixExprToSuffixExpr = (src) => {
stack.push([c1, fnArgsLen]);
fnArgsLen = 1;
} else {
// console.log('c1:', c1, fnArgType, stack, operatorStack);
while (c1 !== '(') {
stack.push(c1);
if (operatorStack.length <= 0) break;
@ -88,7 +86,7 @@ const infixExprToSuffixExpr = (src) => {
operatorStack.push(subStrs.join(''));
} else {
// priority: */ > +-
// console.log('xxxx:', operatorStack, c, stack);
if (operatorStack.length > 0 && (c === '+' || c === '-')) {
let top = operatorStack[operatorStack.length - 1];
if (top !== '(') stack.push(operatorStack.pop());
@ -143,9 +141,8 @@ const evalSubExpr = (subExpr, cellRender) => {
// cellRender: (x, y) => {}
const evalSuffixExpr = (srcStack, formulaMap, cellRender, cellList) => {
const stack = [];
// console.log(':::::formulaMap:', formulaMap);
for (let i = 0; i < srcStack.length; i += 1) {
// console.log(':::>>>', srcStack[i]);
const expr = srcStack[i];
const fc = expr[0];
if (expr === '+') {
@ -199,7 +196,6 @@ const evalSuffixExpr = (srcStack, formulaMap, cellRender, cellList) => {
stack.push(evalSubExpr(expr, cellRender));
cellList.pop();
}
// console.log('stack:', stack);
}
return stack[0];
};

View File

@ -1,16 +1,16 @@
import { expr2xy, xy2expr } from './alphabet';
import { xy2expr, expr2xy } from './alphabet';
class CellRange {
constructor(sri, sci, eri, eci, w = 0, h = 0) {
this.sri = sri;
this.sci = sci;
this.eri = eri;
this.eci = eci;
this.w = w;
this.h = h;
}
constructor(
public sri: number,
public sci: number,
public eri: number,
public eci: number,
public w = 0,
public h = 0,
) {}
set(sri, sci, eri, eci) {
set(sri: number, sci: number, eri: number, eci: number) {
this.sri = sri;
this.sci = sci;
this.eri = eri;
@ -23,18 +23,20 @@ class CellRange {
// cell-index: ri, ci
// cell-ref: A10
includes(...args) {
includes(a: string, b: undefined): boolean;
includes(a: number, b: number): boolean;
includes(a: string | number, b: number | undefined): boolean {
let [ri, ci] = [0, 0];
if (args.length === 1) {
[ci, ri] = expr2xy(args[0]);
} else if (args.length === 2) {
[ri, ci] = args;
if (typeof a === 'string') {
[ci, ri] = expr2xy(a);
} else if (typeof a === 'number') {
[ri, ci] = [a, b];
}
const { sri, sci, eri, eci } = this;
return sri <= ri && ri <= eri && sci <= ci && ci <= eci;
}
each(cb, rowFilter = () => true) {
each(cb, rowFilter = (i: number) => true) {
const { sri, sci, eri, eci } = this;
for (let i = sri; i <= eri; i += 1) {
if (rowFilter(i)) {
@ -45,27 +47,27 @@ class CellRange {
}
}
contains(other) {
contains(other: CellRange) {
return this.sri <= other.sri && this.sci <= other.sci && this.eri >= other.eri && this.eci >= other.eci;
}
// within
within(other) {
within(other: CellRange) {
return this.sri >= other.sri && this.sci >= other.sci && this.eri <= other.eri && this.eci <= other.eci;
}
// disjoint
disjoint(other) {
disjoint(other: CellRange) {
return this.sri > other.eri || this.sci > other.eci || other.sri > this.eri || other.sci > this.eci;
}
// intersects
intersects(other) {
intersects(other: CellRange) {
return this.sri <= other.eri && this.sci <= other.eci && other.sri <= this.eri && other.sci <= this.eci;
}
// union
union(other) {
union(other: CellRange) {
const { sri, sci, eri, eci } = this;
return new CellRange(
other.sri < sri ? other.sri : sri,
@ -80,7 +82,7 @@ class CellRange {
// Returns Array<CellRange> that represents that part of this that does not intersect with other
// difference
difference(other) {
difference(other: CellRange) {
const ret = [];
const addRet = (sri, sci, eri, eci) => {
ret.push(new CellRange(sri, sci, eri, eci));
@ -171,11 +173,11 @@ class CellRange {
}
*/
equals(other) {
equals(other: CellRange) {
return this.eri === other.eri && this.eci === other.eci && this.sri === other.sri && this.sci === other.sci;
}
static valueOf(ref) {
static valueOf(ref: string) {
// B1:B8, B1 => 1 x 1 cell range
const refs = ref.split(':');
const [sci, sri] = expr2xy(refs[0]);

View File

@ -1,16 +1,20 @@
import CellRange from './cell_range';
export default class Clipboard {
range: CellRange;
state: 'clear' | 'cut' | 'copy';
constructor() {
this.range = null; // CellRange
this.state = 'clear';
}
copy(cellRange) {
copy(cellRange: CellRange) {
this.range = cellRange;
this.state = 'copy';
return this;
}
cut(cellRange) {
cut(cellRange: CellRange) {
this.range = cellRange;
this.state = 'cut';
return this;

View File

@ -1,6 +1,12 @@
import helper from './helper';
class Cols {
_: {};
len: number;
width: number;
indexWidth: number;
minWidth: number;
constructor({ len, width, indexWidth, minWidth }) {
this._ = {};
this.len = len;

View File

@ -1,18 +1,16 @@
/* global document */
import { t } from '../locale/locale';
import { expr2xy, xy2expr } from './alphabet';
import AutoFilter from './auto_filter';
import { CellRange } from './cell_range';
import Clipboard from './clipboard';
import { Cols } from './col';
import helper from './helper';
import History from './history';
import { Merges } from './merge';
import { Rows } from './row';
import Scroll from './scroll';
import Selector from './selector';
import Scroll from './scroll';
import History from './history';
import Clipboard from './clipboard';
import AutoFilter from './auto_filter';
import { Merges } from './merge';
import helper from './helper';
import { Rows } from './row';
import { Cols } from './col';
import { Validations } from './validation';
import { CellRange } from './cell_range';
import { expr2xy, xy2expr } from './alphabet';
import { t } from '../locale/locale';
// private methods
/*
@ -109,9 +107,7 @@ const defaultSettings = {
const toolbarHeight = 41;
const bottombarHeight = 41;
// src: cellRange
// dst: cellRange
function canPaste(src, dst, error = () => {}) {
function canPaste(src: CellRange, dst: CellRange, error = (e) => {}) {
const { merges } = this;
const cellRange = dst.clone();
const [srn, scn] = src.size();
@ -137,7 +133,6 @@ function copyPaste(srcCellRange, dstCellRange, what, autofill = false) {
}
rows.copyPaste(srcCellRange, dstCellRange, what, autofill, (ri, ci, cell) => {
if (cell && cell.merge) {
// console.log('cell:', ri, ci, cell);
const [rn, cn] = cell.merge;
if (rn <= 0 && cn <= 0) return;
merges.add(new CellRange(ri, ci, ri + rn, ci + cn));
@ -152,8 +147,15 @@ function cutPaste(srcCellRange, dstCellRange) {
clipboard.clear();
}
export interface BorderStyles {
top?: string[];
bottom?: string[];
left?: string[];
right?: string[];
}
// bss: { top, bottom, left, right }
function setStyleBorder(ri, ci, bss) {
function setStyleBorder(ri, ci, bss: BorderStyles) {
const { styles, rows } = this;
const cell = rows.getCellOrNew(ri, ci);
let cstyle = {};
@ -225,7 +227,7 @@ function setStyleBorders({ mode, style, color }) {
}
const mrl = rn > 0 && ri + rn === eri;
const mcl = cn > 0 && ci + cn === eci;
let bss = {};
let bss: BorderStyles = {};
if (mode === 'all') {
bss = {
bottom: [style, color],
@ -280,7 +282,6 @@ function setStyleBorders({ mode, style, color }) {
function getCellRowByY(y, scrollOffsety) {
const { rows } = this;
const fsh = this.freezeTotalHeight();
// console.log('y:', y, ', fsh:', fsh);
let inits = rows.height;
if (fsh + rows.height < y) inits -= scrollOffsety;
@ -298,7 +299,6 @@ function getCellRowByY(y, scrollOffsety) {
}
}
top -= height;
// console.log('ri:', ri, ', top:', top, ', height:', height);
if (top <= 0) {
return { ri: -1, top: 0, height };
@ -320,6 +320,33 @@ function getCellColByX(x, scrollOffsetx) {
}
export default class DataProxy {
clipboard: Clipboard;
selector: Selector;
// FIXME settings type
settings: {
style?: any;
view?: any;
row?: any;
col?: any;
showToolbar?: any;
showBottomBar?: any;
};
name: any;
freeze: number[];
styles: any[];
merges: Merges;
rows: Rows;
cols: Cols;
validations: Validations;
hyperlinks: {};
comments: {};
scroll: Scroll;
history: History;
autoFilter: AutoFilter;
change: (a?) => void;
exceptRowSet: Set<unknown>;
sortedRowMap: Map<any, any>;
unsortedRowMap: Map<any, any>;
constructor(name, settings) {
this.settings = helper.merge(defaultSettings, settings || {});
// save data begin
@ -347,7 +374,6 @@ export default class DataProxy {
}
addValidation(mode, ref, validator) {
// console.log('mode:', mode, ', ref:', ref, ', validator:', validator);
this.changeData(() => {
this.validations.add(mode, ref, validator);
});
@ -369,7 +395,7 @@ export default class DataProxy {
getSelectedValidation() {
const { ri, ci, range } = this.selector;
const v = this.validations.get(ri, ci);
const ret = { ref: range.toString() };
const ret: any = { ref: range.toString() };
if (v !== null) {
ret.mode = v.mode;
ret.validator = v.validator;
@ -402,7 +428,7 @@ export default class DataProxy {
}
copyToSystemClipboard(evt) {
let copyText = [];
let copyText: string | any[] = [];
const { sri, eri, sci, eci } = this.selector.range;
for (let ri = sri; ri <= eri; ri += 1) {
@ -428,12 +454,15 @@ export default class DataProxy {
// this need https protocol
/* global navigator */
if (navigator.clipboard) {
navigator.clipboard.writeText(copyText).then(
() => {},
(err) => {
console.log('text copy to the system clipboard error ', copyText, err);
},
);
navigator.clipboard
.writeText(copyText)
.then(
() => {},
(err) => {
console.log('text copy to the system clipboard error ', copyText, err);
},
)
.catch(console.error);
}
}
@ -443,7 +472,6 @@ export default class DataProxy {
// what: all | text | format
paste(what = 'all', error = () => {}) {
// console.log('sIndexes:', sIndexes);
const { clipboard, selector } = this;
if (clipboard.isClear()) return false;
if (!canPaste.call(this, clipboard.range, selector.range, error)) return false;
@ -460,20 +488,23 @@ export default class DataProxy {
pasteFromSystemClipboard(resetSheet, eventTrigger) {
const { selector } = this;
navigator.clipboard.readText().then((content) => {
const contentToPaste = this.parseClipboardContent(content);
let startRow = selector.ri;
contentToPaste.forEach((row) => {
let startColumn = selector.ci;
row.forEach((cellContent) => {
this.setCellText(startRow, startColumn, cellContent, 'input');
startColumn += 1;
navigator.clipboard
.readText()
.then((content) => {
const contentToPaste = this.parseClipboardContent(content);
let startRow = selector.ri;
contentToPaste.forEach((row) => {
let startColumn = selector.ci;
row.forEach((cellContent) => {
this.setCellText(startRow, startColumn, cellContent, 'input');
startColumn += 1;
});
startRow += 1;
});
startRow += 1;
});
resetSheet();
eventTrigger(this.rows.getData());
});
resetSheet();
eventTrigger(this.rows.getData());
})
.catch(console.error);
}
parseClipboardContent(clipboardContent) {
@ -533,14 +564,14 @@ export default class DataProxy {
else [sci, eci] = [nci, cci];
selector.range = merges.union(new CellRange(sri, sci, eri, eci));
selector.range = merges.union(selector.range);
// console.log('selector.range:', selector.range);
return selector.range;
}
calSelectedRangeByStart(ri, ci) {
const { selector, rows, cols, merges } = this;
let cellRange = merges.getFirstIncludes(ri, ci);
// console.log('cellRange:', cellRange, ri, ci, merges);
if (cellRange === null) {
cellRange = new CellRange(ri, ci, ri, ci);
if (ri === -1) {
@ -565,7 +596,6 @@ export default class DataProxy {
} else if (property === 'border') {
setStyleBorders.call(this, value);
} else if (property === 'formula') {
// console.log('>>>', selector.multiple());
const { ri, ci, range } = selector;
if (selector.multiple()) {
const [rn, cn] = selector.size();
@ -586,7 +616,8 @@ export default class DataProxy {
} else {
selector.range.each((ri, ci) => {
const cell = rows.getCellOrNew(ri, ci);
let cstyle = {};
// FIXME cstyle type
let cstyle: any = {};
if (cell.style !== undefined) {
cstyle = helper.cloneDeep(styles[cell.style]);
}
@ -641,7 +672,6 @@ export default class DataProxy {
if (vIndex >= 0) {
filter.value.splice(vIndex, 1, text);
}
// console.log('filter:', filter, oldCell);
}
}
// this.resetAutoFilter();
@ -660,7 +690,7 @@ export default class DataProxy {
const { left, top, width, height } = this.getSelectedRect();
const x1 = x - this.cols.indexWidth;
const y1 = y - this.rows.height;
// console.log('x:', x, ',y:', y, 'left:', left, 'top:', top);
return x1 > left && x1 < left + width && y1 > top && y1 < top + height;
}
@ -679,7 +709,7 @@ export default class DataProxy {
getRect(cellRange) {
const { scroll, rows, cols, exceptRowSet } = this;
const { sri, sci, eri, eci } = cellRange;
// console.log('sri:', sri, ',sci:', sci, ', eri:', eri, ', eci:', eci);
// no selector
if (sri < 0 && sci < 0) {
return {
@ -694,7 +724,7 @@ export default class DataProxy {
const top = rows.sumHeight(0, sri, exceptRowSet);
const height = rows.sumHeight(sri, eri + 1, exceptRowSet);
const width = cols.sumWidth(sci, eci + 1);
// console.log('sri:', sri, ', sci:', sci, ', eri:', eri, ', eci:', eci);
let left0 = left - scroll.x;
let top0 = top - scroll.y;
const fsh = this.freezeTotalHeight();
@ -768,7 +798,7 @@ export default class DataProxy {
const { selector, rows } = this;
if (this.isSingleSelected()) return;
const [rn, cn] = selector.size();
// console.log('merge:', rn, cn);
if (rn > 1 || cn > 1) {
const { sri, sci } = selector.range;
this.changeData(() => {
@ -777,7 +807,7 @@ export default class DataProxy {
this.merges.add(selector.range);
// delete merge cells
this.rows.deleteCells(selector.range);
// console.log('cell:', cell, this.d);
this.rows.setCell(sri, sci, cell);
});
}
@ -826,7 +856,7 @@ export default class DataProxy {
const fary = Array.from(fset);
const oldAry = Array.from(fset);
if (sort) {
fary.sort((a, b) => {
fary.sort((a: number, b: number) => {
if (sort.order === 'asc') return a - b;
if (sort.order === 'desc') return b - a;
return 0;
@ -905,9 +935,8 @@ export default class DataProxy {
}
});
}
// console.log('type:', type, ', si:', si, ', size:', size);
merges.shift(type, si, -size, (ri, ci, rn, cn) => {
// console.log('ri:', ri, ', ci:', ci, ', rn:', rn, ', cn:', cn);
const cell = rows.getCell(ri, ci);
cell.merge[0] += rn;
cell.merge[1] += cn;
@ -922,7 +951,7 @@ export default class DataProxy {
const { scroll, freeze, cols } = this;
const [, fci] = freeze;
const [ci, left, width] = helper.rangeReduceIf(fci, cols.len, 0, 0, x, (i) => cols.getWidth(i));
// console.log('fci:', fci, ', ci:', ci);
let x1 = left;
if (x > 0) x1 += width;
if (scroll.x !== x1) {
@ -938,7 +967,7 @@ export default class DataProxy {
const [ri, top, height] = helper.rangeReduceIf(fri, rows.len, 0, 0, y, (i) => rows.getHeight(i));
let y1 = top;
if (y > 0) y1 += height;
// console.log('ri:', ri, ' ,y:', y1);
if (scroll.y !== y1) {
scroll.ri = y > 0 ? ri : 0;
scroll.y = y1;
@ -956,7 +985,7 @@ export default class DataProxy {
if (cell !== null) {
if (cell.merge) {
const [rn, cn] = cell.merge;
// console.log('cell.merge:', cell.merge);
if (rn > 0) {
for (let i = 1; i <= rn; i += 1) {
height += rows.getHeight(ri + i);
@ -969,7 +998,7 @@ export default class DataProxy {
}
}
}
// console.log('data:', this.d);
return {
left,
top,
@ -1098,7 +1127,7 @@ export default class DataProxy {
viewRange() {
const { scroll, rows, cols, freeze, exceptRowSet } = this;
// console.log('scroll:', scroll, ', freeze:', freeze)
let { ri, ci } = scroll;
if (ri <= 0) [ri] = freeze;
if (ci <= 0) [, ci] = freeze;
@ -1117,7 +1146,7 @@ export default class DataProxy {
eci = j;
if (x > this.viewWidth()) break;
}
// console.log(ri, ci, eri, eci, x, y);
return new CellRange(ri, ci, eri, eci, x, y);
}
@ -1157,7 +1186,7 @@ export default class DataProxy {
offset += 1;
}
}
// console.log('min:', min, ', max:', max, ', scroll:', scroll);
for (let i = min + offset; i <= max + offset; i += 1) {
if (frset.has(i)) {
offset += 1;
@ -1191,7 +1220,7 @@ export default class DataProxy {
addStyle(nstyle) {
const { styles } = this;
// console.log('old.styles:', styles, nstyle);
for (let i = 0; i < styles.length; i += 1) {
const style = styles[i];
if (helper.equals(style, nstyle)) return i;

View File

@ -1,10 +1,7 @@
// docs
import './_.prototypes';
/** default font list
* @type {BaseFont[]}
*/
const baseFonts = [
const baseFonts: BaseFont[] = [
{ key: 'Arial', title: 'Arial' },
{ key: 'Helvetica', title: 'Helvetica' },
{ key: 'Source Sans Pro', title: 'Source Sans Pro' },
@ -14,10 +11,20 @@ const baseFonts = [
{ key: 'Lato', title: 'Lato' },
];
/** default fontSize list
* @type {FontSize[]}
*/
const fontSizes = [
export interface FontSize {
pt: number;
px: number;
}
export interface BaseFont {
key: string;
title: string;
}
export type FontsizePX = number;
export type FontsizePT = number;
const fontSizes: FontSize[] = [
{ pt: 7.5, px: 10 },
{ pt: 8, px: 11 },
{ pt: 9, px: 12 },
@ -41,10 +48,10 @@ const fontSizes = [
/** map pt to px
* @date 2019-10-10
* @param {fontsizePT} pt
* @returns {fontsizePX}
* @param {FontsizePT} pt
* @returns {FontsizePX}
*/
function getFontSizePxByPt(pt) {
function getFontSizePxByPt(pt: FontsizePT): FontsizePX {
for (let i = 0; i < fontSizes.length; i += 1) {
const fontSize = fontSizes[i];
if (fontSize.pt === pt) {
@ -59,7 +66,7 @@ function getFontSizePxByPt(pt) {
* @param {BaseFont[]} [ary=[]]
* @returns {object}
*/
function fonts(ary = []) {
function fonts(ary: BaseFont[] = []) {
const map = {};
baseFonts.concat(ary).forEach((f) => {
map[f.key] = f;

View File

@ -1,20 +1,13 @@
/**
formula:
key
title
render
*/
/**
* @typedef {object} Formula
* @property {string} key
* @property {function} title
* @property {function} render
*/
import { tf } from '../locale/locale';
import { numberCalc } from './helper';
/** @type {Formula[]} */
const baseFormulas = [
export interface Formula {
key: string;
title: Function;
render: Function;
}
const baseFormulas: Formula[] = [
{
key: 'SUM',
title: tf('formula.sum'),

View File

@ -1,5 +1,4 @@
/* eslint-disable no-param-reassign */
function cloneDeep(obj) {
function cloneDeep(obj: any) {
return JSON.parse(JSON.stringify(obj));
}
@ -7,7 +6,7 @@ const mergeDeep = (object = {}, ...sources) => {
sources.forEach((source) => {
Object.keys(source).forEach((key) => {
const v = source[key];
// console.log('k:', key, ', v:', source[key], typeof v, v instanceof Object);
if (typeof v === 'string' || typeof v === 'number' || typeof v === 'boolean') {
object[key] = v;
} else if (typeof v !== 'function' && !Array.isArray(v) && v instanceof Object) {
@ -18,7 +17,7 @@ const mergeDeep = (object = {}, ...sources) => {
}
});
});
// console.log('::', object);
return object;
};
@ -48,7 +47,7 @@ function equals(obj1, obj2) {
objOrAry: obejct or Array
cb: (value, index | key) => { return value }
*/
const sum = (objOrAry, cb = (value) => value) => {
const sum = (objOrAry, cb = (value, key: number | string) => value) => {
let total = 0;
let size = 0;
Object.keys(objOrAry).forEach((key) => {
@ -144,4 +143,21 @@ export default {
rangeReduceIf,
deleteProperty,
numberCalc,
};
}; /**
* @desc throttle fn
* @param func function
* @param wait Delay in milliseconds
*/
export function throttle(func: Function, wait: number) {
let timeout: ReturnType<typeof setTimeout>;
return (...arg: any) => {
const that = this;
const args = arg;
if (!timeout) {
timeout = setTimeout(() => {
timeout = null;
func.apply(that, args);
}, wait);
}
};
}

View File

@ -1,6 +1,6 @@
// import helper from '../helper';
export default class History {
undoItems: any[];
redoItems: any[];
constructor() {
this.undoItems = [];
this.redoItems = [];

View File

@ -1,6 +1,7 @@
import { CellRange } from './cell_range';
class Merges {
_: any[];
constructor(d = []) {
this._ = d;
}
@ -31,7 +32,6 @@ class Merges {
for (let i = 0; i < this._.length; i += 1) {
const it = this._[i];
if (it.intersects(cellRange)) {
// console.log('intersects');
return true;
}
}

View File

@ -1,7 +1,10 @@
import { expr2expr } from './alphabet';
import helper from './helper';
import { expr2expr } from './alphabet';
class Rows {
len: number;
height: number;
_: {};
constructor({ len, height }) {
this._ = {};
this.len = len;
@ -49,7 +52,7 @@ class Rows {
row.style = style;
}
sumHeight(min, max, exceptSet) {
sumHeight(min, max, exceptSet?) {
return helper.rangeSum(min, max, (i) => {
if (exceptSet && exceptSet.has(i)) return 0;
return this.getHeight(i);
@ -110,7 +113,7 @@ class Rows {
}
// what: all | format | text
copyPaste(srcCellRange, dstCellRange, what, autofill = false, cb = () => {}) {
copyPaste(srcCellRange, dstCellRange, what, autofill = false, cb = (nri, nci, ncell) => {}) {
const { sri, sci, eri, eci } = srcCellRange;
const dsri = dstCellRange.sri;
const dsci = dstCellRange.sci;
@ -118,7 +121,6 @@ class Rows {
const deci = dstCellRange.eci;
const [rn, cn] = srcCellRange.size();
const [drn, dcn] = dstCellRange.size();
// console.log(srcIndexes, dstIndexes);
let isAdd = true;
let dn = 0;
if (deri < sri || deci < sci) {
@ -160,7 +162,6 @@ class Rows {
(rn <= 1 && cn <= 1)
) {
const result = /[\\.\d]+$/.exec(text);
// console.log('result:', result);
if (result !== null) {
const index = Number(result[0]) + n - 1;
ncell.text = text.substring(0, result.index) + index;

View File

@ -1,8 +0,0 @@
export default class Scroll {
constructor() {
this.x = 0; // left
this.y = 0; // top
this.ri = 0; // cell row-index
this.ci = 0; // cell col-index
}
}

View File

@ -0,0 +1,7 @@
export default class Scroll {
x = 0; // left
y = 0; // top
ri = 0; // cell row-index
ci = 0; // cell col-index
constructor() {}
}

View File

@ -1,6 +1,9 @@
import { CellRange } from './cell_range';
export default class Selector {
range: CellRange;
ri: number;
ci: number;
constructor() {
this.range = new CellRange(0, 0, 0, 0);
this.ri = 0;
@ -11,7 +14,7 @@ export default class Selector {
return this.range.multiple();
}
setIndexes(ri, ci) {
setIndexes(ri: number, ci: number) {
this.ri = ri;
this.ci = ci;
}

View File

@ -1,14 +1,14 @@
import { CellRange } from './cell_range';
import Validator from './validator';
import { CellRange } from './cell_range';
class Validation {
constructor(mode, refs, validator) {
this.refs = refs;
this.mode = mode; // cell
this.validator = validator;
}
constructor(
public mode,
public refs: string[],
public validator,
) {}
includes(ri, ci) {
includes(ri: number, ci: number) {
const { refs } = this;
for (let i = 0; i < refs.length; i += 1) {
const cr = CellRange.valueOf(refs[i]);
@ -17,12 +17,12 @@ class Validation {
return false;
}
addRef(ref) {
addRef(ref: string) {
this.remove(CellRange.valueOf(ref));
this.refs.push(ref);
}
remove(cellRange) {
remove(cellRange: CellRange) {
const nrefs = [];
this.refs.forEach((it) => {
const cr = CellRange.valueOf(it);
@ -54,17 +54,18 @@ class Validation {
}
}
class Validations {
_: any[];
errors: Map<any, any>;
constructor() {
this._ = [];
// ri_ci: errMessage
this.errors = new Map();
}
getError(ri, ci) {
getError(ri: number, ci: number) {
return this.errors.get(`${ri}_${ci}`);
}
validate(ri, ci, text) {
validate(ri: number, ci: number, text: string) {
const v = this.get(ri, ci);
const key = `${ri}_${ci}`;
const { errors } = this;

View File

@ -1,4 +1,5 @@
import { t } from '../locale/locale';
import { FilterOperator } from './auto_filter';
import helper from './helper';
const rules = {
@ -9,19 +10,22 @@ const rules = {
function returnMessage(flag, key, ...arg) {
let message = '';
if (!flag) {
// @ts-ignore
message = t(`validation.${key}`, ...arg);
}
return [flag, message];
}
export default class Validator {
message: string;
// operator: b|nb|eq|neq|lt|lte|gt|gte
// type: date|number|list|phone|email
constructor(type, required, value, operator) {
this.required = required;
this.value = value;
this.type = type;
this.operator = operator;
constructor(
public type,
public required,
public value,
public operator: FilterOperator,
) {
this.message = '';
}

View File

@ -1,32 +1,32 @@
@css-prefix: x-spreadsheet;
// color
@red-color: #db2828;
@red-color: #DB2828;
@red-hover-color: #d01919;
@orange-color: #f2711c;
@orange-color: #F2711C;
@orange-hover-color: #f26202;
@yellow-color: #fbbd08;
@yellow-color: #FBBD08;
@yellow-hover-color: #eaae00;
@olive-color: #b5cc18;
@olive-color: #B5CC18;
@olive-hover-color: #a7bd0d;
@green-color: #21ba45;
@green-color: #21BA45;
@green-hover-color: #16ab39;
@teal-color: #00b5ad;
@teal-color: #00B5AD;
@teal-hover-color: #009c95;
@blue-color: #2185d0;
@blue-color: #2185D0;
@blue-hover-color: #1678c2;
@violet-color: #6435c9;
@violet-color: #6435C9;
@violet-hover-color: #5829bb;
@purple-color: #a333c8;
@purple-color: #A333C8;
@purple-hover-color: #9627ba;
@pink-color: #e03997;
@pink-color: #E03997;
@pink-hover-color: #e61a8d;
@brown-color: #a5673f;
@brown-color: #A5673F;
@brown-hover-color: #975b33;
@grey-color: #767676;
@grey-hover-color: #838383;
@dark-color: #343a40;
@dark-hover-color: darken(@dark-color, 10%);
@black-color: #1b1c1d;
@black-color: #1B1C1D;
@black-hover-color: #27292a;
// base
@ -37,7 +37,7 @@
@border: 1px solid @border-color;
@input-border: @border;
@input-padding: 0.5em 0.75em;
@input-box-shadow: inset 0 1px 2px hsla(0, 0%, 4%, 0.06);
@input-box-shadow: inset 0 1px 2px hsla(0,0%,4%,.06);
@border-radius: 2px;
@form-field-height: 30px;
@primary-color: @blue-color;
@ -47,8 +47,7 @@
.type-primary() {
color: #fff;
background-color: @primary-color;
&:hover,
&.active {
&:hover, &.active {
color: #fff;
background-color: @primary-hover-color;
}
@ -69,13 +68,7 @@ body {
-webkit-font-smoothing: antialiased;
textarea {
font:
400 13px Arial,
'Lato',
'Source Sans Pro',
Roboto,
Helvetica,
sans-serif;
font: 400 13px Arial, 'Lato', 'Source Sans Pro', Roboto, Helvetica, sans-serif;
}
}
@ -97,21 +90,22 @@ body {
border-radius: 1px;
background: rgba(0, 0, 0, 1);
font-size: 12px;
z-index: 201;
// z-index 这个现在是混乱的
z-index: 1201;
&:before {
pointer-events: none;
position: absolute;
left: calc(50% - 4px);
top: -4px;
content: '';
content: "";
width: 8px;
height: 8px;
background: inherit;
-webkit-transform: rotate(45deg);
transform: rotate(45deg);
z-index: 1;
box-shadow: 1px 1px 3px -1px rgba(0, 0, 0, 0.3);
box-shadow: 1px 1px 3px -1px rgba(0, 0, 0, .3);
}
}
@ -169,7 +163,7 @@ body {
text-align: center;
.@{css-prefix}-icon-img {
opacity: 0.8;
opacity: .8;
}
&:hover {
@ -191,7 +185,9 @@ body {
left: 0;
top: -3px;
}
}
}
.@{css-prefix}-dropdown {
@ -201,7 +197,7 @@ body {
position: absolute;
z-index: 200;
background: #fff;
box-shadow: 1px 2px 5px 2px rgba(51, 51, 51, 0.15);
box-shadow: 1px 2px 5px 2px rgba(51,51,51,.15);
}
&.bottom-left {
@ -232,6 +228,7 @@ body {
}
}
.@{css-prefix}-dropdown-title {
padding: 0 5px;
display: inline-block;
@ -256,7 +253,7 @@ body {
z-index: 11;
.@{css-prefix}-resizer-hover {
background-color: rgba(75, 137, 255, 0.25);
background-color: rgba(75, 137, 255, .25);
}
.@{css-prefix}-resizer-line {
position: absolute;
@ -325,8 +322,7 @@ body {
}
}
.@{css-prefix}-editor,
.@{css-prefix}-selector {
.@{css-prefix}-editor, .@{css-prefix}-selector {
box-sizing: content-box;
position: absolute;
overflow: hidden;
@ -345,17 +341,16 @@ body {
input {
padding: 0;
width: 0;
border: none !important;
border: none!important;
}
}
.@{css-prefix}-selector-area {
position: absolute;
border: 2px solid rgb(75, 137, 255);
background: rgba(75, 137, 255, 0.1);
background: rgba(75, 137, 255, .1);
z-index: 5;
}
.@{css-prefix}-selector-clipboard,
.@{css-prefix}-selector-autofill {
.@{css-prefix}-selector-clipboard, .@{css-prefix}-selector-autofill {
position: absolute;
background: transparent;
z-index: 100;
@ -364,7 +359,7 @@ body {
border: 2px dashed rgb(75, 137, 255);
}
.@{css-prefix}-selector-autofill {
border: 1px dashed rgba(0, 0, 0, 0.45); // #606060; // rgba(0, 0, 0, .2);
border: 1px dashed rgba(0, 0, 0, .45); // #606060; // rgba(0, 0, 0, .2);
}
.@{css-prefix}-selector-corner {
pointer-events: auto;
@ -397,13 +392,7 @@ body {
resize: none;
text-align: start;
overflow-y: hidden;
font:
400 13px Arial,
'Lato',
'Source Sans Pro',
Roboto,
Helvetica,
sans-serif;
font: 400 13px Arial, 'Lato', 'Source Sans Pro', Roboto, Helvetica, sans-serif;
color: inherit;
white-space: normal;
word-wrap: break-word;
@ -427,7 +416,7 @@ body {
border: 1px solid transparent;
outline: none;
height: 26px;
color: rgba(0, 0, 0, 0.9);
color: rgba(0, 0, 0, .9);
line-height: 26px;
list-style: none;
padding: 2px 10px;
@ -440,13 +429,12 @@ body {
opacity: 0.5;
}
&:hover,
&.active {
background: rgba(0, 0, 0, 0.05);
&:hover, &.active {
background: rgba(0, 0, 0, .05);
}
// &.active {
//// background: #89aef53d;
//// background: #89aef53d;
// }
&.divider {
@ -454,12 +442,12 @@ body {
padding: 0;
margin: 5px 0;
border: none;
border-bottom: 1px solid rgba(0, 0, 0, 0.1);
border-bottom: 1px solid rgba(0, 0, 0, .1);
}
.label {
float: right;
opacity: 0.65;
opacity: .65;
font-size: 1em;
}
}
@ -467,7 +455,7 @@ body {
.x-spreadsheet-item,
.x-spreadsheet-header {
&.state {
padding-left: 35px !important;
padding-left: 35px!important;
position: relative;
&:before {
@ -501,6 +489,7 @@ body {
// transform: rotate(45deg);
// -webkit-transform: rotate(45deg);
}
}
.@{css-prefix}-checkbox {
@ -516,7 +505,7 @@ body {
position: absolute;
top: 0;
left: 0;
opacity: 0 !important;
opacity: 0!important;
outline: 0;
z-index: -1;
}
@ -544,7 +533,7 @@ body {
margin: 10px;
.@{css-prefix}-header {
padding: 0.5em 0.75em;
padding: .5em .75em;
background: #f8f8f9;
border-bottom: 1px solid #e9e9e9;
border-left: 1px solid transparent;
@ -566,8 +555,7 @@ body {
}
}
.@{css-prefix}-toolbar,
.@{css-prefix}-bottombar {
.@{css-prefix}-toolbar, .@{css-prefix}-bottombar {
height: 40px;
padding: 0 30px;
text-align: left;
@ -598,7 +586,7 @@ body {
.@{css-prefix}-menu > li {
float: left;
line-height: 1.25em;
padding: 0.785em 1em;
padding: .785em 1em;
margin: 0;
vertical-align: middle;
text-align: left;
@ -606,12 +594,12 @@ body {
color: #80868b;
white-space: nowrap;
cursor: pointer;
transition: all 0.3s;
transition: all .3s;
font-weight: bold;
&.active {
background-color: #fff;
color: rgba(0, 0, 0, 0.65);
color: rgba(0, 0, 0, .65);
}
.@{css-prefix}-icon {
@ -619,7 +607,7 @@ body {
.@{css-prefix}-icon-img {
&:hover {
opacity: 0.85;
opacity: .85;
}
}
}
@ -661,9 +649,8 @@ body {
opacity: 0.5;
}
&:hover,
&.active {
background: rgba(0, 0, 0, 0.08);
&:hover, &.active {
background: rgba(0, 0, 0, .08);
}
}
}
@ -727,6 +714,7 @@ body {
}
}
.@{css-prefix}-canvas-card-wraper {
margin: 40px 20px;
}
@ -735,14 +723,11 @@ body {
margin: auto;
page-break-before: auto;
page-break-after: always;
box-shadow:
0 8px 10px 1px rgba(0, 0, 0, 0.14),
0 3px 14px 3px rgba(0, 0, 0, 0.12),
0 4px 5px 0 rgba(0, 0, 0, 0.2);
box-shadow: 0 8px 10px 1px rgba(0,0,0,0.14), 0 3px 14px 3px rgba(0,0,0,0.12), 0 4px 5px 0 rgba(0,0,0,0.20);
}
.@{css-prefix}-calendar {
color: rgba(0, 0, 0, 0.65);
color: rgba(0,0,0,.65);
background: #ffffff;
user-select: none;
@ -768,7 +753,7 @@ body {
border-radius: 2px;
}
a:hover {
background: rgba(0, 0, 0, 0.08);
background: rgba(0,0,0,.08);
}
}
}
@ -777,8 +762,7 @@ body {
border-collapse: collapse;
border-spacing: 0;
th,
td {
th, td {
width: 100%/7;
min-width: 32px;
text-align: center;
@ -794,10 +778,9 @@ body {
background: #ecf6fd;
}
&.active,
&.active:hover {
&.active, &.active:hover {
background: #ecf6fd;
color: #2185d0;
color: #2185D0;
}
&.disabled {
@ -809,7 +792,7 @@ body {
}
.@{css-prefix}-datepicker {
box-shadow: 2px 2px 5px rgba(0, 0, 0, 0.2);
box-shadow: 2px 2px 5px rgba(0,0,0,.2);
position: absolute;
left: 0;
top: calc(100% + 5px);
@ -835,11 +818,11 @@ body {
cursor: pointer;
font-size: 1em;
font-weight: 700;
padding: 0.75em 1em;
color: rgba(0, 0, 0, 0.6);
background: #e0e1e2;
padding: .75em 1em;
color: rgba(0,0,0,.6);
background: #E0E1E2;
text-decoration: none;
font-family: 'Lato', 'proxima-nova', 'Helvetica Neue', Arial, sans-serif;
font-family: "Lato","proxima-nova","Helvetica Neue",Arial,sans-serif;
//box-shadow: 0px 1px 2px -1px rgba(255,255,255,0.5) inset, 0px -2px 0px 0px rgba(0,0,0,0.1) inset;
//box-shadow: 0 0 0 0 rgba(0,0,0,.5) inset;
outline: none;
@ -848,10 +831,9 @@ body {
user-select: none;
transition: all 0.1s linear;
&.active,
&:hover {
background-color: #c0c1c2;
color: rgba(0, 0, 0, 0.8);
&.active, &:hover {
background-color: #C0C1C2;
color: rgba(0,0,0,.8);
}
&.primary {
@ -865,7 +847,7 @@ body {
position: relative;
font-weight: 400;
display: inline-flex;
color: rgba(0, 0, 0, 0.87);
color: rgba(0,0,0,.87);
input {
z-index: 1;
@ -873,7 +855,7 @@ body {
max-width: 100%;
flex: 1 0 auto;
outline: 0;
-webkit-tap-highlight-color: rgba(255, 255, 255, 0);
-webkit-tap-highlight-color: rgba(255,255,255,0);
text-align: left;
line-height: @form-field-height;
height: @form-field-height;
@ -881,14 +863,12 @@ body {
background: #fff;
border: 1px solid #e9e9e9;
border-radius: 3px;
transition:
box-shadow 0.1s ease,
border-color 0.1s ease;
box-shadow: inset 0 1px 2px hsla(0, 0%, 4%, 0.06);
transition: box-shadow .1s ease,border-color .1s ease;
box-shadow: inset 0 1px 2px hsla(0,0%,4%,.06);
&:focus {
border-color: rgb(75, 137, 255);
box-shadow: inset 0 1px 2px rgba(75, 137, 255, 0.2);
box-shadow: inset 0 1px 2px rgba(75, 137, 255, .2);
}
}
}
@ -900,9 +880,9 @@ body {
border: @input-border;
border-radius: 2px;
cursor: pointer;
color: rgba(0, 0, 0, 0.87);
color: rgba(0,0,0,.87);
user-select: none;
box-shadow: inset 0 1px 2px hsla(0, 0%, 4%, 0.06);
box-shadow: inset 0 1px 2px hsla(0,0%,4%,.06);
.input-text {
text-overflow: ellipsis;
@ -938,15 +918,14 @@ body {
}
&.error {
.@{css-prefix}-form-select,
input {
.@{css-prefix}-form-select, input {
border-color: #f04134;
}
}
.tip {
color: #f04134;
font-size: 0.9em;
font-size: .9em;
}
}
// form end
@ -980,7 +959,7 @@ form fieldset {
label {
display: block;
margin-bottom: 0.5em;
margin-bottom: .5em;
font-size: 1em;
color: #666;
}
@ -991,44 +970,42 @@ form fieldset {
background-color: #fff;
border: none;
border-bottom: 2px solid #ddd;
padding: 0.5em 0.85em;
padding: .5em .85em;
border-radius: 2px;
}
}
.@{css-prefix}-modal,
.@{css-prefix}-toast {
.@{css-prefix}-modal, .@{css-prefix}-toast {
font-size: 13px;
position: fixed;
z-index: 1001;
text-align: left;
line-height: @line-height;
min-width: 360px;
color: rgba(0, 0, 0, 0.87);
color: rgba(0,0,0,.87);
font-family: 'Lato', 'Source Sans Pro', Roboto, Helvetica, Arial, sans-serif;
border-radius: 4px;
border: 1px solid rgba(0, 0, 0, 0.1);
border: 1px solid rgba(0,0,0,.1);
background-color: #fff;
background-clip: padding-box;
box-shadow: rgba(0, 0, 0, 0.2) 0px 2px 8px;
}
.@{css-prefix}-toast {
background-color: rgba(255, 255, 255, 0.85);
background-color: rgba(255,255,255,.85);
}
.@{css-prefix}-modal-header,
.@{css-prefix}-toast-header {
.@{css-prefix}-modal-header, .@{css-prefix}-toast-header {
font-weight: 600;
background-clip: padding-box;
background-color: rgba(255, 255, 255, 0.85);
border-bottom: 1px solid rgba(0, 0, 0, 0.05);
background-color: rgba(255,255,255,.85);
border-bottom: 1px solid rgba(0,0,0,.05);
border-radius: 4px 4px 0 0;
.@{css-prefix}-icon {
position: absolute;
right: 0.8em;
top: 0.65em;
right: .8em;
top: .65em;
border-radius: 18px;
&:hover {
@ -1044,7 +1021,7 @@ form fieldset {
.@{css-prefix}-modal-header {
border-bottom: @border-style;
background: rgba(0, 0, 0, 0.08);
background: rgba(0, 0, 0, .08);
font-size: 1.0785em;
}
@ -1052,7 +1029,7 @@ form fieldset {
.@{css-prefix}-modal-content,
.@{css-prefix}-toast-header,
.@{css-prefix}-toast-content {
padding: 0.75em 1em;
padding: .75em 1em;
}
@media screen and (min-width: 320px) and (max-width: 480px) {

View File

@ -1,12 +1,19 @@
import React from 'react';
import { createRoot, Root } from 'react-dom/client';
import Bottombar from './component/bottombar';
import { h, HComponent } from './component/element';
import Sheet from './component/sheet';
import { Bottombar as RBottombar } from './components/Bottombar';
import { cssPrefix } from './config';
import DataProxy from './core/data_proxy';
import { locale } from './locale/locale';
import './index.less';
import { SheetRoot } from './components/SheetRoot';
export type CELL_SELECTED = 'cell-selected';
export type CELLS_SELECTED = 'cells-selected';
export type CELL_EDITED = 'cell-edited';
@ -148,11 +155,11 @@ class Spreadsheet {
sheet: any;
data: DataProxy;
rootEl: HComponent;
root: Root;
constructor(selectors: string | HTMLElement, options: Options = {}) {
this.options = { showBottomBar: true, ...options };
this.sheetIndex = 1;
this.datas = [];
if (typeof selectors === 'string') {
this.targetEl = document.querySelector(selectors) as HTMLElement;
} else {
@ -179,17 +186,32 @@ class Spreadsheet {
)
: null;
this.data = this.addSheet();
this.rootEl = h('div', `${cssPrefix}`).on('contextmenu', (evt) => evt.preventDefault());
// create canvas element
this.targetEl.appendChild(this.rootEl.el);
this.sheet = new Sheet(this.rootEl, this.data);
if (this.bottombar !== null) {
this.rootEl.child(this.bottombar.el);
}
this.root = createRoot(this.targetEl);
this.root.render(<SheetRoot options={this.options} />);
// this.rootEl = h('div', `${cssPrefix}`).on('contextmenu', (evt: Event) => {
// evt.preventDefault();
// evt.stopPropagation();
// });
// // create canvas element
// console.log('🚀 ~ file: index.ts:183 ~ Spreadsheet ~ constructor ~ rootEl:', this.rootEl);
// this.targetEl.appendChild(this.rootEl.el);
// console.log('🚀 ~ file: index.ts:183 ~ Spreadsheet ~ constructor ~ this.targetEl:', this.targetEl);
// this.sheet = new Sheet(this.rootEl, this.data);
// if (this.bottombar !== null) {
// // this.rootEl.child(this.bottombar.el);
// const bottomDiv = document.createElement('div');
// this.targetEl.appendChild(bottomDiv);
// const bottomRoot = createRoot(bottomDiv);
// bottomRoot.render(<RBottombar />);
// console.log('🚀 ~ file: index.tsx:191 ~ Spreadsheet ~ constructor ~ bottombar:', this.bottombar);
// }
}
dispose() {
this.targetEl.removeChild(this.rootEl.el);
// this.targetEl.removeChild(this.rootEl.el);
this.root.unmount();
console.debug('x-sheet is diposed');
}
@ -200,7 +222,6 @@ class Spreadsheet {
this.sheet.trigger('change', ...args);
};
this.datas.push(d);
// console.log('d:', n, d, this.datas);
if (this.bottombar !== null) {
this.bottombar.addItem(n, active, this.options);
}
@ -231,7 +252,7 @@ class Spreadsheet {
const nd = this.addSheet(it.name, i === 0);
nd.setData(it);
if (i === 0) {
this.sheet.resetData(nd);
// this.sheet.resetData(nd);
}
}
}

View File

@ -39,7 +39,10 @@ function translate(key, messages) {
function t(key) {
let v = translate(key, $messages);
// FIXME
// @ts-ignore
if (!v && window && window.x_spreadsheet && window.x_spreadsheet.$messages) {
// @ts-ignore
v = translate(key, window.x_spreadsheet.$messages);
}
return v || '';

Some files were not shown because too many files have changed in this diff Show More