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 { 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 { interface Attr {
align?: 'left' | 'center' | 'right'; align?: 'left' | 'center' | 'right';
@ -159,7 +163,6 @@ export class Draw {
const { ctx } = this; const { ctx } = this;
ctx.lineWidth = thinLineWidth(); ctx.lineWidth = thinLineWidth();
ctx.strokeStyle = color; ctx.strokeStyle = color;
// console.log('style:', style);
if (style === 'medium') { if (style === 'medium') {
ctx.lineWidth = npx(2) - 0.5; ctx.lineWidth = npx(2) - 0.5;
} else if (style === 'thick') { } 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 { h } from './element';
import Icon from './icon'; import Icon from './icon';
import DropdownColor from './dropdown_color';
import DropdownLineType from './dropdown_linetype';
import { cssPrefix } from '../config';
function buildTable(...trs) { function buildTable(...trs) {
return h('table', '').child(h('tbody', '').children(...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 { cssPrefix } from '../config';
import Icon from './icon';
import FormInput from './form_input';
import Dropdown from './dropdown';
// Record: temp not used // Record: temp not used
// import { xtoast } from './message'; // import { xtoast } from './message';
import { tf } from '../locale/locale'; 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 { class DropdownMore extends Dropdown {
constructor(click) { constructor(click) {
@ -32,7 +32,10 @@ class DropdownMore extends Dropdown {
setTitle() {} 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) { function buildMenuItem(item) {
return h('div', `${cssPrefix}-item`) return h('div', `${cssPrefix}-item`)

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,7 +1,7 @@
import { cssPrefix } from '../config';
import Dropdown from './dropdown'; import Dropdown from './dropdown';
import { h } from './element'; import { h } from './element';
import Icon from './icon'; import Icon from './icon';
import { cssPrefix } from '../config';
function buildItemWithIcon(iconName) { function buildItemWithIcon(iconName) {
return h('div', `${cssPrefix}-item`).child(new Icon(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 Dropdown from './dropdown';
import Icon from './icon'; import Icon from './icon';
import BorderPalette from './border_palette';
export default class DropdownBorder extends Dropdown { export default class DropdownBorder extends Dropdown {
constructor() { constructor() {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,11 +1,11 @@
import { cssPrefix } from '../config'; import Modal from './modal';
import { t } from '../locale/locale';
import Button from './button';
import { h } from './element';
import FormField from './form_field';
import FormInput from './form_input'; import FormInput from './form_input';
import FormSelect from './form_select'; 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; const fieldLabelWidth = 100;
@ -146,9 +146,8 @@ export default class ModalValidation extends Modal {
const attrs = ['mf', 'rf', 'cf', 'of', 'svf', 'vf', 'minvf', 'maxvf']; const attrs = ['mf', 'rf', 'cf', 'of', 'svf', 'vf', 'minvf', 'maxvf'];
for (let i = 0; i < attrs.length; i += 1) { for (let i = 0; i < attrs.length; i += 1) {
const field = this[attrs[i]]; const field = this[attrs[i]];
// console.log('field:', field);
if (field.isShow()) { if (field.isShow()) {
// console.log('it:', it);
if (!field.validate()) return; if (!field.validate()) return;
} }
} }
@ -165,7 +164,7 @@ export default class ModalValidation extends Modal {
value = this.vf.val(); value = this.vf.val();
} }
} }
// console.log(mode, ref, type, operator, value);
this.change('save', mode, ref, { this.change('save', mode, ref, {
type, type,
operator, 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 { h } from './element';
import { cssPrefix } from '../config';
import Button from './button';
import { Draw } from '../canvas';
import { renderCell } from './table'; import { renderCell } from './table';
import { t } from '../locale/locale';
// resolution: 72 => 595 x 842 // resolution: 72 => 595 x 842
// 150 => 1240 x 1754 // 150 => 1240 x 1754
@ -22,8 +21,8 @@ const PAGER_SIZES = [
const PAGER_ORIENTATIONS = ['landscape', 'portrait']; const PAGER_ORIENTATIONS = ['landscape', 'portrait'];
function inches2px(inc) { function inches2px(inc: number) {
return parseInt(96 * inc, 10); return parseInt(String(96 * inc), 10);
} }
function btnClick(type) { function btnClick(type) {
@ -38,9 +37,8 @@ function pagerSizeChange(evt) {
const { paper } = this; const { paper } = this;
const { value } = evt.target; const { value } = evt.target;
const ps = PAGER_SIZES[value]; const ps = PAGER_SIZES[value];
paper.w = inches2px(ps[1]); paper.w = inches2px(ps[1] as number);
paper.h = inches2px(ps[2]); paper.h = inches2px(ps[2] as number);
// console.log('paper:', ps, paper);
this.preview(); this.preview();
} }
function pagerOrientationChange(evt) { function pagerOrientationChange(evt) {
@ -52,11 +50,15 @@ function pagerOrientationChange(evt) {
} }
export default class Print { export default class Print {
constructor(data) { paper;
el;
contentEl;
canvases;
constructor(private data) {
this.paper = { this.paper = {
w: inches2px(PAGER_SIZES[0][1]), w: inches2px(PAGER_SIZES[0][1] as number),
h: inches2px(PAGER_SIZES[0][2]), h: inches2px(PAGER_SIZES[0][2] as number),
padding: 50, padding: 0,
orientation: PAGER_ORIENTATIONS[0], orientation: PAGER_ORIENTATIONS[0],
get width() { get width() {
return this.orientation === 'landscape' ? this.h : this.w; return this.orientation === 'landscape' ? this.h : this.w;
@ -65,7 +67,6 @@ export default class Print {
return this.orientation === 'landscape' ? this.w : this.h; return this.orientation === 'landscape' ? this.w : this.h;
}, },
}; };
this.data = data;
this.el = h('div', `${cssPrefix}-print`) this.el = h('div', `${cssPrefix}-print`)
.children( .children(
h('div', `${cssPrefix}-print-bar`).children( h('div', `${cssPrefix}-print-bar`).children(
@ -120,7 +121,7 @@ export default class Print {
const iwidth = width - padding * 2; const iwidth = width - padding * 2;
const iheight = height - padding * 2; const iheight = height - padding * 2;
const cr = data.contentRange(); 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; const scale = iwidth / cr.w;
let left = padding; let left = padding;
const top = padding; const top = padding;
@ -143,12 +144,12 @@ export default class Print {
const wrap = h('div', `${cssPrefix}-canvas-card`); const wrap = h('div', `${cssPrefix}-canvas-card`);
const canvas = h('canvas', `${cssPrefix}-canvas`); const canvas = h('canvas', `${cssPrefix}-canvas`);
this.canvases.push(canvas.el); 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 // cell-content
draw.save(); draw.save();
draw.translate(left, top); draw.translate(left, top);
if (scale < 1) draw.scale(scale, scale); if (scale < 1) draw.scale(scale, scale);
// console.log('ri:', ri, cr.eri, yoffset);
for (; ri <= cr.eri; ri += 1) { for (; ri <= cr.eri; ri += 1) {
const rh = data.rows.getHeight(ri); const rh = data.rows.getHeight(ri);
th += rh; th += rh;
@ -188,13 +189,14 @@ export default class Print {
const iframe = h('iframe', '').hide(); const iframe = h('iframe', '').hide();
const { el } = iframe; const { el } = iframe;
window.document.body.appendChild(el); window.document.body.appendChild(el);
const { contentWindow } = el; // FIXME 可以用泛型
const { contentWindow } = el as HTMLIFrameElement;
const idoc = contentWindow.document; const idoc = contentWindow.document;
const style = document.createElement('style'); const style = document.createElement('style');
style.innerHTML = ` style.innerHTML = `
@page { size: ${paper.width}px ${paper.height}px; }; @page { size: ${paper.width}px ${paper.height}px; };
canvas { canvas {
page-break-before: auto; page-break-before: auto;
page-break-after: always; page-break-after: always;
image-rendering: pixelated; image-rendering: pixelated;
}; };

View File

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

View File

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

View File

@ -1,6 +1,6 @@
import { h } from './element';
import { cssPrefix } from '../config'; import { cssPrefix } from '../config';
import { CellRange } from '../core/cell_range'; import { CellRange } from '../core/cell_range';
import { h } from './element';
const selectorHeightBorderWidth = 2 * 2 - 1; const selectorHeightBorderWidth = 2 * 2 - 1;
let startZIndex = 10; let startZIndex = 10;
@ -128,7 +128,7 @@ function calLAreaOffset(offset) {
const { top, width, height, l, t, scroll } = offset; const { top, width, height, l, t, scroll } = offset;
const ftheight = data.freezeTotalHeight(); const ftheight = data.freezeTotalHeight();
let top0 = top - ftheight; let top0 = top - ftheight;
// console.log('ftheight:', ftheight, ', t:', t);
if (ftheight > t) top0 -= scroll.y; if (ftheight > t) top0 -= scroll.y;
return { return {
left: l, left: l,
@ -247,7 +247,6 @@ export default class Selector {
} }
resetAreaOffset() { resetAreaOffset() {
// console.log('offset:', offset);
const offset = this.data.getSelectedRect(); const offset = this.data.getSelectedRect();
const coffset = this.data.getClipboardRect(); const coffset = this.data.getClipboardRect();
setAllAreaOffset.call(this, offset); setAllAreaOffset.call(this, offset);
@ -307,14 +306,13 @@ export default class Selector {
} }
reset() { reset() {
// console.log('::::', this.data);
const { eri, eci } = this.data.selector.range; const { eri, eci } = this.data.selector.range;
this.setEnd(eri, eci); this.setEnd(eri, eci);
} }
showAutofill(ri, ci) { showAutofill(ri, ci) {
if (ri === -1 && ci === -1) return; if (ri === -1 && ci === -1) return;
// console.log('ri:', ri, ', ci:', ci);
// const [sri, sci] = this.sIndexes; // const [sri, sci] = this.sIndexes;
// const [eri, eci] = this.eIndexes; // const [eri, eci] = this.eIndexes;
const { sri, sci, eri, eci } = this.range; const { sri, sci, eri, eci } = this.range;
@ -327,41 +325,39 @@ export default class Selector {
const ecn = eci - ci; const ecn = eci - ci;
if (scn > 0) { if (scn > 0) {
// left // left
// console.log('left');
this.arange = new CellRange(sri, nci, eri, sci - 1); this.arange = new CellRange(sri, nci, eri, sci - 1);
// this.saIndexes = [sri, nci]; // this.saIndexes = [sri, nci];
// this.eaIndexes = [eri, sci - 1]; // this.eaIndexes = [eri, sci - 1];
// data.calRangeIndexes2( // data.calRangeIndexes2(
} else if (srn > 0) { } else if (srn > 0) {
// top // top
// console.log('top');
// nri = sri; // nri = sri;
this.arange = new CellRange(nri, sci, sri - 1, eci); this.arange = new CellRange(nri, sci, sri - 1, eci);
// this.saIndexes = [nri, sci]; // this.saIndexes = [nri, sci];
// this.eaIndexes = [sri - 1, eci]; // this.eaIndexes = [sri - 1, eci];
} else if (ecn < 0) { } else if (ecn < 0) {
// right // right
// console.log('right');
// nci = eci; // nci = eci;
this.arange = new CellRange(sri, eci + 1, eri, nci); this.arange = new CellRange(sri, eci + 1, eri, nci);
// this.saIndexes = [sri, eci + 1]; // this.saIndexes = [sri, eci + 1];
// this.eaIndexes = [eri, nci]; // this.eaIndexes = [eri, nci];
} else if (ern < 0) { } else if (ern < 0) {
// bottom // bottom
// console.log('bottom');
// nri = eri; // nri = eri;
this.arange = new CellRange(eri + 1, sci, nri, eci); this.arange = new CellRange(eri + 1, sci, nri, eci);
// this.saIndexes = [eri + 1, sci]; // this.saIndexes = [eri + 1, sci];
// this.eaIndexes = [nri, eci]; // this.eaIndexes = [nri, eci];
} else { } else {
// console.log('else:');
this.arange = null; this.arange = null;
// this.saIndexes = null; // this.saIndexes = null;
// this.eaIndexes = null; // this.eaIndexes = null;
return; return;
} }
if (this.arange !== null) { if (this.arange !== null) {
// console.log(this.saIndexes, ':', this.eaIndexes);
const offset = this.data.getRect(this.arange); const offset = this.data.getRect(this.arange);
offset.width += 2; offset.width += 2;
offset.height += 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 { h } from './element';
import { bind, bindTouch, createEventEmitter, mouseMoveUp } from './event'; import { bind, mouseMoveUp, bindTouch, createEventEmitter } from './event';
import { xtoast } from './message';
import ModalValidation from './modal_validation';
import Print from './print';
import Resizer from './resizer'; import Resizer from './resizer';
import Scrollbar from './scrollbar'; import Scrollbar from './scrollbar';
import Selector from './selector'; 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 Table from './table';
import Toolbar from './toolbar/index'; import Toolbar from './toolbar/index';
import ModalValidation from './modal_validation';
/** import SortFilter from './sort_filter';
* @desc throttle fn import { xtoast } from './message';
* @param func function import { cssPrefix } from '../config';
* @param wait Delay in milliseconds import { formulas } from '../core/formula';
*/ import { throttle } from '../core/helper';
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);
}
};
}
function scrollbarMove() { function scrollbarMove() {
const { data, verticalScrollbar, horizontalScrollbar } = this; const { data, verticalScrollbar, horizontalScrollbar } = this;
const { l, t, left, top, width, height } = data.getSelectedRect(); const { l, t, left, top, width, height } = data.getSelectedRect();
const tableOffset = this.getTableOffset(); const tableOffset = this.getTableOffset();
// console.log(',l:', l, ', left:', left, ', tOffset.left:', tableOffset.width);
if (Math.abs(left) + width > tableOffset.width) { if (Math.abs(left) + width > tableOffset.width) {
horizontalScrollbar.move({ left: l + width - tableOffset.width }); horizontalScrollbar.move({ left: l + width - tableOffset.width });
} else { } else {
@ -47,7 +28,7 @@ function scrollbarMove() {
horizontalScrollbar.move({ left: l - 1 - fsw }); horizontalScrollbar.move({ left: l - 1 - fsw });
} }
} }
// console.log('top:', top, ', height:', height, ', tof.height:', tableOffset.height);
if (Math.abs(top) + height > tableOffset.height) { if (Math.abs(top) + height > tableOffset.height) {
verticalScrollbar.move({ top: t + height - tableOffset.height - 1 }); verticalScrollbar.move({ top: t + height - tableOffset.height - 1 });
} else { } else {
@ -85,7 +66,7 @@ function selectorMove(multiple, direction) {
if (multiple) { if (multiple) {
[ri, ci] = selector.moveIndexes; [ri, ci] = selector.moveIndexes;
} }
// console.log('selector.move:', ri, ci);
if (direction === 'left') { if (direction === 'left') {
if (ci > 0) ci -= 1; if (ci > 0) ci -= 1;
} else if (direction === 'right') { } else if (direction === 'right') {
@ -114,7 +95,6 @@ function selectorMove(multiple, direction) {
// private methods // private methods
function overlayerMousemove(evt) { function overlayerMousemove(evt) {
// console.log('x:', evt.offsetX, ', y:', evt.offsetY);
if (evt.buttons !== 0) return; if (evt.buttons !== 0) return;
if (evt.target.className === `${cssPrefix}-resizer-hover`) return; if (evt.target.className === `${cssPrefix}-resizer-hover`) return;
const { offsetX, offsetY } = evt; const { offsetX, offsetY } = evt;
@ -164,7 +144,6 @@ function overlayerMousescroll(evt) {
const { verticalScrollbar, horizontalScrollbar, data } = this; const { verticalScrollbar, horizontalScrollbar, data } = this;
const { top } = verticalScrollbar.scroll(); const { top } = verticalScrollbar.scroll();
const { left } = horizontalScrollbar.scroll(); const { left } = horizontalScrollbar.scroll();
// console.log('evt:::', evt.wheelDelta, evt.detail * 40);
const { rows, cols } = data; const { rows, cols } = data;
@ -179,7 +158,7 @@ function overlayerMousescroll(evt) {
} while (v <= 0); } while (v <= 0);
return v; return v;
}; };
// console.log('deltaX', deltaX, 'evt.detail', evt.detail);
// if (evt.detail) deltaY = evt.detail * 40; // if (evt.detail) deltaY = evt.detail * 40;
const moveY = (vertical) => { const moveY = (vertical) => {
if (vertical > 0) { if (vertical > 0) {
@ -220,7 +199,7 @@ function overlayerMousescroll(evt) {
const tempY = Math.abs(deltaY); const tempY = Math.abs(deltaY);
const tempX = Math.abs(deltaX); const tempX = Math.abs(deltaX);
const temp = Math.max(tempY, tempX); const temp = Math.max(tempY, tempX);
// console.log('event:', evt);
// detail for windows/mac firefox vertical scroll // detail for windows/mac firefox vertical scroll
if (/Firefox/i.test(window.navigator.userAgent)) throttle(moveY(evt.detail), 50); if (/Firefox/i.test(window.navigator.userAgent)) throttle(moveY(evt.detail), 50);
if (temp === tempX) throttle(moveX(deltaX), 50); if (temp === tempX) throttle(moveX(deltaX), 50);
@ -243,7 +222,7 @@ function verticalScrollbarSet() {
const { data, verticalScrollbar } = this; const { data, verticalScrollbar } = this;
const { height } = this.getTableOffset(); const { height } = this.getTableOffset();
const erth = data.exceptRowTotalHeight(0, -1); const erth = data.exceptRowTotalHeight(0, -1);
// console.log('erth:', erth);
verticalScrollbar.set(height, data.rows.totalHeight() - erth); verticalScrollbar.set(height, data.rows.totalHeight() - erth);
} }
@ -349,8 +328,6 @@ function toolbarChangePaintformatPaste() {
} }
function overlayerMousedown(evt) { 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 { offsetX, offsetY } = evt;
const isAutofillEl = evt.target.className === `${cssPrefix}-selector-corner`; const isAutofillEl = evt.target.className === `${cssPrefix}-selector-corner`;
@ -369,9 +346,7 @@ function overlayerMousedown(evt) {
} }
} }
// console.log('ri:', ri, ', ci:', ci);
if (!evt.shiftKey) { if (!evt.shiftKey) {
// console.log('selectorSetStart:::');
if (isAutofillEl) { if (isAutofillEl) {
selector.showAutofill(ri, ci); selector.showAutofill(ri, ci);
} else { } else {
@ -382,7 +357,6 @@ function overlayerMousedown(evt) {
mouseMoveUp( mouseMoveUp(
window, window,
(e) => { (e) => {
// console.log('mouseMoveUp::::');
({ ri, ci } = data.getCellRectByXY(e.offsetX, e.offsetY)); ({ ri, ci } = data.getCellRectByXY(e.offsetX, e.offsetY));
if (isAutofillEl) { if (isAutofillEl) {
selector.showAutofill(ri, ci); selector.showAutofill(ri, ci);
@ -404,7 +378,6 @@ function overlayerMousedown(evt) {
if (!isAutofillEl && evt.buttons === 1) { if (!isAutofillEl && evt.buttons === 1) {
if (evt.shiftKey) { if (evt.shiftKey) {
// console.log('shiftKey::::');
selectorSet.call(this, true, ri, ci); selectorSet.call(this, true, ri, ci);
} }
} }
@ -415,7 +388,7 @@ function editorSetOffset() {
const sOffset = data.getSelectedRect(); const sOffset = data.getSelectedRect();
const tOffset = this.getTableOffset(); const tOffset = this.getTableOffset();
let sPosition = 'top'; let sPosition = 'top';
// console.log('sOffset:', sOffset, ':', tOffset);
if (sOffset.top > tOffset.height / 2) { if (sOffset.top > tOffset.height / 2) {
sPosition = 'bottom'; sPosition = 'bottom';
} }
@ -564,7 +537,6 @@ function toolbarChange(type, value) {
} }
function sortFilterChange(ci, order, operator, value) { function sortFilterChange(ci, order, operator, value) {
// console.log('sort:', sortDesc, operator, value);
this.data.setAutoFilter(ci, order, operator, value); this.data.setAutoFilter(ci, order, operator, value);
sheetReset.call(this); sheetReset.call(this);
} }
@ -669,7 +641,6 @@ function sheetInitEvents() {
}; };
// contextmenu // contextmenu
contextMenu.itemClick = (type) => { contextMenu.itemClick = (type) => {
// console.log('type:', type);
if (type === 'validation') { if (type === 'validation') {
modalValidation.setValue(this.data.getSelectedValidation()); modalValidation.setValue(this.data.getSelectedValidation());
} else if (type === 'copy') { } else if (type === 'copy') {
@ -714,7 +685,7 @@ function sheetInitEvents() {
if (!this.focusing) return; if (!this.focusing) return;
const keyCode = evt.keyCode || evt.which; 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) { if (ctrlKey || metaKey) {
// const { sIndexes, eIndexes } = selector; // const { sIndexes, eIndexes } = selector;
// let what = 'all'; // let what = 'all';
@ -789,7 +760,6 @@ function sheetInitEvents() {
break; break;
} }
} else { } else {
// console.log('evt.keyCode:', evt.keyCode);
switch (keyCode) { switch (keyCode) {
case 32: case 32:
if (shiftKey) { 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 { cssPrefix } from '../config';
import { t } from '../locale/locale'; import { t } from '../locale/locale';
import Button from './button';
import { h } from './element';
import { bindClickoutside, unbindClickoutside } from './event';
function buildMenu(clsName) { function buildMenu(clsName) {
return h('div', `${cssPrefix}-item ${clsName}`); return h('div', `${cssPrefix}-item ${clsName}`);
@ -51,7 +51,6 @@ export default class SortFilter {
), ),
) )
.hide(); .hide();
// this.setFilters(['test1', 'test2', 'text3']);
this.ci = null; this.ci = null;
this.sortDesc = null; this.sortDesc = null;
this.values = null; this.values = null;
@ -69,7 +68,6 @@ export default class SortFilter {
} }
itemClick(it) { itemClick(it) {
// console.log('it:', it);
this.sort = it; this.sort = it;
const { sortAscEl, sortDescEl } = this; const { sortAscEl, sortDescEl } = this;
sortAscEl.checked(it === 'asc'); sortAscEl.checked(it === 'asc');
@ -77,7 +75,6 @@ export default class SortFilter {
} }
filterClick(index, it) { filterClick(index, it) {
// console.log('index:', index, it);
const { filterbEl, filterValues, values } = this; const { filterbEl, filterValues, values } = this;
const children = filterbEl.children(); const children = filterbEl.children();
if (it === 'all') { if (it === 'all') {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,9 +1,8 @@
/* global document */
import { cssPrefix } from '../config';
import { h } from './element'; import { h } from './element';
import { bind } from './event'; 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')) { if (target.classList.contains('active')) {
return; return;
} }
@ -11,7 +10,6 @@ export default function tooltip(html, target) {
const el = h('div', `${cssPrefix}-tooltip`).html(html).show(); const el = h('div', `${cssPrefix}-tooltip`).html(html).show();
document.body.appendChild(el.el); document.body.appendChild(el.el);
const elBox = el.box(); const elBox = el.box();
// console.log('elBox:', elBox);
el.css('left', `${left + width / 2 - elBox.width / 2}px`).css('top', `${top + height + 2}px`); el.css('left', `${left + width / 2 - elBox.width / 2}px`).css('top', `${top + height + 2}px`);
bind(target, 'mouseleave', () => { 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 cssPrefix = 'x-spreadsheet';
export const dpr = window.devicePixelRatio || 1; export const dpr = window.devicePixelRatio || 1;
export default { 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 = [ const alphabets = [
'A', 'A',
@ -36,13 +37,13 @@ const alphabets = [
* @param {number} index * @param {number} index
* @returns {string} * @returns {string}
*/ */
export function stringAt(index) { export function stringAt(index: number): string {
let str = ''; let str = '';
let cindex = index; let cindex = index;
while (cindex >= alphabets.length) { while (cindex >= alphabets.length) {
cindex /= alphabets.length; cindex /= alphabets.length;
cindex -= 1; cindex -= 1;
str += alphabets[parseInt(cindex, 10) % alphabets.length]; str += alphabets[parseInt(String(cindex), 10) % alphabets.length];
} }
const last = index % alphabets.length; const last = index % alphabets.length;
str += alphabets[last]; str += alphabets[last];
@ -55,20 +56,19 @@ export function stringAt(index) {
* @param {string} str "AA" in A1-tag "AA1" * @param {string} str "AA" in A1-tag "AA1"
* @returns {number} * @returns {number}
*/ */
export function indexAt(str) { export function indexAt(str: TagA1) {
let ret = 0; let ret = 0;
for (let i = 0; i !== str.length; ++i) ret = 26 * ret + str.charCodeAt(i) - 64; for (let i = 0; i !== str.length; ++i) ret = 26 * ret + str.charCodeAt(i) - 64;
return ret - 1; return ret - 1;
} }
// B10 => x,y
/** translate A1-tag to XY-tag /** translate A1-tag to XY-tag
* @date 2019-10-10 * @date 2019-10-10
* @export * @export
* @param {tagA1} src * @param {TagA1} src
* @returns {tagXY} * @returns {TagXY}
*/ */
export function expr2xy(src) { export function expr2xy(src: TagA1): TagXY {
let x = ''; let x = '';
let y = ''; let y = '';
for (let i = 0; i < src.length; i += 1) { for (let i = 0; i < src.length; i += 1) {
@ -87,21 +87,21 @@ export function expr2xy(src) {
* @export * @export
* @param {number} x * @param {number} x
* @param {number} y * @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}`; return `${stringAt(x)}${y + 1}`;
} }
/** translate A1-tag src by (xn, yn) /** translate A1-tag src by (xn, yn)
* @date 2019-10-10 * @date 2019-10-10
* @export * @export
* @param {tagA1} src * @param {TagA1} src
* @param {number} xn * @param {number} xn
* @param {number} yn * @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; if (xn === 0 && yn === 0) return src;
const [x, y] = expr2xy(src); const [x, y] = expr2xy(src);
if (!condition(x, y)) return src; if (!condition(x, y)) return src;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,5 +1,4 @@
/* eslint-disable no-param-reassign */ function cloneDeep(obj: any) {
function cloneDeep(obj) {
return JSON.parse(JSON.stringify(obj)); return JSON.parse(JSON.stringify(obj));
} }
@ -7,7 +6,7 @@ const mergeDeep = (object = {}, ...sources) => {
sources.forEach((source) => { sources.forEach((source) => {
Object.keys(source).forEach((key) => { Object.keys(source).forEach((key) => {
const v = source[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') { if (typeof v === 'string' || typeof v === 'number' || typeof v === 'boolean') {
object[key] = v; object[key] = v;
} else if (typeof v !== 'function' && !Array.isArray(v) && v instanceof Object) { } else if (typeof v !== 'function' && !Array.isArray(v) && v instanceof Object) {
@ -18,7 +17,7 @@ const mergeDeep = (object = {}, ...sources) => {
} }
}); });
}); });
// console.log('::', object);
return object; return object;
}; };
@ -48,7 +47,7 @@ function equals(obj1, obj2) {
objOrAry: obejct or Array objOrAry: obejct or Array
cb: (value, index | key) => { return value } 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 total = 0;
let size = 0; let size = 0;
Object.keys(objOrAry).forEach((key) => { Object.keys(objOrAry).forEach((key) => {
@ -144,4 +143,21 @@ export default {
rangeReduceIf, rangeReduceIf,
deleteProperty, deleteProperty,
numberCalc, 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 { export default class History {
undoItems: any[];
redoItems: any[];
constructor() { constructor() {
this.undoItems = []; this.undoItems = [];
this.redoItems = []; this.redoItems = [];

View File

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

View File

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

View File

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

View File

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

View File

@ -1,32 +1,32 @@
@css-prefix: x-spreadsheet; @css-prefix: x-spreadsheet;
// color // color
@red-color: #db2828; @red-color: #DB2828;
@red-hover-color: #d01919; @red-hover-color: #d01919;
@orange-color: #f2711c; @orange-color: #F2711C;
@orange-hover-color: #f26202; @orange-hover-color: #f26202;
@yellow-color: #fbbd08; @yellow-color: #FBBD08;
@yellow-hover-color: #eaae00; @yellow-hover-color: #eaae00;
@olive-color: #b5cc18; @olive-color: #B5CC18;
@olive-hover-color: #a7bd0d; @olive-hover-color: #a7bd0d;
@green-color: #21ba45; @green-color: #21BA45;
@green-hover-color: #16ab39; @green-hover-color: #16ab39;
@teal-color: #00b5ad; @teal-color: #00B5AD;
@teal-hover-color: #009c95; @teal-hover-color: #009c95;
@blue-color: #2185d0; @blue-color: #2185D0;
@blue-hover-color: #1678c2; @blue-hover-color: #1678c2;
@violet-color: #6435c9; @violet-color: #6435C9;
@violet-hover-color: #5829bb; @violet-hover-color: #5829bb;
@purple-color: #a333c8; @purple-color: #A333C8;
@purple-hover-color: #9627ba; @purple-hover-color: #9627ba;
@pink-color: #e03997; @pink-color: #E03997;
@pink-hover-color: #e61a8d; @pink-hover-color: #e61a8d;
@brown-color: #a5673f; @brown-color: #A5673F;
@brown-hover-color: #975b33; @brown-hover-color: #975b33;
@grey-color: #767676; @grey-color: #767676;
@grey-hover-color: #838383; @grey-hover-color: #838383;
@dark-color: #343a40; @dark-color: #343a40;
@dark-hover-color: darken(@dark-color, 10%); @dark-hover-color: darken(@dark-color, 10%);
@black-color: #1b1c1d; @black-color: #1B1C1D;
@black-hover-color: #27292a; @black-hover-color: #27292a;
// base // base
@ -37,7 +37,7 @@
@border: 1px solid @border-color; @border: 1px solid @border-color;
@input-border: @border; @input-border: @border;
@input-padding: 0.5em 0.75em; @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; @border-radius: 2px;
@form-field-height: 30px; @form-field-height: 30px;
@primary-color: @blue-color; @primary-color: @blue-color;
@ -47,8 +47,7 @@
.type-primary() { .type-primary() {
color: #fff; color: #fff;
background-color: @primary-color; background-color: @primary-color;
&:hover, &:hover, &.active {
&.active {
color: #fff; color: #fff;
background-color: @primary-hover-color; background-color: @primary-hover-color;
} }
@ -69,13 +68,7 @@ body {
-webkit-font-smoothing: antialiased; -webkit-font-smoothing: antialiased;
textarea { textarea {
font: font: 400 13px Arial, 'Lato', 'Source Sans Pro', Roboto, Helvetica, sans-serif;
400 13px Arial,
'Lato',
'Source Sans Pro',
Roboto,
Helvetica,
sans-serif;
} }
} }
@ -97,21 +90,22 @@ body {
border-radius: 1px; border-radius: 1px;
background: rgba(0, 0, 0, 1); background: rgba(0, 0, 0, 1);
font-size: 12px; font-size: 12px;
z-index: 201; // z-index 这个现在是混乱的
z-index: 1201;
&:before { &:before {
pointer-events: none; pointer-events: none;
position: absolute; position: absolute;
left: calc(50% - 4px); left: calc(50% - 4px);
top: -4px; top: -4px;
content: ''; content: "";
width: 8px; width: 8px;
height: 8px; height: 8px;
background: inherit; background: inherit;
-webkit-transform: rotate(45deg); -webkit-transform: rotate(45deg);
transform: rotate(45deg); transform: rotate(45deg);
z-index: 1; 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; text-align: center;
.@{css-prefix}-icon-img { .@{css-prefix}-icon-img {
opacity: 0.8; opacity: .8;
} }
&:hover { &:hover {
@ -191,7 +185,9 @@ body {
left: 0; left: 0;
top: -3px; top: -3px;
} }
} }
} }
.@{css-prefix}-dropdown { .@{css-prefix}-dropdown {
@ -201,7 +197,7 @@ body {
position: absolute; position: absolute;
z-index: 200; z-index: 200;
background: #fff; 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 { &.bottom-left {
@ -232,6 +228,7 @@ body {
} }
} }
.@{css-prefix}-dropdown-title { .@{css-prefix}-dropdown-title {
padding: 0 5px; padding: 0 5px;
display: inline-block; display: inline-block;
@ -256,7 +253,7 @@ body {
z-index: 11; z-index: 11;
.@{css-prefix}-resizer-hover { .@{css-prefix}-resizer-hover {
background-color: rgba(75, 137, 255, 0.25); background-color: rgba(75, 137, 255, .25);
} }
.@{css-prefix}-resizer-line { .@{css-prefix}-resizer-line {
position: absolute; position: absolute;
@ -325,8 +322,7 @@ body {
} }
} }
.@{css-prefix}-editor, .@{css-prefix}-editor, .@{css-prefix}-selector {
.@{css-prefix}-selector {
box-sizing: content-box; box-sizing: content-box;
position: absolute; position: absolute;
overflow: hidden; overflow: hidden;
@ -345,17 +341,16 @@ body {
input { input {
padding: 0; padding: 0;
width: 0; width: 0;
border: none !important; border: none!important;
} }
} }
.@{css-prefix}-selector-area { .@{css-prefix}-selector-area {
position: absolute; position: absolute;
border: 2px solid rgb(75, 137, 255); border: 2px solid rgb(75, 137, 255);
background: rgba(75, 137, 255, 0.1); background: rgba(75, 137, 255, .1);
z-index: 5; z-index: 5;
} }
.@{css-prefix}-selector-clipboard, .@{css-prefix}-selector-clipboard, .@{css-prefix}-selector-autofill {
.@{css-prefix}-selector-autofill {
position: absolute; position: absolute;
background: transparent; background: transparent;
z-index: 100; z-index: 100;
@ -364,7 +359,7 @@ body {
border: 2px dashed rgb(75, 137, 255); border: 2px dashed rgb(75, 137, 255);
} }
.@{css-prefix}-selector-autofill { .@{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 { .@{css-prefix}-selector-corner {
pointer-events: auto; pointer-events: auto;
@ -397,13 +392,7 @@ body {
resize: none; resize: none;
text-align: start; text-align: start;
overflow-y: hidden; overflow-y: hidden;
font: font: 400 13px Arial, 'Lato', 'Source Sans Pro', Roboto, Helvetica, sans-serif;
400 13px Arial,
'Lato',
'Source Sans Pro',
Roboto,
Helvetica,
sans-serif;
color: inherit; color: inherit;
white-space: normal; white-space: normal;
word-wrap: break-word; word-wrap: break-word;
@ -427,7 +416,7 @@ body {
border: 1px solid transparent; border: 1px solid transparent;
outline: none; outline: none;
height: 26px; height: 26px;
color: rgba(0, 0, 0, 0.9); color: rgba(0, 0, 0, .9);
line-height: 26px; line-height: 26px;
list-style: none; list-style: none;
padding: 2px 10px; padding: 2px 10px;
@ -440,13 +429,12 @@ body {
opacity: 0.5; opacity: 0.5;
} }
&:hover, &:hover, &.active {
&.active { background: rgba(0, 0, 0, .05);
background: rgba(0, 0, 0, 0.05);
} }
// &.active { // &.active {
//// background: #89aef53d; //// background: #89aef53d;
// } // }
&.divider { &.divider {
@ -454,12 +442,12 @@ body {
padding: 0; padding: 0;
margin: 5px 0; margin: 5px 0;
border: none; border: none;
border-bottom: 1px solid rgba(0, 0, 0, 0.1); border-bottom: 1px solid rgba(0, 0, 0, .1);
} }
.label { .label {
float: right; float: right;
opacity: 0.65; opacity: .65;
font-size: 1em; font-size: 1em;
} }
} }
@ -467,7 +455,7 @@ body {
.x-spreadsheet-item, .x-spreadsheet-item,
.x-spreadsheet-header { .x-spreadsheet-header {
&.state { &.state {
padding-left: 35px !important; padding-left: 35px!important;
position: relative; position: relative;
&:before { &:before {
@ -501,6 +489,7 @@ body {
// transform: rotate(45deg); // transform: rotate(45deg);
// -webkit-transform: rotate(45deg); // -webkit-transform: rotate(45deg);
} }
} }
.@{css-prefix}-checkbox { .@{css-prefix}-checkbox {
@ -516,7 +505,7 @@ body {
position: absolute; position: absolute;
top: 0; top: 0;
left: 0; left: 0;
opacity: 0 !important; opacity: 0!important;
outline: 0; outline: 0;
z-index: -1; z-index: -1;
} }
@ -544,7 +533,7 @@ body {
margin: 10px; margin: 10px;
.@{css-prefix}-header { .@{css-prefix}-header {
padding: 0.5em 0.75em; padding: .5em .75em;
background: #f8f8f9; background: #f8f8f9;
border-bottom: 1px solid #e9e9e9; border-bottom: 1px solid #e9e9e9;
border-left: 1px solid transparent; border-left: 1px solid transparent;
@ -566,8 +555,7 @@ body {
} }
} }
.@{css-prefix}-toolbar, .@{css-prefix}-toolbar, .@{css-prefix}-bottombar {
.@{css-prefix}-bottombar {
height: 40px; height: 40px;
padding: 0 30px; padding: 0 30px;
text-align: left; text-align: left;
@ -598,7 +586,7 @@ body {
.@{css-prefix}-menu > li { .@{css-prefix}-menu > li {
float: left; float: left;
line-height: 1.25em; line-height: 1.25em;
padding: 0.785em 1em; padding: .785em 1em;
margin: 0; margin: 0;
vertical-align: middle; vertical-align: middle;
text-align: left; text-align: left;
@ -606,12 +594,12 @@ body {
color: #80868b; color: #80868b;
white-space: nowrap; white-space: nowrap;
cursor: pointer; cursor: pointer;
transition: all 0.3s; transition: all .3s;
font-weight: bold; font-weight: bold;
&.active { &.active {
background-color: #fff; background-color: #fff;
color: rgba(0, 0, 0, 0.65); color: rgba(0, 0, 0, .65);
} }
.@{css-prefix}-icon { .@{css-prefix}-icon {
@ -619,7 +607,7 @@ body {
.@{css-prefix}-icon-img { .@{css-prefix}-icon-img {
&:hover { &:hover {
opacity: 0.85; opacity: .85;
} }
} }
} }
@ -661,9 +649,8 @@ body {
opacity: 0.5; opacity: 0.5;
} }
&:hover, &:hover, &.active {
&.active { background: rgba(0, 0, 0, .08);
background: rgba(0, 0, 0, 0.08);
} }
} }
} }
@ -727,6 +714,7 @@ body {
} }
} }
.@{css-prefix}-canvas-card-wraper { .@{css-prefix}-canvas-card-wraper {
margin: 40px 20px; margin: 40px 20px;
} }
@ -735,14 +723,11 @@ body {
margin: auto; margin: auto;
page-break-before: auto; page-break-before: auto;
page-break-after: always; page-break-after: always;
box-shadow: 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);
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);
} }
.@{css-prefix}-calendar { .@{css-prefix}-calendar {
color: rgba(0, 0, 0, 0.65); color: rgba(0,0,0,.65);
background: #ffffff; background: #ffffff;
user-select: none; user-select: none;
@ -768,7 +753,7 @@ body {
border-radius: 2px; border-radius: 2px;
} }
a:hover { a:hover {
background: rgba(0, 0, 0, 0.08); background: rgba(0,0,0,.08);
} }
} }
} }
@ -777,8 +762,7 @@ body {
border-collapse: collapse; border-collapse: collapse;
border-spacing: 0; border-spacing: 0;
th, th, td {
td {
width: 100%/7; width: 100%/7;
min-width: 32px; min-width: 32px;
text-align: center; text-align: center;
@ -794,10 +778,9 @@ body {
background: #ecf6fd; background: #ecf6fd;
} }
&.active, &.active, &.active:hover {
&.active:hover {
background: #ecf6fd; background: #ecf6fd;
color: #2185d0; color: #2185D0;
} }
&.disabled { &.disabled {
@ -809,7 +792,7 @@ body {
} }
.@{css-prefix}-datepicker { .@{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; position: absolute;
left: 0; left: 0;
top: calc(100% + 5px); top: calc(100% + 5px);
@ -835,11 +818,11 @@ body {
cursor: pointer; cursor: pointer;
font-size: 1em; font-size: 1em;
font-weight: 700; font-weight: 700;
padding: 0.75em 1em; padding: .75em 1em;
color: rgba(0, 0, 0, 0.6); color: rgba(0,0,0,.6);
background: #e0e1e2; background: #E0E1E2;
text-decoration: none; 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: 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; //box-shadow: 0 0 0 0 rgba(0,0,0,.5) inset;
outline: none; outline: none;
@ -848,10 +831,9 @@ body {
user-select: none; user-select: none;
transition: all 0.1s linear; transition: all 0.1s linear;
&.active, &.active, &:hover {
&:hover { background-color: #C0C1C2;
background-color: #c0c1c2; color: rgba(0,0,0,.8);
color: rgba(0, 0, 0, 0.8);
} }
&.primary { &.primary {
@ -865,7 +847,7 @@ body {
position: relative; position: relative;
font-weight: 400; font-weight: 400;
display: inline-flex; display: inline-flex;
color: rgba(0, 0, 0, 0.87); color: rgba(0,0,0,.87);
input { input {
z-index: 1; z-index: 1;
@ -873,7 +855,7 @@ body {
max-width: 100%; max-width: 100%;
flex: 1 0 auto; flex: 1 0 auto;
outline: 0; outline: 0;
-webkit-tap-highlight-color: rgba(255, 255, 255, 0); -webkit-tap-highlight-color: rgba(255,255,255,0);
text-align: left; text-align: left;
line-height: @form-field-height; line-height: @form-field-height;
height: @form-field-height; height: @form-field-height;
@ -881,14 +863,12 @@ body {
background: #fff; background: #fff;
border: 1px solid #e9e9e9; border: 1px solid #e9e9e9;
border-radius: 3px; border-radius: 3px;
transition: transition: box-shadow .1s ease,border-color .1s ease;
box-shadow 0.1s ease, box-shadow: inset 0 1px 2px hsla(0,0%,4%,.06);
border-color 0.1s ease;
box-shadow: inset 0 1px 2px hsla(0, 0%, 4%, 0.06);
&:focus { &:focus {
border-color: rgb(75, 137, 255); 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: @input-border;
border-radius: 2px; border-radius: 2px;
cursor: pointer; cursor: pointer;
color: rgba(0, 0, 0, 0.87); color: rgba(0,0,0,.87);
user-select: none; 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 { .input-text {
text-overflow: ellipsis; text-overflow: ellipsis;
@ -938,15 +918,14 @@ body {
} }
&.error { &.error {
.@{css-prefix}-form-select, .@{css-prefix}-form-select, input {
input {
border-color: #f04134; border-color: #f04134;
} }
} }
.tip { .tip {
color: #f04134; color: #f04134;
font-size: 0.9em; font-size: .9em;
} }
} }
// form end // form end
@ -980,7 +959,7 @@ form fieldset {
label { label {
display: block; display: block;
margin-bottom: 0.5em; margin-bottom: .5em;
font-size: 1em; font-size: 1em;
color: #666; color: #666;
} }
@ -991,44 +970,42 @@ form fieldset {
background-color: #fff; background-color: #fff;
border: none; border: none;
border-bottom: 2px solid #ddd; border-bottom: 2px solid #ddd;
padding: 0.5em 0.85em; padding: .5em .85em;
border-radius: 2px; border-radius: 2px;
} }
} }
.@{css-prefix}-modal, .@{css-prefix}-modal, .@{css-prefix}-toast {
.@{css-prefix}-toast {
font-size: 13px; font-size: 13px;
position: fixed; position: fixed;
z-index: 1001; z-index: 1001;
text-align: left; text-align: left;
line-height: @line-height; line-height: @line-height;
min-width: 360px; 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; font-family: 'Lato', 'Source Sans Pro', Roboto, Helvetica, Arial, sans-serif;
border-radius: 4px; border-radius: 4px;
border: 1px solid rgba(0, 0, 0, 0.1); border: 1px solid rgba(0,0,0,.1);
background-color: #fff; background-color: #fff;
background-clip: padding-box; background-clip: padding-box;
box-shadow: rgba(0, 0, 0, 0.2) 0px 2px 8px; box-shadow: rgba(0, 0, 0, 0.2) 0px 2px 8px;
} }
.@{css-prefix}-toast { .@{css-prefix}-toast {
background-color: rgba(255, 255, 255, 0.85); background-color: rgba(255,255,255,.85);
} }
.@{css-prefix}-modal-header, .@{css-prefix}-modal-header, .@{css-prefix}-toast-header {
.@{css-prefix}-toast-header {
font-weight: 600; font-weight: 600;
background-clip: padding-box; background-clip: padding-box;
background-color: rgba(255, 255, 255, 0.85); background-color: rgba(255,255,255,.85);
border-bottom: 1px solid rgba(0, 0, 0, 0.05); border-bottom: 1px solid rgba(0,0,0,.05);
border-radius: 4px 4px 0 0; border-radius: 4px 4px 0 0;
.@{css-prefix}-icon { .@{css-prefix}-icon {
position: absolute; position: absolute;
right: 0.8em; right: .8em;
top: 0.65em; top: .65em;
border-radius: 18px; border-radius: 18px;
&:hover { &:hover {
@ -1044,7 +1021,7 @@ form fieldset {
.@{css-prefix}-modal-header { .@{css-prefix}-modal-header {
border-bottom: @border-style; border-bottom: @border-style;
background: rgba(0, 0, 0, 0.08); background: rgba(0, 0, 0, .08);
font-size: 1.0785em; font-size: 1.0785em;
} }
@ -1052,7 +1029,7 @@ form fieldset {
.@{css-prefix}-modal-content, .@{css-prefix}-modal-content,
.@{css-prefix}-toast-header, .@{css-prefix}-toast-header,
.@{css-prefix}-toast-content { .@{css-prefix}-toast-content {
padding: 0.75em 1em; padding: .75em 1em;
} }
@media screen and (min-width: 320px) and (max-width: 480px) { @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 Bottombar from './component/bottombar';
import { h, HComponent } from './component/element'; import { h, HComponent } from './component/element';
import Sheet from './component/sheet'; import Sheet from './component/sheet';
import { Bottombar as RBottombar } from './components/Bottombar';
import { cssPrefix } from './config'; import { cssPrefix } from './config';
import DataProxy from './core/data_proxy'; import DataProxy from './core/data_proxy';
import { locale } from './locale/locale'; import { locale } from './locale/locale';
import './index.less'; import './index.less';
import { SheetRoot } from './components/SheetRoot';
export type CELL_SELECTED = 'cell-selected'; export type CELL_SELECTED = 'cell-selected';
export type CELLS_SELECTED = 'cells-selected'; export type CELLS_SELECTED = 'cells-selected';
export type CELL_EDITED = 'cell-edited'; export type CELL_EDITED = 'cell-edited';
@ -148,11 +155,11 @@ class Spreadsheet {
sheet: any; sheet: any;
data: DataProxy; data: DataProxy;
rootEl: HComponent; rootEl: HComponent;
root: Root;
constructor(selectors: string | HTMLElement, options: Options = {}) { constructor(selectors: string | HTMLElement, options: Options = {}) {
this.options = { showBottomBar: true, ...options }; this.options = { showBottomBar: true, ...options };
this.sheetIndex = 1; this.sheetIndex = 1;
this.datas = []; this.datas = [];
if (typeof selectors === 'string') { if (typeof selectors === 'string') {
this.targetEl = document.querySelector(selectors) as HTMLElement; this.targetEl = document.querySelector(selectors) as HTMLElement;
} else { } else {
@ -179,17 +186,32 @@ class Spreadsheet {
) )
: null; : null;
this.data = this.addSheet(); this.data = this.addSheet();
this.rootEl = h('div', `${cssPrefix}`).on('contextmenu', (evt) => evt.preventDefault());
// create canvas element this.root = createRoot(this.targetEl);
this.targetEl.appendChild(this.rootEl.el); this.root.render(<SheetRoot options={this.options} />);
this.sheet = new Sheet(this.rootEl, this.data);
if (this.bottombar !== null) { // this.rootEl = h('div', `${cssPrefix}`).on('contextmenu', (evt: Event) => {
this.rootEl.child(this.bottombar.el); // 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() { dispose() {
this.targetEl.removeChild(this.rootEl.el); // this.targetEl.removeChild(this.rootEl.el);
this.root.unmount();
console.debug('x-sheet is diposed'); console.debug('x-sheet is diposed');
} }
@ -200,7 +222,6 @@ class Spreadsheet {
this.sheet.trigger('change', ...args); this.sheet.trigger('change', ...args);
}; };
this.datas.push(d); this.datas.push(d);
// console.log('d:', n, d, this.datas);
if (this.bottombar !== null) { if (this.bottombar !== null) {
this.bottombar.addItem(n, active, this.options); this.bottombar.addItem(n, active, this.options);
} }
@ -231,7 +252,7 @@ class Spreadsheet {
const nd = this.addSheet(it.name, i === 0); const nd = this.addSheet(it.name, i === 0);
nd.setData(it); nd.setData(it);
if (i === 0) { if (i === 0) {
this.sheet.resetData(nd); // this.sheet.resetData(nd);
} }
} }
} }

View File

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

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