New features: serialize and load data

Fixed imports in modules
This commit is contained in:
Eugene 2023-07-25 12:33:25 +03:00
parent 73645c14f6
commit 11376f13ac
27 changed files with 182 additions and 64 deletions

View File

@ -14,4 +14,7 @@ const target = document.getElementById('spreadsheet')
const sheet = new Spreadsheet(target) const sheet = new Spreadsheet(target)
``` ```
<span>Load data</span>
</div> </div>

View File

@ -1,4 +1,4 @@
import { Spreadsheet } from "../main"; import Spreadsheet from "../main";
import { Position } from "../modules/cell"; import { Position } from "../modules/cell";
export declare class Editor { export declare class Editor {
element: HTMLInputElement; element: HTMLInputElement;

View File

@ -1,4 +1,4 @@
import { Spreadsheet } from "../main"; import Spreadsheet from "../main";
export declare class Header { export declare class Header {
element: HTMLHeadElement; element: HTMLHeadElement;
root: Spreadsheet; root: Spreadsheet;

View File

@ -1,4 +1,4 @@
import { Spreadsheet } from "../main"; import Spreadsheet from "../main";
export interface ViewportRect { export interface ViewportRect {
top: number; top: number;
left: number; left: number;

View File

@ -1,4 +1,4 @@
import { Spreadsheet } from "../main"; import Spreadsheet from "../main";
import { Position } from "../modules/cell"; import { Position } from "../modules/cell";
/** /**
* Display (CANVAS) element where cells render * Display (CANVAS) element where cells render

View File

@ -1,4 +1,4 @@
import { Spreadsheet } from "../main"; import Spreadsheet from "../main";
import { ViewProperties } from "../modules/config"; import { ViewProperties } from "../modules/config";
/** Base (root) component */ /** Base (root) component */
export declare class Table { export declare class Table {

View File

@ -1,4 +1,4 @@
import { Spreadsheet } from "../main"; import Spreadsheet from "../main";
export declare class Toolbar { export declare class Toolbar {
element: HTMLDivElement; element: HTMLDivElement;
root: Spreadsheet; root: Spreadsheet;

6
dist/main.cjs vendored

File diff suppressed because one or more lines are too long

2
dist/main.cjs.map vendored

File diff suppressed because one or more lines are too long

15
dist/main.d.ts vendored
View File

@ -9,7 +9,7 @@ interface SpreadsheetConstructorProperties {
config?: Omit<Config, 'view'>; config?: Omit<Config, 'view'>;
view?: ViewProperties; view?: ViewProperties;
} }
export declare class Spreadsheet { export default class Spreadsheet {
private table; private table;
private scroller; private scroller;
private toolbar; private toolbar;
@ -37,7 +37,16 @@ export declare class Spreadsheet {
showEditor(position: Position): void; showEditor(position: Position): void;
renderSheet(): void; renderSheet(): void;
renderCell(row: number, col: number): void; renderCell(row: number, col: number): void;
loadData(data: Cell[][]): void; loadData(data: Cell[][]): Spreadsheet;
private makeConfigFromData; private makeConfigFromData;
} }
export {}; export * from './modules/cache';
export * from './modules/cell';
export * from './modules/column';
export * from './modules/config';
export * from './modules/renderBox';
export * from './modules/row';
export * from './modules/selection';
export * from './modules/styles';
export * from './modules/viewport';
export * from './utils/createData';

76
dist/main.js vendored
View File

@ -1,6 +1,6 @@
var f = Object.defineProperty; var p = Object.defineProperty;
var C = (r, t, e) => t in r ? f(r, t, { enumerable: !0, configurable: !0, writable: !0, value: e }) : r[t] = e; var y = (r, t, e) => t in r ? p(r, t, { enumerable: !0, configurable: !0, writable: !0, value: e }) : r[t] = e;
var o = (r, t, e) => (C(r, typeof t != "symbol" ? t + "" : t, e), e); var o = (r, t, e) => (y(r, typeof t != "symbol" ? t + "" : t, e), e);
class w { class w {
constructor(t, e) { constructor(t, e) {
o(this, "x"); o(this, "x");
@ -22,7 +22,7 @@ class w {
return s; return s;
} }
} }
class p { class S {
constructor(t) { constructor(t) {
o(this, "element"); o(this, "element");
o(this, "root"); o(this, "root");
@ -56,7 +56,7 @@ class p {
this.element.classList.remove("hide"), this.element.style.top = n - this.root.viewport.top + "px", this.element.style.left = l - this.root.viewport.left + "px", this.element.style.width = s + "px", this.element.style.height = e + "px", this.element.style.display = "block", window.addEventListener("click", this.handleClickOutside), this.element.addEventListener("keydown", this.handleKeydown), this.element.value = i.value, this.element.focus(), this.element.select(); this.element.classList.remove("hide"), this.element.style.top = n - this.root.viewport.top + "px", this.element.style.left = l - this.root.viewport.left + "px", this.element.style.width = s + "px", this.element.style.height = e + "px", this.element.style.display = "block", window.addEventListener("click", this.handleClickOutside), this.element.addEventListener("keydown", this.handleKeydown), this.element.value = i.value, this.element.focus(), this.element.select();
} }
} }
class y { class v {
constructor(t) { constructor(t) {
o(this, "element"); o(this, "element");
o(this, "root"); o(this, "root");
@ -65,7 +65,7 @@ class y {
e.classList.add(), this.element = e; e.classList.add(), this.element = e;
} }
} }
class v { class x {
constructor(t) { constructor(t) {
o(this, "element"); o(this, "element");
o(this, "verticalScroller"); o(this, "verticalScroller");
@ -160,7 +160,7 @@ class v {
this.horizontalScroller.style.width = t + "px"; this.horizontalScroller.style.width = t + "px";
} }
} }
class S { class R {
constructor(t) { constructor(t) {
o(this, "fontSize", 16); o(this, "fontSize", 16);
o(this, "fontColor", "black"); o(this, "fontColor", "black");
@ -171,21 +171,21 @@ class S {
t && Object.assign(this, t); t && Object.assign(this, t);
} }
} }
class x { class b {
constructor(t, e) { constructor(t, e) {
o(this, "row"); o(this, "row");
o(this, "column"); o(this, "column");
this.row = t, this.column = e; this.row = t, this.column = e;
} }
} }
class R { class k {
constructor(t) { constructor(t) {
o(this, "value"); o(this, "value");
o(this, "displayValue"); o(this, "displayValue");
/** This refers to the values that were obtained by calculations, for example, after calculating the formula */ /** This refers to the values that were obtained by calculations, for example, after calculating the formula */
o(this, "resultValue"); o(this, "resultValue");
o(this, "position"); o(this, "position");
o(this, "style", new S()); o(this, "style", new R());
this.value = t.value, this.displayValue = t.displayValue, this.resultValue = t.resultValue, this.position = t.position; this.value = t.value, this.displayValue = t.displayValue, this.resultValue = t.resultValue, this.position = t.position;
} }
changeValues(t) { changeValues(t) {
@ -205,7 +205,7 @@ class R {
n -= t.viewport.top, l -= t.viewport.left, i.clearRect(l, n, s, e), i.fillStyle = c || h ? this.style.selectedBackground : this.style.background, i.strokeStyle = "black", i.fillRect(l, n, s - 1, e - 1), i.strokeRect(l, n, s, e), i.fillStyle = c || h ? this.style.selectedFontColor : this.style.fontColor, i.textAlign = "left", i.font = `${this.style.fontSize}px Arial`, i.textBaseline = "middle", i.fillText(this.displayValue, l + 2, n + e / 2); n -= t.viewport.top, l -= t.viewport.left, i.clearRect(l, n, s, e), i.fillStyle = c || h ? this.style.selectedBackground : this.style.background, i.strokeStyle = "black", i.fillRect(l, n, s - 1, e - 1), i.strokeRect(l, n, s, e), i.fillStyle = c || h ? this.style.selectedFontColor : this.style.fontColor, i.textAlign = "left", i.font = `${this.style.fontSize}px Arial`, i.textBaseline = "middle", i.fillText(this.displayValue, l + 2, n + e / 2);
} }
} }
class b { class E {
constructor(t) { constructor(t) {
o(this, "element"); o(this, "element");
o(this, "ctx"); o(this, "ctx");
@ -225,7 +225,7 @@ class b {
let n = 0, i = 0; let n = 0, i = 0;
for (; i <= t && (i += this.root.config.columns[n].width, !(i >= t)); ) for (; i <= t && (i += this.root.config.columns[n].width, !(i >= t)); )
n++; n++;
return new x(s, n); return new b(s, n);
} }
renderCell(t) { renderCell(t) {
const { column: e, row: s } = t; const { column: e, row: s } = t;
@ -238,7 +238,7 @@ class b {
this.renderCell({ column: i, row: n }); this.renderCell({ column: i, row: n });
} }
} }
class k { class I {
constructor(t) { constructor(t) {
o(this, "element"); o(this, "element");
o(this, "root"); o(this, "root");
@ -251,7 +251,7 @@ class k {
this.element.style.width = s + "px", this.element.style.height = e + "px"; this.element.style.width = s + "px", this.element.style.height = e + "px";
} }
} }
class E { class V {
constructor(t) { constructor(t) {
o(this, "element"); o(this, "element");
o(this, "root"); o(this, "root");
@ -271,13 +271,13 @@ class d {
this.columns = t.columns, this.rows = t.rows, this.view = t.view; this.columns = t.columns, this.rows = t.rows, this.view = t.view;
} }
} }
class I { class L {
constructor() { constructor() {
o(this, "selectedCell", null); o(this, "selectedCell", null);
o(this, "selectedRange", null); o(this, "selectedRange", null);
} }
} }
class V { class B {
} }
class u { class u {
constructor(t, e) { constructor(t, e) {
@ -332,12 +332,12 @@ class g {
this.height = t.height, this.title = t.title; this.height = t.height, this.title = t.title;
} }
} }
function L(r, t, e = !1) { function f(r, t, e = !1) {
const s = []; const s = [];
for (let l = 0; l <= r; l++) { for (let l = 0; l <= r; l++) {
const n = []; const n = [];
for (let i = 0; i <= t; i++) { for (let i = 0; i <= t; i++) {
const c = e ? `${l}:${i}` : "", h = new R({ const c = e ? `${l}:${i}` : "", h = new k({
displayValue: c, displayValue: c,
resultValue: c, resultValue: c,
value: c, value: c,
@ -352,7 +352,7 @@ function L(r, t, e = !1) {
} }
return s; return s;
} }
function B(r, t) { function C(r, t) {
const e = []; const e = [];
for (let n = 0; n <= r; n++) { for (let n = 0; n <= r; n++) {
const i = new g({ const i = new g({
@ -378,6 +378,10 @@ function B(r, t) {
} }
}); });
} }
function z(r, t) {
const e = f(r, t), s = C(r, t);
return { data: e, config: s };
}
class A { class A {
constructor(t) { constructor(t) {
o(this, "xPos"); o(this, "xPos");
@ -392,7 +396,7 @@ class M {
this.yPos = t.yPos, this.rowIdx = t.rowIdx; this.yPos = t.yPos, this.rowIdx = t.rowIdx;
} }
} }
class T { class D {
constructor(t) { constructor(t) {
o(this, "columns"); o(this, "columns");
o(this, "rows"); o(this, "rows");
@ -417,7 +421,7 @@ class T {
return e; return e;
} }
} }
class D { class F {
constructor(t, e) { constructor(t, e) {
o(this, "table"); o(this, "table");
o(this, "scroller"); o(this, "scroller");
@ -431,10 +435,10 @@ class D {
o(this, "viewport"); o(this, "viewport");
o(this, "selection"); o(this, "selection");
o(this, "cache"); o(this, "cache");
const s = B(500, 500); const s = C(500, 500);
e != null && e.view && (s.view = e.view), this.config = new d(s), this.sheet = new b(this); e != null && e.view && (s.view = e.view), this.config = new d(s), this.sheet = new E(this);
const l = L(500, 500); const l = f(500, 500);
this.table = new k(this), this.scroller = new v(this), this.toolbar = new E(this), this.header = new y(this), this.editor = new p(this), this.cache = this.getInitialCache(), this.viewport = new u(this, this.scroller.getViewportBoundlingRect()), this.selection = new I(), this.data = l, this.styles = new V(), this.buildComponent(), this.appendTableToTarget(t), this.renderSheet(); this.table = new I(this), this.scroller = new x(this), this.toolbar = new V(this), this.header = new v(this), this.editor = new S(this), this.cache = this.getInitialCache(), this.viewport = new u(this, this.scroller.getViewportBoundlingRect()), this.selection = new L(), this.data = l, this.styles = new B(), this.buildComponent(), this.appendTableToTarget(t), this.renderSheet();
} }
getInitialCache() { getInitialCache() {
const t = []; const t = [];
@ -459,7 +463,7 @@ class D {
}); });
s.push(h); s.push(h);
} }
const n = new T({ const n = new D({
columns: t, columns: t,
rows: s rows: s
}); });
@ -536,7 +540,7 @@ class D {
this.data[t][e].render(this); this.data[t][e].render(this);
} }
loadData(t) { loadData(t) {
this.data = t, this.config = this.makeConfigFromData(t, this.config.view), this.cache = this.getInitialCache(), this.scroller.updateScrollerSize(), this.viewport = new u(this, this.scroller.getViewportBoundlingRect()), this.renderSheet(); return this.data = t, this.config = this.makeConfigFromData(t, this.config.view), this.cache = this.getInitialCache(), this.scroller.updateScrollerSize(), this.viewport = new u(this, this.scroller.getViewportBoundlingRect()), this.renderSheet(), this;
} }
makeConfigFromData(t, e) { makeConfigFromData(t, e) {
const s = t.length - 1, l = t[0] ? t[0].length : 0, n = []; const s = t.length - 1, l = t[0] ? t[0].length : 0, n = [];
@ -559,6 +563,22 @@ class D {
} }
} }
export { export {
D as Spreadsheet D as Cache,
A as CachedColumn,
M as CachedRow,
k as Cell,
R as CellStyles,
m as Column,
d as Config,
b as Position,
w as RenderBox,
g as Row,
L as Selection,
B as Styles,
u as Viewport,
C as createSampleConfig,
f as createSampleData,
F as default,
z as makeSpreadsheetConfigAndData
}; };
//# sourceMappingURL=main.js.map //# sourceMappingURL=main.js.map

2
dist/main.js.map vendored

File diff suppressed because one or more lines are too long

View File

@ -1,4 +1,4 @@
import { Spreadsheet } from "../main"; import Spreadsheet from "../main";
export type CellConstructorProps = { export type CellConstructorProps = {
value: string; value: string;
displayValue: string; displayValue: string;

View File

@ -1,4 +1,4 @@
import { Spreadsheet } from "../main"; import Spreadsheet from "../main";
export type ViewportConstructorProps = { export type ViewportConstructorProps = {
top: number; top: number;
left: number; left: number;

View File

@ -2,9 +2,8 @@ import { Cell } from "../modules/cell";
import { Config } from "../modules/config"; import { Config } from "../modules/config";
export declare function createSampleData(rows: number, columns: number, fillCellsByCoords?: boolean): Cell[][]; export declare function createSampleData(rows: number, columns: number, fillCellsByCoords?: boolean): Cell[][];
export declare function createSampleConfig(rows: number, columns: number): Config; export declare function createSampleConfig(rows: number, columns: number): Config;
type SpreadsheetConfigAndDataReturnType = { export type SpreadsheetConfigAndDataReturnType = {
config: Config; config: Config;
data: Cell[][]; data: Cell[][];
}; };
export declare function makeSpreadsheetConfigAndData(rows: number, columns: number): SpreadsheetConfigAndDataReturnType; export declare function makeSpreadsheetConfigAndData(rows: number, columns: number): SpreadsheetConfigAndDataReturnType;
export {};

View File

@ -8,6 +8,8 @@
</head> </head>
<body> <body>
<div id="spreadsheet"></div> <div id="spreadsheet"></div>
<button id="save_button">Save sheet</button>
<button id="load_button">Load sheet</button>
<script type="module" src="/src/index.ts"></script> <script type="module" src="/src/index.ts"></script>
</body> </body>
</html> </html>

View File

@ -1,7 +1,7 @@
{ {
"name": "modern_spreadsheet", "name": "modern_spreadsheet",
"private": false, "private": false,
"version": "0.0.19", "version": "0.0.20",
"exports": { "exports": {
".": { ".": {
"import": "./dist/main.js", "import": "./dist/main.js",

View File

@ -1,4 +1,4 @@
import { Spreadsheet } from "../main"; import Spreadsheet from "../main";
import { Position } from "../modules/cell"; import { Position } from "../modules/cell";
import { RenderBox } from "../modules/renderBox"; import { RenderBox } from "../modules/renderBox";

View File

@ -1,4 +1,4 @@
import { Spreadsheet } from "../main" import Spreadsheet from "../main"
export class Header { export class Header {
element: HTMLHeadElement element: HTMLHeadElement

View File

@ -1,4 +1,4 @@
import { Spreadsheet } from "../main" import Spreadsheet from "../main"
export interface ViewportRect { export interface ViewportRect {
top: number top: number

View File

@ -1,4 +1,4 @@
import { Spreadsheet } from "../main" import Spreadsheet from "../main"
import { Position } from "../modules/cell" import { Position } from "../modules/cell"
/** /**

View File

@ -1,4 +1,4 @@
import { Spreadsheet } from "../main" import Spreadsheet from "../main"
import { ViewProperties } from "../modules/config" import { ViewProperties } from "../modules/config"
/** Base (root) component */ /** Base (root) component */

View File

@ -1,4 +1,4 @@
import { Spreadsheet } from "../main" import Spreadsheet from "../main"
export class Toolbar { export class Toolbar {
element: HTMLDivElement element: HTMLDivElement

View File

@ -1,5 +1,23 @@
import Spreadsheet, { createSampleData, } from './main' import Spreadsheet from './main'
const sheet = new Spreadsheet('#spreadsheet').loadData(createSampleData(20, 20, true)) const saveButton = document.querySelector('#save_button')
const loadButton = document.querySelector('#load_button')
console.log(sheet) if(!saveButton || !loadButton) throw new Error("LOST")
const sheet = new Spreadsheet('#spreadsheet')
function saveDataToLS() {
const serializableData = sheet.serializeData()
localStorage.setItem('sheet', JSON.stringify(serializableData))
}
function loadDataFromLS() {
const data = localStorage.getItem('sheet')
if(!data) return
const json = JSON.parse(data)
sheet.loadData(json)
}
saveButton.addEventListener('click', saveDataToLS)
loadButton.addEventListener('click', loadDataFromLS)

View File

@ -4,7 +4,7 @@ import { Scroller } from "./components/scroller";
import { Sheet } from "./components/sheet"; import { Sheet } from "./components/sheet";
import { Table } from "./components/table"; import { Table } from "./components/table";
import { Toolbar } from "./components/toolbar"; import { Toolbar } from "./components/toolbar";
import { Cell, CellConstructorProps, Position } from "./modules/cell"; import { Cell, CellConstructorProps, Position, SerializableCell } from "./modules/cell";
import { Config, ViewProperties } from "./modules/config"; import { Config, ViewProperties } from "./modules/config";
import { RangeSelectionType, Selection } from "./modules/selection"; import { RangeSelectionType, Selection } from "./modules/selection";
import { Styles } from "./modules/styles"; import { Styles } from "./modules/styles";
@ -47,14 +47,15 @@ export default class Spreadsheet {
public cache: Cache public cache: Cache
constructor(target: string | HTMLElement, props?: SpreadsheetConstructorProperties) { constructor(target: string | HTMLElement, props?: SpreadsheetConstructorProperties) {
const config = createSampleConfig(500, 500) const data = createSampleData(40, 40)
const config = this.makeConfigFromData(data, props?.view ?? { height: 600, width: 800 })
if (props?.view) { if (props?.view) {
config.view = props.view config.view = props.view
} }
this.config = new Config(config) this.config = new Config(config)
this.sheet = new Sheet(this) this.sheet = new Sheet(this)
const data = createSampleData(500, 500)
this.table = new Table(this) this.table = new Table(this)
this.scroller = new Scroller(this) this.scroller = new Scroller(this)
this.toolbar = new Toolbar(this) this.toolbar = new Toolbar(this)
@ -209,9 +210,32 @@ export default class Spreadsheet {
this.data[row][col].render(this) this.data[row][col].render(this)
} }
public loadData(data: Cell[][]): Spreadsheet { public loadData(data: Cell[][] | SerializableCell[][]): Spreadsheet {
this.data = data const rowsLength = data.length
this.config = this.makeConfigFromData(data, this.config.view) const colsLength = data[0] ? this.data[0].length : 0
this.data = []
const formattedData: Cell[][] = []
for (let row = 0; row < rowsLength; row++) {
const innerRow: Cell[] = []
for (let col = 0; col < colsLength; col++) {
const cell = data[row][col]
innerRow.push(new Cell({
displayValue: cell.displayValue,
position: cell.position,
resultValue: cell.resultValue,
value: cell.value
}))
}
formattedData.push(innerRow)
}
this.data = formattedData
this.selection.selectedCell = null
this.selection.selectedRange = null
this.config = this.makeConfigFromData(formattedData, this.config.view)
this.cache = this.getInitialCache() this.cache = this.getInitialCache()
this.scroller.updateScrollerSize() this.scroller.updateScrollerSize()
this.viewport = new Viewport(this, this.scroller.getViewportBoundlingRect()) this.viewport = new Viewport(this, this.scroller.getViewportBoundlingRect())
@ -249,6 +273,23 @@ export default class Spreadsheet {
return config return config
} }
public serializeData(): SerializableCell[][] {
const rowsLength = this.data.length
const colsLength = this.data[0] ? this.data[0].length : 0
const cellsArray: SerializableCell[][] = []
for (let row = 0; row < rowsLength; row++) {
const innerRow: SerializableCell[] = []
for (let col = 0; col < colsLength; col++) {
innerRow.push(this.data[row][col].getSerializableCell())
}
cellsArray.push(innerRow)
}
return cellsArray
}
} }
export * from './modules/cache' export * from './modules/cache'

View File

@ -1,4 +1,4 @@
import { Spreadsheet } from "../main" import Spreadsheet from "../main"
import { RenderBox } from "./renderBox" import { RenderBox } from "./renderBox"
export type CellConstructorProps = { export type CellConstructorProps = {
@ -43,6 +43,21 @@ export class Position {
} }
} }
export class SerializableCell {
value: string
displayValue: string
resultValue: string
position: Position
style: CellStyles
constructor(props: SerializableCell | SerializableCell) {
this.value = props.value
this.displayValue = props.displayValue
this.resultValue = props.resultValue
this.position = props.position
this.style = props.style
}
}
export class Cell { export class Cell {
value: string value: string
displayValue: string displayValue: string
@ -58,6 +73,17 @@ export class Cell {
this.position = props.position this.position = props.position
} }
public getSerializableCell(): SerializableCell {
const cell:SerializableCell = new SerializableCell({
displayValue: this.displayValue,
position: this.position,
resultValue: this.resultValue,
style: this.style,
value: this.value
})
return cell
}
changeValues(values: Partial<Omit<CellConstructorProps, 'position'>>) { changeValues(values: Partial<Omit<CellConstructorProps, 'position'>>) {
Object.assign(this, values) Object.assign(this, values)
} }

View File

@ -1,4 +1,4 @@
import { Spreadsheet } from "../main" import Spreadsheet from "../main"
export type ViewportConstructorProps = { export type ViewportConstructorProps = {
top: number top: number