Compare commits
28 Commits
implement-
...
main
| Author | SHA1 | Date |
|---|---|---|
|
|
41bae423c3 | |
|
|
47f2163124 | |
|
|
ae0f8b49e6 | |
|
|
c26c166295 | |
|
|
cddfc134f8 | |
|
|
1e2847f7f2 | |
|
|
d8c5e4343f | |
|
|
997e8f086b | |
|
|
6dc20f92e7 | |
|
|
8aed4c81b9 | |
|
|
ca67d409d5 | |
|
|
8bdaff0521 | |
|
|
651fea95e4 | |
|
|
954b3b8260 | |
|
|
022435103b | |
|
|
3a1367a901 | |
|
|
fc9a0df38d | |
|
|
322378904c | |
|
|
8596220e85 | |
|
|
9dd64b9a77 | |
|
|
50b3c3986f | |
|
|
35cb54e23e | |
|
|
06dd0a0cdf | |
|
|
0f277badb9 | |
|
|
22e238087c | |
|
|
53e081435b | |
|
|
1ef3ae3de4 | |
|
|
c34d913619 |
|
|
@ -4,7 +4,11 @@ module.exports = {
|
||||||
es2021: true,
|
es2021: true,
|
||||||
node: true,
|
node: true,
|
||||||
},
|
},
|
||||||
extends: ["eslint:recommended", "plugin:@typescript-eslint/recommended", "prettier"],
|
extends: [
|
||||||
|
"eslint:recommended",
|
||||||
|
"plugin:@typescript-eslint/recommended",
|
||||||
|
"prettier",
|
||||||
|
],
|
||||||
overrides: [
|
overrides: [
|
||||||
{
|
{
|
||||||
env: {
|
env: {
|
||||||
|
|
|
||||||
46
README.md
46
README.md
|
|
@ -1,9 +1,15 @@
|
||||||
# Modern Spreadsheet
|
# Modern Spreadsheet
|
||||||
|
|
||||||
|
<img src="https://raw.githubusercontent.com/yazmeyaa/modern_spreadsheet/6dc20f92e769210c076600c7fcfacd4ed528f085/repo_assets/spreadsheet_preview.png?raw=true" alt="spreadsheet_preview">
|
||||||
|
|
||||||
|
## Features:
|
||||||
- High performance spreadsheet based on CanvasAPI.
|
- High performance spreadsheet based on CanvasAPI.
|
||||||
- TypeScript supported
|
- TypeScript supported
|
||||||
|
- Native scrolling
|
||||||
|
- Customizable
|
||||||
|
- Copy & Paste support
|
||||||
|
|
||||||
## Basic usage
|
### Basic usage
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
import Spreadsheet from "modern_spreadsheet";
|
import Spreadsheet from "modern_spreadsheet";
|
||||||
|
|
@ -14,7 +20,7 @@ const sheet = new Spreadsheet(target);
|
||||||
//...
|
//...
|
||||||
```
|
```
|
||||||
|
|
||||||
## Save and load data
|
### Save and load data
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
function saveData() {
|
function saveData() {
|
||||||
|
|
@ -30,10 +36,39 @@ function loadData() {
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
## Roadmap
|
#### Supported events
|
||||||
|
- onCellClick
|
||||||
|
- onSelectionChange
|
||||||
|
- onCellChange
|
||||||
|
- onCopy
|
||||||
|
|
||||||
- Custom event functions (ex.: onSelectionChange, onCellEdit...). Full list of supported events will available on this page
|
### Using events examples
|
||||||
- Rows number and columns heading render
|
```ts
|
||||||
|
import Spreadsheet, { SpreadsheetConstructorProperties } from "./main";
|
||||||
|
|
||||||
|
const options: SpreadsheetConstructorProperties = {
|
||||||
|
onCellClick: (event, cell) => {
|
||||||
|
console.log("Cell click", event, cell);
|
||||||
|
},
|
||||||
|
onSelectionChange: (selection) => {
|
||||||
|
console.log("Changed selection: ", selection);
|
||||||
|
},
|
||||||
|
onCellChange = (cell) => {
|
||||||
|
console.log("Cell changed: ", cell);
|
||||||
|
},
|
||||||
|
onCopy: (range, data, dataAsString) => {
|
||||||
|
console.log("Copy event: ", range, data, dataAsString)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const sheet = new Spreadsheet("#spreadsheet", options);
|
||||||
|
```
|
||||||
|
|
||||||
|
### Roadmap
|
||||||
|
|
||||||
|
- ~~Rows number and columns heading render~~
|
||||||
|
- ~~Custom event functions (ex.: onSelectionChange, onCellEdit...). Full list of supported events will available on this page~~
|
||||||
|
- ~~Copy & Paste support~~
|
||||||
- Rows and columns resizing
|
- Rows and columns resizing
|
||||||
- Toolbar
|
- Toolbar
|
||||||
- Context menu
|
- Context menu
|
||||||
|
|
@ -41,4 +76,3 @@ function loadData() {
|
||||||
- Selected cell depends cells highlight
|
- Selected cell depends cells highlight
|
||||||
- Async formulas support
|
- Async formulas support
|
||||||
- Mutlisheets (?)
|
- Mutlisheets (?)
|
||||||
- Copy & Paste support
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,16 @@
|
||||||
|
import Spreadsheet from "../main";
|
||||||
|
export declare class ColumnsBar {
|
||||||
|
element: HTMLCanvasElement;
|
||||||
|
private root;
|
||||||
|
height: number;
|
||||||
|
width: number;
|
||||||
|
ctx: CanvasRenderingContext2D;
|
||||||
|
constructor(root: Spreadsheet);
|
||||||
|
private createElement;
|
||||||
|
setElementPosition(top: number, left: number): void;
|
||||||
|
private isColumnSelected;
|
||||||
|
private renderText;
|
||||||
|
private renderRect;
|
||||||
|
private renderSingleColumn;
|
||||||
|
renderBar(): void;
|
||||||
|
}
|
||||||
|
|
@ -1,6 +0,0 @@
|
||||||
import Spreadsheet from "../main";
|
|
||||||
export declare class Header {
|
|
||||||
element: HTMLHeadElement;
|
|
||||||
root: Spreadsheet;
|
|
||||||
constructor(root: Spreadsheet);
|
|
||||||
}
|
|
||||||
|
|
@ -0,0 +1,17 @@
|
||||||
|
import Spreadsheet from "../main";
|
||||||
|
export declare class RowsBar {
|
||||||
|
element: HTMLCanvasElement;
|
||||||
|
ctx: CanvasRenderingContext2D;
|
||||||
|
root: Spreadsheet;
|
||||||
|
width: number;
|
||||||
|
height: number;
|
||||||
|
resizerHeight: number;
|
||||||
|
constructor(root: Spreadsheet);
|
||||||
|
private createElement;
|
||||||
|
setElementPosition(top: number, left: number): void;
|
||||||
|
private isRowSelected;
|
||||||
|
private renderText;
|
||||||
|
private renderRect;
|
||||||
|
private renderSingleRow;
|
||||||
|
renderBar(): void;
|
||||||
|
}
|
||||||
|
|
@ -12,6 +12,7 @@ export declare class Scroller {
|
||||||
private root;
|
private root;
|
||||||
private isSelecting;
|
private isSelecting;
|
||||||
constructor(root: Spreadsheet);
|
constructor(root: Spreadsheet);
|
||||||
|
setSelectingMode(mode: boolean): void;
|
||||||
private handleMouseMove;
|
private handleMouseMove;
|
||||||
private handleMouseUp;
|
private handleMouseUp;
|
||||||
private handleDoubleClick;
|
private handleDoubleClick;
|
||||||
|
|
|
||||||
|
|
@ -10,5 +10,8 @@ export declare class Sheet {
|
||||||
constructor(root: Spreadsheet);
|
constructor(root: Spreadsheet);
|
||||||
getCellByCoords(x: number, y: number): Position;
|
getCellByCoords(x: number, y: number): Position;
|
||||||
renderCell(position: Position): void;
|
renderCell(position: Position): void;
|
||||||
|
private getSelectionRange;
|
||||||
|
private renderSelectionRange;
|
||||||
|
renderSelection(): void;
|
||||||
renderSheet(): void;
|
renderSheet(): void;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,5 +2,6 @@ import Spreadsheet from "../main";
|
||||||
export declare class Toolbar {
|
export declare class Toolbar {
|
||||||
element: HTMLDivElement;
|
element: HTMLDivElement;
|
||||||
root: Spreadsheet;
|
root: Spreadsheet;
|
||||||
|
height: number;
|
||||||
constructor(root: Spreadsheet);
|
constructor(root: Spreadsheet);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
|
@ -1,20 +1,26 @@
|
||||||
import { Cell, CellConstructorProps, CellStyles, Position, SerializableCell } from "./modules/cell";
|
import { Cell, CellConstructorProps, CellStyles, Position, SerializableCell } from "./modules/cell";
|
||||||
import { Config, ViewProperties } from "./modules/config";
|
import { CellChangeEvent, CellClickEvent, Config, CopyEvent, SelectionChangeEvent, 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";
|
||||||
import { Viewport } from "./modules/viewport";
|
import { Viewport } from "./modules/viewport";
|
||||||
import './scss/main.scss';
|
import "./scss/main.scss";
|
||||||
import { Cache } from "./modules/cache";
|
import { Cache } from "./modules/cache";
|
||||||
interface SpreadsheetConstructorProperties {
|
import { Events } from "./modules/events";
|
||||||
config?: Omit<Config, 'view'>;
|
import { Clipboard } from "./modules/clipboard";
|
||||||
|
export interface SpreadsheetConstructorProperties {
|
||||||
view?: ViewProperties;
|
view?: ViewProperties;
|
||||||
|
onCellClick?: CellClickEvent | null;
|
||||||
|
onSelectionChange?: SelectionChangeEvent | null;
|
||||||
|
onCellChange?: CellChangeEvent | null;
|
||||||
|
onCopy?: CopyEvent | null;
|
||||||
}
|
}
|
||||||
export declare const CSS_PREFIX = "modern_sc_";
|
export declare const CSS_PREFIX = "modern_sc_";
|
||||||
export default class Spreadsheet {
|
export default class Spreadsheet {
|
||||||
private table;
|
private table;
|
||||||
private scroller;
|
private scroller;
|
||||||
private toolbar;
|
private toolbar;
|
||||||
private header;
|
private rowsBar;
|
||||||
|
private columnsBar;
|
||||||
private sheet;
|
private sheet;
|
||||||
private editor;
|
private editor;
|
||||||
styles: Styles;
|
styles: Styles;
|
||||||
|
|
@ -23,7 +29,12 @@ export default class Spreadsheet {
|
||||||
viewport: Viewport;
|
viewport: Viewport;
|
||||||
selection: Selection;
|
selection: Selection;
|
||||||
cache: Cache;
|
cache: Cache;
|
||||||
|
events: Events;
|
||||||
|
clipboard: Clipboard;
|
||||||
constructor(target: string | HTMLElement, props?: SpreadsheetConstructorProperties);
|
constructor(target: string | HTMLElement, props?: SpreadsheetConstructorProperties);
|
||||||
|
private setRowsBarPosition;
|
||||||
|
private setColumnsBarPosition;
|
||||||
|
private setElementsPositions;
|
||||||
private getInitialCache;
|
private getInitialCache;
|
||||||
private buildComponent;
|
private buildComponent;
|
||||||
/**Destroy spreadsheet DOM element.
|
/**Destroy spreadsheet DOM element.
|
||||||
|
|
@ -38,28 +49,34 @@ export default class Spreadsheet {
|
||||||
*/
|
*/
|
||||||
get ctx(): CanvasRenderingContext2D;
|
get ctx(): CanvasRenderingContext2D;
|
||||||
get viewProps(): ViewProperties;
|
get viewProps(): ViewProperties;
|
||||||
|
get columnsBarHeight(): number;
|
||||||
|
get rowsBarWidth(): number;
|
||||||
|
get toolbarHeight(): number;
|
||||||
/** Focusing on interactive part of spreadsheet */
|
/** Focusing on interactive part of spreadsheet */
|
||||||
focusTable(): void;
|
focusTable(): void;
|
||||||
getCellByCoords(x: number, y: number): Position;
|
getCellByCoords(x: number, y: number): Position;
|
||||||
getCell(position: Position): Cell;
|
getCell(position: Position): Cell;
|
||||||
changeCellValues(position: Position, values: Partial<Omit<CellConstructorProps, 'position'>>): void;
|
changeCellValues(position: Position, values: Partial<Omit<CellConstructorProps, "position">>, enableCallback?: boolean): void;
|
||||||
changeCellStyles(position: Position, styles: CellStyles): void;
|
changeCellStyles(position: Position, styles: CellStyles): void;
|
||||||
applyActionToRange(range: RangeSelectionType, callback: (cell: Cell) => any): void;
|
applyActionToRange(range: RangeSelectionType, callback: (cell: Cell) => void): void;
|
||||||
deleteSelectedCellsValues(): void;
|
deleteSelectedCellsValues(): void;
|
||||||
showEditor(position: Position, initialString?: string): void;
|
showEditor(position: Position, initialString?: string): void;
|
||||||
renderSheet(): void;
|
renderSheet(): void;
|
||||||
|
renderSelection(): void;
|
||||||
|
renderColumnsBar(): void;
|
||||||
|
renderRowsBar(): void;
|
||||||
renderCell(row: number, col: number): void;
|
renderCell(row: number, col: number): void;
|
||||||
loadData(data: Cell[][] | SerializableCell[][]): Spreadsheet;
|
loadData(data: Cell[][] | SerializableCell[][]): Spreadsheet;
|
||||||
private makeConfigFromData;
|
private makeConfigFromData;
|
||||||
serializeData(): SerializableCell[][];
|
serializeData(): SerializableCell[][];
|
||||||
}
|
}
|
||||||
export * from './modules/cache';
|
export * from "./modules/cache";
|
||||||
export * from './modules/cell';
|
export * from "./modules/cell";
|
||||||
export * from './modules/column';
|
export * from "./modules/column";
|
||||||
export * from './modules/config';
|
export * from "./modules/config";
|
||||||
export * from './modules/renderBox';
|
export * from "./modules/renderBox";
|
||||||
export * from './modules/row';
|
export * from "./modules/row";
|
||||||
export * from './modules/selection';
|
export * from "./modules/selection";
|
||||||
export * from './modules/styles';
|
export * from "./modules/styles";
|
||||||
export * from './modules/viewport';
|
export * from "./modules/viewport";
|
||||||
export * from './utils/createData';
|
export * from "./utils/createData";
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
|
|
@ -41,15 +41,14 @@ export declare class Cell {
|
||||||
value: string;
|
value: string;
|
||||||
/** Value to render */
|
/** Value to render */
|
||||||
displayValue: string;
|
displayValue: string;
|
||||||
/** 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 */
|
||||||
resultValue: string;
|
resultValue: string;
|
||||||
position: Position;
|
position: Position;
|
||||||
style: CellStyles | null;
|
style: CellStyles | null;
|
||||||
constructor(props: CellConstructorProps);
|
constructor(props: CellConstructorProps);
|
||||||
getSerializableCell(): SerializableCell;
|
getSerializableCell(): SerializableCell;
|
||||||
changeStyles(styles: CellStyles): void;
|
changeStyles(styles: CellStyles): void;
|
||||||
changeValues(values: Partial<Omit<CellConstructorProps, 'position'>>): void;
|
changeValues(values: Partial<Omit<CellConstructorProps, "position">>): void;
|
||||||
private isCellInRange;
|
|
||||||
render(root: Spreadsheet): void;
|
render(root: Spreadsheet): void;
|
||||||
}
|
}
|
||||||
export {};
|
export {};
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,9 @@
|
||||||
|
import Spreadsheet, { RangeSelectionType } from "../main";
|
||||||
|
import { Cell, Position } from "./cell";
|
||||||
|
export declare class Clipboard {
|
||||||
|
saved: Cell[][] | null;
|
||||||
|
root: Spreadsheet;
|
||||||
|
constructor(root: Spreadsheet);
|
||||||
|
copy(data: Cell[][], range: RangeSelectionType): void;
|
||||||
|
paste(root: Spreadsheet, { column, row }: Position, event: ClipboardEvent): void;
|
||||||
|
}
|
||||||
|
|
@ -1,9 +1,15 @@
|
||||||
|
import { Cell } from "./cell";
|
||||||
import { Column } from "./column";
|
import { Column } from "./column";
|
||||||
import { Row } from "./row";
|
import { Row } from "./row";
|
||||||
|
import { RangeSelectionType, Selection } from "./selection";
|
||||||
export interface ViewProperties {
|
export interface ViewProperties {
|
||||||
width: number;
|
width: number;
|
||||||
height: number;
|
height: number;
|
||||||
}
|
}
|
||||||
|
export type CellClickEvent = (event: MouseEvent, cell: Cell) => void;
|
||||||
|
export type SelectionChangeEvent = (selection: Selection) => void;
|
||||||
|
export type CellChangeEvent = (cell: Cell) => void;
|
||||||
|
export type CopyEvent = (range: RangeSelectionType, data: Cell[][], dataAsString: string) => void;
|
||||||
export type ConfigProperties = {
|
export type ConfigProperties = {
|
||||||
/** Please, end it with '_' symbol.
|
/** Please, end it with '_' symbol.
|
||||||
*
|
*
|
||||||
|
|
@ -14,6 +20,10 @@ export type ConfigProperties = {
|
||||||
rows: Row[];
|
rows: Row[];
|
||||||
columns: Column[];
|
columns: Column[];
|
||||||
view: ViewProperties;
|
view: ViewProperties;
|
||||||
|
onCellClick?: CellClickEvent | null;
|
||||||
|
onSelectionChange?: SelectionChangeEvent | null;
|
||||||
|
onCellChange?: CellChangeEvent | null;
|
||||||
|
onCopy?: CopyEvent | null;
|
||||||
};
|
};
|
||||||
export type SheetConfigConstructorProps = {
|
export type SheetConfigConstructorProps = {
|
||||||
rows: Row[];
|
rows: Row[];
|
||||||
|
|
@ -23,5 +33,9 @@ export declare class Config {
|
||||||
rows: Row[];
|
rows: Row[];
|
||||||
columns: Column[];
|
columns: Column[];
|
||||||
view: ViewProperties;
|
view: ViewProperties;
|
||||||
|
onCellClick: CellClickEvent | null;
|
||||||
|
onSelectonChange: SelectionChangeEvent | null;
|
||||||
|
onCellChange: CellChangeEvent | null;
|
||||||
|
onCopy: CopyEvent | null;
|
||||||
constructor(props: ConfigProperties);
|
constructor(props: ConfigProperties);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,39 @@
|
||||||
|
import { Scroller } from "../components/scroller";
|
||||||
|
import Spreadsheet, { Cell, RangeSelectionType, Selection } from "../main";
|
||||||
|
export declare enum EventTypes {
|
||||||
|
CELL_CLICK = "CELL_CLICK",
|
||||||
|
SELECTION_CHANGE = "CHANGE_SELECTION",
|
||||||
|
CELL_CHANGE = "CELL_CHANGE",
|
||||||
|
COPY_CELLS = "COPY_CELLS"
|
||||||
|
}
|
||||||
|
export type CellClickEvent = {
|
||||||
|
type: EventTypes.CELL_CLICK;
|
||||||
|
event: MouseEvent;
|
||||||
|
scroller: Scroller;
|
||||||
|
};
|
||||||
|
export type ChangeSelectionEvent = {
|
||||||
|
type: EventTypes.SELECTION_CHANGE;
|
||||||
|
selection: Selection;
|
||||||
|
enableCallback?: boolean;
|
||||||
|
};
|
||||||
|
export type ChangeCellEvent = {
|
||||||
|
type: EventTypes.CELL_CHANGE;
|
||||||
|
cell: Cell;
|
||||||
|
enableCallback?: boolean;
|
||||||
|
};
|
||||||
|
export type CopyAction = {
|
||||||
|
type: EventTypes.COPY_CELLS;
|
||||||
|
range: RangeSelectionType;
|
||||||
|
data: Cell[][];
|
||||||
|
dataAsString: string;
|
||||||
|
};
|
||||||
|
export type ActionTypes = CellClickEvent | ChangeSelectionEvent | ChangeCellEvent | CopyAction;
|
||||||
|
export declare class Events {
|
||||||
|
root: Spreadsheet;
|
||||||
|
constructor(root: Spreadsheet);
|
||||||
|
dispatch(action: ActionTypes): void;
|
||||||
|
private cellClick;
|
||||||
|
private changeSelection;
|
||||||
|
private changeCellValues;
|
||||||
|
private copy;
|
||||||
|
}
|
||||||
|
|
@ -1 +1 @@
|
||||||
body{padding:0;margin:0}.modern_sc_content{position:absolute;top:0;left:0}.modern_sc_spreadsheet_container{position:relative;isolation:isolate;border:2px solid black}.modern_sc_sheet{display:block;contain:strict}.modern_sc_scroller{overflow:scroll;box-sizing:border-box;transform:translateZ(0)}.modern_sc_scroller:focus{outline:none}.modern_sc_editor{position:absolute;box-sizing:border-box;font-size:16px;font-family:Arial,Helvetica,sans-serif}.modern_sc_hide{visibility:hidden}
|
body{padding:0;margin:0}.modern_sc_spreadsheet_container{position:relative;isolation:isolate;border:2px solid black}.modern_sc_content{position:absolute}.modern_sc_sheet{display:block;contain:strict}.modern_sc_scroller{position:absolute;overflow:scroll;box-sizing:border-box;transform:translateZ(0)}.modern_sc_scroller:focus{outline:none}.modern_sc_editor{position:absolute;box-sizing:border-box;font-size:16px;font-family:Arial,Helvetica,sans-serif}.modern_sc_hide{visibility:hidden}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
import { BaseSelectionType, RangeSelectionType } from "../main";
|
||||||
|
export declare function checkEqualRanges(range1: RangeSelectionType, range2: RangeSelectionType): boolean;
|
||||||
|
export declare function checkEqualCellSelections(selection1: BaseSelectionType, selection2: BaseSelectionType): boolean;
|
||||||
13
package.json
13
package.json
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"name": "modern_spreadsheet",
|
"name": "modern_spreadsheet",
|
||||||
"private": false,
|
"private": false,
|
||||||
"version": "0.0.27",
|
"version": "0.0.33",
|
||||||
"exports": {
|
"exports": {
|
||||||
".": {
|
".": {
|
||||||
"import": "./dist/main.js",
|
"import": "./dist/main.js",
|
||||||
|
|
@ -33,18 +33,21 @@
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite",
|
"dev": "vite",
|
||||||
"build": "tsc && vite build",
|
"build": "tsc && vite build",
|
||||||
|
"build:HTML": "tsc && cross-env BUILD_BROWSER=true vite build",
|
||||||
"build:watch": "tsc && vite build --watch",
|
"build:watch": "tsc && vite build --watch",
|
||||||
"preview": "vite preview",
|
"preview": "vite preview",
|
||||||
"predeploy": "npm run dist",
|
"predeploy": "npm run dist",
|
||||||
"deploy": "gh-pages -d dist",
|
"deploy": "gh-pages -d dist",
|
||||||
"lint": "eslint src --ext .ts",
|
"lint": "eslint src --ext .ts",
|
||||||
"lint:fix": "eslint src --ext .ts --fix"
|
"lint:fix": "eslint src --ext .ts --fix",
|
||||||
|
"format": "prettier --write ./src/**/*.ts"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@rollup/plugin-typescript": "^11.1.2",
|
"@rollup/plugin-typescript": "^11.1.2",
|
||||||
"@types/node": "^20.4.4",
|
"@types/node": "^20.4.4",
|
||||||
"@typescript-eslint/eslint-plugin": "^6.2.0",
|
"@typescript-eslint/eslint-plugin": "^6.2.0",
|
||||||
"@typescript-eslint/parser": "^6.2.0",
|
"@typescript-eslint/parser": "^6.2.0",
|
||||||
|
"cross-env": "^7.0.3",
|
||||||
"eslint": "^8.45.0",
|
"eslint": "^8.45.0",
|
||||||
"eslint-config-prettier": "^8.8.0",
|
"eslint-config-prettier": "^8.8.0",
|
||||||
"gh-pages": "^5.0.0",
|
"gh-pages": "^5.0.0",
|
||||||
|
|
@ -53,6 +56,10 @@
|
||||||
"sass": "^1.63.6",
|
"sass": "^1.63.6",
|
||||||
"tslib": "^2.6.0",
|
"tslib": "^2.6.0",
|
||||||
"typescript": "^5.0.2",
|
"typescript": "^5.0.2",
|
||||||
"vite": "^4.4.0"
|
"vite": "^4.4.0",
|
||||||
|
"vite-plugin-html": "^3.2.0"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"fast-formula-parser": "^1.0.19"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
370
pnpm-lock.yaml
370
pnpm-lock.yaml
|
|
@ -4,6 +4,11 @@ settings:
|
||||||
autoInstallPeers: true
|
autoInstallPeers: true
|
||||||
excludeLinksFromLockfile: false
|
excludeLinksFromLockfile: false
|
||||||
|
|
||||||
|
dependencies:
|
||||||
|
fast-formula-parser:
|
||||||
|
specifier: ^1.0.19
|
||||||
|
version: 1.0.19
|
||||||
|
|
||||||
devDependencies:
|
devDependencies:
|
||||||
'@rollup/plugin-typescript':
|
'@rollup/plugin-typescript':
|
||||||
specifier: ^11.1.2
|
specifier: ^11.1.2
|
||||||
|
|
@ -17,6 +22,9 @@ devDependencies:
|
||||||
'@typescript-eslint/parser':
|
'@typescript-eslint/parser':
|
||||||
specifier: ^6.2.0
|
specifier: ^6.2.0
|
||||||
version: 6.2.0(eslint@8.45.0)(typescript@5.0.2)
|
version: 6.2.0(eslint@8.45.0)(typescript@5.0.2)
|
||||||
|
cross-env:
|
||||||
|
specifier: ^7.0.3
|
||||||
|
version: 7.0.3
|
||||||
eslint:
|
eslint:
|
||||||
specifier: ^8.45.0
|
specifier: ^8.45.0
|
||||||
version: 8.45.0
|
version: 8.45.0
|
||||||
|
|
@ -44,6 +52,9 @@ devDependencies:
|
||||||
vite:
|
vite:
|
||||||
specifier: ^4.4.0
|
specifier: ^4.4.0
|
||||||
version: 4.4.0(@types/node@20.4.4)(sass@1.63.6)
|
version: 4.4.0(@types/node@20.4.4)(sass@1.63.6)
|
||||||
|
vite-plugin-html:
|
||||||
|
specifier: ^3.2.0
|
||||||
|
version: 3.2.0(vite@4.4.0)
|
||||||
|
|
||||||
packages:
|
packages:
|
||||||
|
|
||||||
|
|
@ -307,6 +318,43 @@ packages:
|
||||||
resolution: {integrity: sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==}
|
resolution: {integrity: sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/@jridgewell/gen-mapping@0.3.3:
|
||||||
|
resolution: {integrity: sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==}
|
||||||
|
engines: {node: '>=6.0.0'}
|
||||||
|
dependencies:
|
||||||
|
'@jridgewell/set-array': 1.1.2
|
||||||
|
'@jridgewell/sourcemap-codec': 1.4.15
|
||||||
|
'@jridgewell/trace-mapping': 0.3.20
|
||||||
|
dev: true
|
||||||
|
|
||||||
|
/@jridgewell/resolve-uri@3.1.1:
|
||||||
|
resolution: {integrity: sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==}
|
||||||
|
engines: {node: '>=6.0.0'}
|
||||||
|
dev: true
|
||||||
|
|
||||||
|
/@jridgewell/set-array@1.1.2:
|
||||||
|
resolution: {integrity: sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==}
|
||||||
|
engines: {node: '>=6.0.0'}
|
||||||
|
dev: true
|
||||||
|
|
||||||
|
/@jridgewell/source-map@0.3.5:
|
||||||
|
resolution: {integrity: sha512-UTYAUj/wviwdsMfzoSJspJxbkH5o1snzwX0//0ENX1u/55kkZZkcTZP6u9bwKGkv+dkk9at4m1Cpt0uY80kcpQ==}
|
||||||
|
dependencies:
|
||||||
|
'@jridgewell/gen-mapping': 0.3.3
|
||||||
|
'@jridgewell/trace-mapping': 0.3.20
|
||||||
|
dev: true
|
||||||
|
|
||||||
|
/@jridgewell/sourcemap-codec@1.4.15:
|
||||||
|
resolution: {integrity: sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==}
|
||||||
|
dev: true
|
||||||
|
|
||||||
|
/@jridgewell/trace-mapping@0.3.20:
|
||||||
|
resolution: {integrity: sha512-R8LcPeWZol2zR8mmH3JeKQ6QRCFb7XgUhV9ZlGhHLGyg4wpPiPZNQOOWhFZhxKw8u//yTbNGI42Bx/3paXEQ+Q==}
|
||||||
|
dependencies:
|
||||||
|
'@jridgewell/resolve-uri': 3.1.1
|
||||||
|
'@jridgewell/sourcemap-codec': 1.4.15
|
||||||
|
dev: true
|
||||||
|
|
||||||
/@nodelib/fs.scandir@2.1.5:
|
/@nodelib/fs.scandir@2.1.5:
|
||||||
resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==}
|
resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==}
|
||||||
engines: {node: '>= 8'}
|
engines: {node: '>= 8'}
|
||||||
|
|
@ -347,6 +395,14 @@ packages:
|
||||||
typescript: 5.0.2
|
typescript: 5.0.2
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/@rollup/pluginutils@4.2.1:
|
||||||
|
resolution: {integrity: sha512-iKnFXr7NkdZAIHiIWE+BX5ULi/ucVFYWD6TbAV+rZctiRTY2PL6tsIKhoIOaoskiWAkgu+VsbXgUVDNLHf+InQ==}
|
||||||
|
engines: {node: '>= 8.0.0'}
|
||||||
|
dependencies:
|
||||||
|
estree-walker: 2.0.2
|
||||||
|
picomatch: 2.3.1
|
||||||
|
dev: true
|
||||||
|
|
||||||
/@rollup/pluginutils@5.0.2:
|
/@rollup/pluginutils@5.0.2:
|
||||||
resolution: {integrity: sha512-pTd9rIsP92h+B6wWwFbW8RkZv4hiR/xKsqre4SIuAOaOEQRxi0lqLke9k2/7WegC85GgUs9pjmOjCUi3In4vwA==}
|
resolution: {integrity: sha512-pTd9rIsP92h+B6wWwFbW8RkZv4hiR/xKsqre4SIuAOaOEQRxi0lqLke9k2/7WegC85GgUs9pjmOjCUi3In4vwA==}
|
||||||
engines: {node: '>=14.0.0'}
|
engines: {node: '>=14.0.0'}
|
||||||
|
|
@ -577,15 +633,28 @@ packages:
|
||||||
resolution: {integrity: sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ==}
|
resolution: {integrity: sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ==}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/bahttext@1.1.0:
|
||||||
|
resolution: {integrity: sha512-3g7DbmCwYhLnnN5hJFB+Mtbst4q2gfXpAdqOMK6tz6FmgtlrhX1n1uhuzkIhEr+WcnFR6eRvebIE5msGuEtA7Q==}
|
||||||
|
dev: false
|
||||||
|
|
||||||
/balanced-match@1.0.2:
|
/balanced-match@1.0.2:
|
||||||
resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==}
|
resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/bessel@1.0.2:
|
||||||
|
resolution: {integrity: sha512-Al3nHGQGqDYqqinXhQzmwmcRToe/3WyBv4N8aZc5Pef8xw2neZlR9VPi84Sa23JtgWcucu18HxVZrnI0fn2etw==}
|
||||||
|
engines: {node: '>=0.8'}
|
||||||
|
dev: false
|
||||||
|
|
||||||
/binary-extensions@2.2.0:
|
/binary-extensions@2.2.0:
|
||||||
resolution: {integrity: sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==}
|
resolution: {integrity: sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==}
|
||||||
engines: {node: '>=8'}
|
engines: {node: '>=8'}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/boolbase@1.0.0:
|
||||||
|
resolution: {integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==}
|
||||||
|
dev: true
|
||||||
|
|
||||||
/brace-expansion@1.1.11:
|
/brace-expansion@1.1.11:
|
||||||
resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==}
|
resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==}
|
||||||
dependencies:
|
dependencies:
|
||||||
|
|
@ -593,6 +662,12 @@ packages:
|
||||||
concat-map: 0.0.1
|
concat-map: 0.0.1
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/brace-expansion@2.0.1:
|
||||||
|
resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==}
|
||||||
|
dependencies:
|
||||||
|
balanced-match: 1.0.2
|
||||||
|
dev: true
|
||||||
|
|
||||||
/braces@3.0.2:
|
/braces@3.0.2:
|
||||||
resolution: {integrity: sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==}
|
resolution: {integrity: sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==}
|
||||||
engines: {node: '>=8'}
|
engines: {node: '>=8'}
|
||||||
|
|
@ -600,11 +675,22 @@ packages:
|
||||||
fill-range: 7.0.1
|
fill-range: 7.0.1
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/buffer-from@1.1.2:
|
||||||
|
resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==}
|
||||||
|
dev: true
|
||||||
|
|
||||||
/callsites@3.1.0:
|
/callsites@3.1.0:
|
||||||
resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==}
|
resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==}
|
||||||
engines: {node: '>=6'}
|
engines: {node: '>=6'}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/camel-case@4.1.2:
|
||||||
|
resolution: {integrity: sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw==}
|
||||||
|
dependencies:
|
||||||
|
pascal-case: 3.1.2
|
||||||
|
tslib: 2.6.0
|
||||||
|
dev: true
|
||||||
|
|
||||||
/chalk@4.1.2:
|
/chalk@4.1.2:
|
||||||
resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==}
|
resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==}
|
||||||
engines: {node: '>=10'}
|
engines: {node: '>=10'}
|
||||||
|
|
@ -613,6 +699,12 @@ packages:
|
||||||
supports-color: 7.2.0
|
supports-color: 7.2.0
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/chevrotain@7.1.2:
|
||||||
|
resolution: {integrity: sha512-9bQsXVQ7UAvzMs7iUBBJ9Yv//exOy7bIR3PByOEk4M64vIE/LsiOiX7VIkMF/vEMlrSStwsaE884Bp9CpjtC5g==}
|
||||||
|
dependencies:
|
||||||
|
regexp-to-ast: 0.5.0
|
||||||
|
dev: false
|
||||||
|
|
||||||
/chokidar@3.5.3:
|
/chokidar@3.5.3:
|
||||||
resolution: {integrity: sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==}
|
resolution: {integrity: sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==}
|
||||||
engines: {node: '>= 8.10.0'}
|
engines: {node: '>= 8.10.0'}
|
||||||
|
|
@ -628,6 +720,13 @@ packages:
|
||||||
fsevents: 2.3.2
|
fsevents: 2.3.2
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/clean-css@5.3.2:
|
||||||
|
resolution: {integrity: sha512-JVJbM+f3d3Q704rF4bqQ5UUyTtuJ0JRKNbTKVEeujCCBoMdkEi+V+e8oktO9qGQNSvHrFTM6JZRXrUvGR1czww==}
|
||||||
|
engines: {node: '>= 10.0'}
|
||||||
|
dependencies:
|
||||||
|
source-map: 0.6.1
|
||||||
|
dev: true
|
||||||
|
|
||||||
/color-convert@2.0.1:
|
/color-convert@2.0.1:
|
||||||
resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==}
|
resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==}
|
||||||
engines: {node: '>=7.0.0'}
|
engines: {node: '>=7.0.0'}
|
||||||
|
|
@ -639,10 +738,19 @@ packages:
|
||||||
resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==}
|
resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/colorette@2.0.20:
|
||||||
|
resolution: {integrity: sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==}
|
||||||
|
dev: true
|
||||||
|
|
||||||
/commander@2.20.3:
|
/commander@2.20.3:
|
||||||
resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==}
|
resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/commander@8.3.0:
|
||||||
|
resolution: {integrity: sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==}
|
||||||
|
engines: {node: '>= 12'}
|
||||||
|
dev: true
|
||||||
|
|
||||||
/commondir@1.0.1:
|
/commondir@1.0.1:
|
||||||
resolution: {integrity: sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==}
|
resolution: {integrity: sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
@ -651,6 +759,23 @@ packages:
|
||||||
resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==}
|
resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/connect-history-api-fallback@1.6.0:
|
||||||
|
resolution: {integrity: sha512-e54B99q/OUoH64zYYRf3HBP5z24G38h5D3qXu23JGRoigpX5Ss4r9ZnDk3g0Z8uQC2x2lPaJ+UlWBc1ZWBWdLg==}
|
||||||
|
engines: {node: '>=0.8'}
|
||||||
|
dev: true
|
||||||
|
|
||||||
|
/consola@2.15.3:
|
||||||
|
resolution: {integrity: sha512-9vAdYbHj6x2fLKC4+oPH0kFzY/orMZyG2Aj+kNylHxKGJ/Ed4dpNyAQYwJOdqO4zdM7XpVHmyejQDcQHrnuXbw==}
|
||||||
|
dev: true
|
||||||
|
|
||||||
|
/cross-env@7.0.3:
|
||||||
|
resolution: {integrity: sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw==}
|
||||||
|
engines: {node: '>=10.14', npm: '>=6', yarn: '>=1'}
|
||||||
|
hasBin: true
|
||||||
|
dependencies:
|
||||||
|
cross-spawn: 7.0.3
|
||||||
|
dev: true
|
||||||
|
|
||||||
/cross-spawn@7.0.3:
|
/cross-spawn@7.0.3:
|
||||||
resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==}
|
resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==}
|
||||||
engines: {node: '>= 8'}
|
engines: {node: '>= 8'}
|
||||||
|
|
@ -660,6 +785,21 @@ packages:
|
||||||
which: 2.0.2
|
which: 2.0.2
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/css-select@4.3.0:
|
||||||
|
resolution: {integrity: sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ==}
|
||||||
|
dependencies:
|
||||||
|
boolbase: 1.0.0
|
||||||
|
css-what: 6.1.0
|
||||||
|
domhandler: 4.3.1
|
||||||
|
domutils: 2.8.0
|
||||||
|
nth-check: 2.1.1
|
||||||
|
dev: true
|
||||||
|
|
||||||
|
/css-what@6.1.0:
|
||||||
|
resolution: {integrity: sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==}
|
||||||
|
engines: {node: '>= 6'}
|
||||||
|
dev: true
|
||||||
|
|
||||||
/debug@4.3.4:
|
/debug@4.3.4:
|
||||||
resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==}
|
resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==}
|
||||||
engines: {node: '>=6.0'}
|
engines: {node: '>=6.0'}
|
||||||
|
|
@ -690,10 +830,66 @@ packages:
|
||||||
esutils: 2.0.3
|
esutils: 2.0.3
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/dom-serializer@1.4.1:
|
||||||
|
resolution: {integrity: sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==}
|
||||||
|
dependencies:
|
||||||
|
domelementtype: 2.3.0
|
||||||
|
domhandler: 4.3.1
|
||||||
|
entities: 2.2.0
|
||||||
|
dev: true
|
||||||
|
|
||||||
|
/domelementtype@2.3.0:
|
||||||
|
resolution: {integrity: sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==}
|
||||||
|
dev: true
|
||||||
|
|
||||||
|
/domhandler@4.3.1:
|
||||||
|
resolution: {integrity: sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==}
|
||||||
|
engines: {node: '>= 4'}
|
||||||
|
dependencies:
|
||||||
|
domelementtype: 2.3.0
|
||||||
|
dev: true
|
||||||
|
|
||||||
|
/domutils@2.8.0:
|
||||||
|
resolution: {integrity: sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==}
|
||||||
|
dependencies:
|
||||||
|
dom-serializer: 1.4.1
|
||||||
|
domelementtype: 2.3.0
|
||||||
|
domhandler: 4.3.1
|
||||||
|
dev: true
|
||||||
|
|
||||||
|
/dot-case@3.0.4:
|
||||||
|
resolution: {integrity: sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==}
|
||||||
|
dependencies:
|
||||||
|
no-case: 3.0.4
|
||||||
|
tslib: 2.6.0
|
||||||
|
dev: true
|
||||||
|
|
||||||
|
/dotenv-expand@8.0.3:
|
||||||
|
resolution: {integrity: sha512-SErOMvge0ZUyWd5B0NXMQlDkN+8r+HhVUsxgOO7IoPDOdDRD2JjExpN6y3KnFR66jsJMwSn1pqIivhU5rcJiNg==}
|
||||||
|
engines: {node: '>=12'}
|
||||||
|
dev: true
|
||||||
|
|
||||||
|
/dotenv@16.3.1:
|
||||||
|
resolution: {integrity: sha512-IPzF4w4/Rd94bA9imS68tZBaYyBWSCE47V1RGuMrB94iyTOIEwRmVL2x/4An+6mETpLrKJ5hQkB8W4kFAadeIQ==}
|
||||||
|
engines: {node: '>=12'}
|
||||||
|
dev: true
|
||||||
|
|
||||||
|
/ejs@3.1.9:
|
||||||
|
resolution: {integrity: sha512-rC+QVNMJWv+MtPgkt0y+0rVEIdbtxVADApW9JXrUVlzHetgcyczP/E7DJmWJ4fJCZF2cPcBk0laWO9ZHMG3DmQ==}
|
||||||
|
engines: {node: '>=0.10.0'}
|
||||||
|
hasBin: true
|
||||||
|
dependencies:
|
||||||
|
jake: 10.8.7
|
||||||
|
dev: true
|
||||||
|
|
||||||
/email-addresses@5.0.0:
|
/email-addresses@5.0.0:
|
||||||
resolution: {integrity: sha512-4OIPYlA6JXqtVn8zpHpGiI7vE6EQOAg16aGnDMIAlZVinnoZ8208tW1hAbjWydgN/4PLTT9q+O1K6AH/vALJGw==}
|
resolution: {integrity: sha512-4OIPYlA6JXqtVn8zpHpGiI7vE6EQOAg16aGnDMIAlZVinnoZ8208tW1hAbjWydgN/4PLTT9q+O1K6AH/vALJGw==}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/entities@2.2.0:
|
||||||
|
resolution: {integrity: sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==}
|
||||||
|
dev: true
|
||||||
|
|
||||||
/esbuild@0.18.14:
|
/esbuild@0.18.14:
|
||||||
resolution: {integrity: sha512-uNPj5oHPYmj+ZhSQeYQVFZ+hAlJZbAGOmmILWIqrGvPVlNLbyOvU5Bu6Woi8G8nskcx0vwY0iFoMPrzT86Ko+w==}
|
resolution: {integrity: sha512-uNPj5oHPYmj+ZhSQeYQVFZ+hAlJZbAGOmmILWIqrGvPVlNLbyOvU5Bu6Woi8G8nskcx0vwY0iFoMPrzT86Ko+w==}
|
||||||
engines: {node: '>=12'}
|
engines: {node: '>=12'}
|
||||||
|
|
@ -843,6 +1039,15 @@ packages:
|
||||||
resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==}
|
resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/fast-formula-parser@1.0.19:
|
||||||
|
resolution: {integrity: sha512-8Roq7V1XjuYj3cWZ9tILyqBHGjS7NkejRaHxSWePu0qjIcvimZwe6WfeA9che03dgWrykZqrw691qg3lwiIkMg==}
|
||||||
|
dependencies:
|
||||||
|
bahttext: 1.1.0
|
||||||
|
bessel: 1.0.2
|
||||||
|
chevrotain: 7.1.2
|
||||||
|
jstat: 1.9.6
|
||||||
|
dev: false
|
||||||
|
|
||||||
/fast-glob@3.3.1:
|
/fast-glob@3.3.1:
|
||||||
resolution: {integrity: sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg==}
|
resolution: {integrity: sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg==}
|
||||||
engines: {node: '>=8.6.0'}
|
engines: {node: '>=8.6.0'}
|
||||||
|
|
@ -875,6 +1080,12 @@ packages:
|
||||||
flat-cache: 3.0.4
|
flat-cache: 3.0.4
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/filelist@1.0.4:
|
||||||
|
resolution: {integrity: sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==}
|
||||||
|
dependencies:
|
||||||
|
minimatch: 5.1.6
|
||||||
|
dev: true
|
||||||
|
|
||||||
/filename-reserved-regex@2.0.0:
|
/filename-reserved-regex@2.0.0:
|
||||||
resolution: {integrity: sha512-lc1bnsSr4L4Bdif8Xb/qrtokGbq5zlsms/CYH8PP+WtCkGNF65DPiQY8vG3SakEdRn8Dlnm+gW/qWKKjS5sZzQ==}
|
resolution: {integrity: sha512-lc1bnsSr4L4Bdif8Xb/qrtokGbq5zlsms/CYH8PP+WtCkGNF65DPiQY8vG3SakEdRn8Dlnm+gW/qWKKjS5sZzQ==}
|
||||||
engines: {node: '>=4'}
|
engines: {node: '>=4'}
|
||||||
|
|
@ -933,6 +1144,15 @@ packages:
|
||||||
resolution: {integrity: sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==}
|
resolution: {integrity: sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/fs-extra@10.1.0:
|
||||||
|
resolution: {integrity: sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==}
|
||||||
|
engines: {node: '>=12'}
|
||||||
|
dependencies:
|
||||||
|
graceful-fs: 4.2.11
|
||||||
|
jsonfile: 6.1.0
|
||||||
|
universalify: 2.0.1
|
||||||
|
dev: true
|
||||||
|
|
||||||
/fs-extra@8.1.0:
|
/fs-extra@8.1.0:
|
||||||
resolution: {integrity: sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==}
|
resolution: {integrity: sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==}
|
||||||
engines: {node: '>=6 <7 || >=8'}
|
engines: {node: '>=6 <7 || >=8'}
|
||||||
|
|
@ -1047,6 +1267,25 @@ packages:
|
||||||
function-bind: 1.1.1
|
function-bind: 1.1.1
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/he@1.2.0:
|
||||||
|
resolution: {integrity: sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==}
|
||||||
|
hasBin: true
|
||||||
|
dev: true
|
||||||
|
|
||||||
|
/html-minifier-terser@6.1.0:
|
||||||
|
resolution: {integrity: sha512-YXxSlJBZTP7RS3tWnQw74ooKa6L9b9i9QYXY21eUEvhZ3u9XLfv6OnFsQq6RxkhHygsaUMvYsZRV5rU/OVNZxw==}
|
||||||
|
engines: {node: '>=12'}
|
||||||
|
hasBin: true
|
||||||
|
dependencies:
|
||||||
|
camel-case: 4.1.2
|
||||||
|
clean-css: 5.3.2
|
||||||
|
commander: 8.3.0
|
||||||
|
he: 1.2.0
|
||||||
|
param-case: 3.0.4
|
||||||
|
relateurl: 0.2.7
|
||||||
|
terser: 5.24.0
|
||||||
|
dev: true
|
||||||
|
|
||||||
/ignore@5.2.4:
|
/ignore@5.2.4:
|
||||||
resolution: {integrity: sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==}
|
resolution: {integrity: sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==}
|
||||||
engines: {node: '>= 4'}
|
engines: {node: '>= 4'}
|
||||||
|
|
@ -1119,6 +1358,17 @@ packages:
|
||||||
resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==}
|
resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/jake@10.8.7:
|
||||||
|
resolution: {integrity: sha512-ZDi3aP+fG/LchyBzUM804VjddnwfSfsdeYkwt8NcbKRvo4rFkjhs456iLFn3k2ZUWvNe4i48WACDbza8fhq2+w==}
|
||||||
|
engines: {node: '>=10'}
|
||||||
|
hasBin: true
|
||||||
|
dependencies:
|
||||||
|
async: 3.2.4
|
||||||
|
chalk: 4.1.2
|
||||||
|
filelist: 1.0.4
|
||||||
|
minimatch: 3.1.2
|
||||||
|
dev: true
|
||||||
|
|
||||||
/js-yaml@4.1.0:
|
/js-yaml@4.1.0:
|
||||||
resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==}
|
resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
|
|
@ -1140,6 +1390,18 @@ packages:
|
||||||
graceful-fs: 4.2.11
|
graceful-fs: 4.2.11
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/jsonfile@6.1.0:
|
||||||
|
resolution: {integrity: sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==}
|
||||||
|
dependencies:
|
||||||
|
universalify: 2.0.1
|
||||||
|
optionalDependencies:
|
||||||
|
graceful-fs: 4.2.11
|
||||||
|
dev: true
|
||||||
|
|
||||||
|
/jstat@1.9.6:
|
||||||
|
resolution: {integrity: sha512-rPBkJbK2TnA8pzs93QcDDPlKcrtZWuuCo2dVR0TFLOJSxhqfWOVCSp8aV3/oSbn+4uY4yw1URtLpHQedtmXfug==}
|
||||||
|
dev: false
|
||||||
|
|
||||||
/levn@0.4.1:
|
/levn@0.4.1:
|
||||||
resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==}
|
resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==}
|
||||||
engines: {node: '>= 0.8.0'}
|
engines: {node: '>= 0.8.0'}
|
||||||
|
|
@ -1166,6 +1428,12 @@ packages:
|
||||||
resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==}
|
resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/lower-case@2.0.2:
|
||||||
|
resolution: {integrity: sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==}
|
||||||
|
dependencies:
|
||||||
|
tslib: 2.6.0
|
||||||
|
dev: true
|
||||||
|
|
||||||
/lru-cache@6.0.0:
|
/lru-cache@6.0.0:
|
||||||
resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==}
|
resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==}
|
||||||
engines: {node: '>=10'}
|
engines: {node: '>=10'}
|
||||||
|
|
@ -1199,6 +1467,13 @@ packages:
|
||||||
brace-expansion: 1.1.11
|
brace-expansion: 1.1.11
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/minimatch@5.1.6:
|
||||||
|
resolution: {integrity: sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==}
|
||||||
|
engines: {node: '>=10'}
|
||||||
|
dependencies:
|
||||||
|
brace-expansion: 2.0.1
|
||||||
|
dev: true
|
||||||
|
|
||||||
/ms@2.1.2:
|
/ms@2.1.2:
|
||||||
resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==}
|
resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
@ -1217,11 +1492,31 @@ packages:
|
||||||
resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==}
|
resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/no-case@3.0.4:
|
||||||
|
resolution: {integrity: sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==}
|
||||||
|
dependencies:
|
||||||
|
lower-case: 2.0.2
|
||||||
|
tslib: 2.6.0
|
||||||
|
dev: true
|
||||||
|
|
||||||
|
/node-html-parser@5.4.2:
|
||||||
|
resolution: {integrity: sha512-RaBPP3+51hPne/OolXxcz89iYvQvKOydaqoePpOgXcrOKZhjVIzmpKZz+Hd/RBO2/zN2q6CNJhQzucVz+u3Jyw==}
|
||||||
|
dependencies:
|
||||||
|
css-select: 4.3.0
|
||||||
|
he: 1.2.0
|
||||||
|
dev: true
|
||||||
|
|
||||||
/normalize-path@3.0.0:
|
/normalize-path@3.0.0:
|
||||||
resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==}
|
resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==}
|
||||||
engines: {node: '>=0.10.0'}
|
engines: {node: '>=0.10.0'}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/nth-check@2.1.1:
|
||||||
|
resolution: {integrity: sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==}
|
||||||
|
dependencies:
|
||||||
|
boolbase: 1.0.0
|
||||||
|
dev: true
|
||||||
|
|
||||||
/object-assign@4.1.1:
|
/object-assign@4.1.1:
|
||||||
resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==}
|
resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==}
|
||||||
engines: {node: '>=0.10.0'}
|
engines: {node: '>=0.10.0'}
|
||||||
|
|
@ -1278,6 +1573,13 @@ packages:
|
||||||
engines: {node: '>=6'}
|
engines: {node: '>=6'}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/param-case@3.0.4:
|
||||||
|
resolution: {integrity: sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A==}
|
||||||
|
dependencies:
|
||||||
|
dot-case: 3.0.4
|
||||||
|
tslib: 2.6.0
|
||||||
|
dev: true
|
||||||
|
|
||||||
/parent-module@1.0.1:
|
/parent-module@1.0.1:
|
||||||
resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==}
|
resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==}
|
||||||
engines: {node: '>=6'}
|
engines: {node: '>=6'}
|
||||||
|
|
@ -1285,6 +1587,13 @@ packages:
|
||||||
callsites: 3.1.0
|
callsites: 3.1.0
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/pascal-case@3.1.2:
|
||||||
|
resolution: {integrity: sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g==}
|
||||||
|
dependencies:
|
||||||
|
no-case: 3.0.4
|
||||||
|
tslib: 2.6.0
|
||||||
|
dev: true
|
||||||
|
|
||||||
/path-exists@4.0.0:
|
/path-exists@4.0.0:
|
||||||
resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==}
|
resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==}
|
||||||
engines: {node: '>=8'}
|
engines: {node: '>=8'}
|
||||||
|
|
@ -1309,6 +1618,10 @@ packages:
|
||||||
engines: {node: '>=8'}
|
engines: {node: '>=8'}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/pathe@0.2.0:
|
||||||
|
resolution: {integrity: sha512-sTitTPYnn23esFR3RlqYBWn4c45WGeLcsKzQiUpXJAyfcWkolvlYpV8FLo7JishK946oQwMFUCHXQ9AjGPKExw==}
|
||||||
|
dev: true
|
||||||
|
|
||||||
/picocolors@1.0.0:
|
/picocolors@1.0.0:
|
||||||
resolution: {integrity: sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==}
|
resolution: {integrity: sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
@ -1378,6 +1691,15 @@ packages:
|
||||||
picomatch: 2.3.1
|
picomatch: 2.3.1
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/regexp-to-ast@0.5.0:
|
||||||
|
resolution: {integrity: sha512-tlbJqcMHnPKI9zSrystikWKwHkBqu2a/Sgw01h3zFjvYrMxEDYHzzoMZnUrbIfpTFEsoRnnviOXNCzFiSc54Qw==}
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/relateurl@0.2.7:
|
||||||
|
resolution: {integrity: sha512-G08Dxvm4iDN3MLM0EsP62EDV9IuhXPR6blNz6Utcp7zyV3tr4HVNINt6MpaRWbxoOHT3Q7YN2P+jaHX8vUbgog==}
|
||||||
|
engines: {node: '>= 0.10'}
|
||||||
|
dev: true
|
||||||
|
|
||||||
/resolve-from@4.0.0:
|
/resolve-from@4.0.0:
|
||||||
resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==}
|
resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==}
|
||||||
engines: {node: '>=4'}
|
engines: {node: '>=4'}
|
||||||
|
|
@ -1471,6 +1793,18 @@ packages:
|
||||||
engines: {node: '>=0.10.0'}
|
engines: {node: '>=0.10.0'}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/source-map-support@0.5.21:
|
||||||
|
resolution: {integrity: sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==}
|
||||||
|
dependencies:
|
||||||
|
buffer-from: 1.1.2
|
||||||
|
source-map: 0.6.1
|
||||||
|
dev: true
|
||||||
|
|
||||||
|
/source-map@0.6.1:
|
||||||
|
resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==}
|
||||||
|
engines: {node: '>=0.10.0'}
|
||||||
|
dev: true
|
||||||
|
|
||||||
/strip-ansi@6.0.1:
|
/strip-ansi@6.0.1:
|
||||||
resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==}
|
resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==}
|
||||||
engines: {node: '>=8'}
|
engines: {node: '>=8'}
|
||||||
|
|
@ -1502,6 +1836,17 @@ packages:
|
||||||
engines: {node: '>= 0.4'}
|
engines: {node: '>= 0.4'}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/terser@5.24.0:
|
||||||
|
resolution: {integrity: sha512-ZpGR4Hy3+wBEzVEnHvstMvqpD/nABNelQn/z2r0fjVWGQsN3bpOLzQlqDxmb4CDZnXq5lpjnQ+mHQLAOpfM5iw==}
|
||||||
|
engines: {node: '>=10'}
|
||||||
|
hasBin: true
|
||||||
|
dependencies:
|
||||||
|
'@jridgewell/source-map': 0.3.5
|
||||||
|
acorn: 8.10.0
|
||||||
|
commander: 2.20.3
|
||||||
|
source-map-support: 0.5.21
|
||||||
|
dev: true
|
||||||
|
|
||||||
/text-table@0.2.0:
|
/text-table@0.2.0:
|
||||||
resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==}
|
resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
@ -1556,12 +1901,37 @@ packages:
|
||||||
engines: {node: '>= 4.0.0'}
|
engines: {node: '>= 4.0.0'}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/universalify@2.0.1:
|
||||||
|
resolution: {integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==}
|
||||||
|
engines: {node: '>= 10.0.0'}
|
||||||
|
dev: true
|
||||||
|
|
||||||
/uri-js@4.4.1:
|
/uri-js@4.4.1:
|
||||||
resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==}
|
resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==}
|
||||||
dependencies:
|
dependencies:
|
||||||
punycode: 2.3.0
|
punycode: 2.3.0
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/vite-plugin-html@3.2.0(vite@4.4.0):
|
||||||
|
resolution: {integrity: sha512-2VLCeDiHmV/BqqNn5h2V+4280KRgQzCFN47cst3WiNK848klESPQnzuC3okH5XHtgwHH/6s1Ho/YV6yIO0pgoQ==}
|
||||||
|
peerDependencies:
|
||||||
|
vite: '>=2.0.0'
|
||||||
|
dependencies:
|
||||||
|
'@rollup/pluginutils': 4.2.1
|
||||||
|
colorette: 2.0.20
|
||||||
|
connect-history-api-fallback: 1.6.0
|
||||||
|
consola: 2.15.3
|
||||||
|
dotenv: 16.3.1
|
||||||
|
dotenv-expand: 8.0.3
|
||||||
|
ejs: 3.1.9
|
||||||
|
fast-glob: 3.3.1
|
||||||
|
fs-extra: 10.1.0
|
||||||
|
html-minifier-terser: 6.1.0
|
||||||
|
node-html-parser: 5.4.2
|
||||||
|
pathe: 0.2.0
|
||||||
|
vite: 4.4.0(@types/node@20.4.4)(sass@1.63.6)
|
||||||
|
dev: true
|
||||||
|
|
||||||
/vite@4.4.0(@types/node@20.4.4)(sass@1.63.6):
|
/vite@4.4.0(@types/node@20.4.4)(sass@1.63.6):
|
||||||
resolution: {integrity: sha512-Wf+DCEjuM8aGavEYiF77hnbxEZ+0+/jC9nABR46sh5Xi+GYeSvkeEFRiVuI3x+tPjxgZeS91h1jTAQTPFgePpA==}
|
resolution: {integrity: sha512-Wf+DCEjuM8aGavEYiF77hnbxEZ+0+/jC9nABR46sh5Xi+GYeSvkeEFRiVuI3x+tPjxgZeS91h1jTAQTPFgePpA==}
|
||||||
engines: {node: ^14.18.0 || >=16.0.0}
|
engines: {node: ^14.18.0 || >=16.0.0}
|
||||||
|
|
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 36 KiB |
|
|
@ -1,54 +1,54 @@
|
||||||
import Spreadsheet, { RenderBox } from "../main"
|
import Spreadsheet, { RenderBox } from "../main";
|
||||||
|
|
||||||
export class ColumnsBar {
|
export class ColumnsBar {
|
||||||
public element: HTMLCanvasElement
|
public element: HTMLCanvasElement;
|
||||||
private root: Spreadsheet
|
private root: Spreadsheet;
|
||||||
public height: number = 32
|
public height: number = 35;
|
||||||
public width: number
|
public width: number;
|
||||||
private resizerWidth = 1
|
// private resizerWidth = 1;
|
||||||
ctx: CanvasRenderingContext2D
|
ctx: CanvasRenderingContext2D;
|
||||||
|
|
||||||
constructor(root: Spreadsheet) {
|
constructor(root: Spreadsheet) {
|
||||||
this.root = root
|
this.root = root;
|
||||||
this.element = this.createElement()
|
this.element = this.createElement();
|
||||||
const ctx = this.element.getContext('2d')
|
const ctx = this.element.getContext("2d");
|
||||||
if (!ctx) throw new Error("Enable hardware acceleration");
|
if (!ctx) throw new Error("Enable hardware acceleration");
|
||||||
this.ctx = ctx
|
this.ctx = ctx;
|
||||||
|
|
||||||
this.width = this.root.viewProps.width
|
this.width = this.root.viewProps.width;
|
||||||
}
|
}
|
||||||
|
|
||||||
private createElement(): HTMLCanvasElement {
|
private createElement(): HTMLCanvasElement {
|
||||||
const element = document.createElement('canvas')
|
const element = document.createElement("canvas");
|
||||||
element.style.position = 'absolute'
|
element.style.position = "absolute";
|
||||||
element.style.height = this.height + 'px'
|
element.style.height = this.height + "px";
|
||||||
element.style.width = this.root.viewProps.width + 'px'
|
element.style.width = this.root.viewProps.width + "px";
|
||||||
element.style.display = 'block'
|
element.style.display = "block";
|
||||||
element.style.borderLeft = '1px solid black'
|
element.style.borderLeft = "1px solid black";
|
||||||
// element.style.boxSizing = 'border-box'
|
// element.style.boxSizing = 'border-box'
|
||||||
|
|
||||||
|
element.width = this.root.viewProps.width;
|
||||||
element.width = this.root.viewProps.width
|
element.height = this.height;
|
||||||
element.height = this.height
|
return element;
|
||||||
return element
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public setElementPosition(top: number, left: number) {
|
public setElementPosition(top: number, left: number) {
|
||||||
this.element.style.top = top + 'px'
|
this.element.style.top = top + "px";
|
||||||
this.element.style.left = left + 'px'
|
this.element.style.left = left + "px";
|
||||||
}
|
}
|
||||||
|
|
||||||
private isColumnSelected(column: number): boolean {
|
private isColumnSelected(column: number): boolean {
|
||||||
const { selectedCell, selectedRange } = this.root.selection
|
const { selectedCell, selectedRange } = this.root.selection;
|
||||||
if (selectedCell && selectedCell.column === column) return true
|
if (selectedCell && selectedCell.column === column) return true;
|
||||||
if (selectedRange) {
|
if (selectedRange) {
|
||||||
const inRange =
|
const inRange =
|
||||||
column >= Math.min(selectedRange.from.column, selectedRange.to.column) &&
|
column >=
|
||||||
column <= Math.max(selectedRange.from.column, selectedRange.to.column)
|
Math.min(selectedRange.from.column, selectedRange.to.column) &&
|
||||||
|
column <= Math.max(selectedRange.from.column, selectedRange.to.column);
|
||||||
|
|
||||||
return inRange
|
return inRange;
|
||||||
}
|
}
|
||||||
return false
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// private getYCoordWithOffset(renderBox: RenderBox): number {
|
// private getYCoordWithOffset(renderBox: RenderBox): number {
|
||||||
|
|
@ -64,38 +64,42 @@ export class ColumnsBar {
|
||||||
// }
|
// }
|
||||||
|
|
||||||
private renderText(column: number, renderBox: RenderBox) {
|
private renderText(column: number, renderBox: RenderBox) {
|
||||||
const { width, x } = renderBox
|
const { width, x } = renderBox;
|
||||||
|
|
||||||
this.ctx.fillStyle = 'black'
|
this.ctx.fillStyle = "black";
|
||||||
this.ctx.textAlign = 'center'
|
this.ctx.textAlign = "center";
|
||||||
this.ctx.textBaseline = 'middle'
|
this.ctx.textBaseline = "middle";
|
||||||
this.ctx.font = '16px Arial'
|
this.ctx.font = "12px Arial";
|
||||||
this.ctx.fillText(this.root.config.columns[column].title, x + (width / 2) - this.root.viewport.left, 0 + this.height / 2)
|
this.ctx.fillText(
|
||||||
|
this.root.config.columns[column].title,
|
||||||
|
x + width / 2 - this.root.viewport.left,
|
||||||
|
0 + this.height / 2,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private renderRect(column: number, renderBox: RenderBox) {
|
private renderRect(column: number, renderBox: RenderBox) {
|
||||||
const { width, x } = renderBox
|
const { width, x } = renderBox;
|
||||||
|
|
||||||
const isColSelected = this.isColumnSelected(column)
|
const isColSelected = this.isColumnSelected(column);
|
||||||
|
|
||||||
this.ctx.fillStyle = isColSelected ? this.root.styles.cells.selectedBackground : 'white'
|
this.ctx.fillStyle = isColSelected ? "#c7ebff" : "white";
|
||||||
this.ctx.strokeStyle = 'black'
|
this.ctx.strokeStyle = "black";
|
||||||
this.ctx.lineWidth = 1
|
this.ctx.lineWidth = 1;
|
||||||
|
|
||||||
const specialX = x - this.root.viewport.left
|
const specialX = x - this.root.viewport.left;
|
||||||
|
|
||||||
this.ctx.fillRect(specialX - 1 , 0, width, this.height)
|
this.ctx.fillRect(specialX - 1, 0, width, this.height);
|
||||||
this.ctx.strokeRect(specialX - 1, 0, width, this.height)
|
this.ctx.strokeRect(specialX - 1, 0, width, this.height);
|
||||||
}
|
}
|
||||||
|
|
||||||
private renderSingleColumn(column: number) {
|
private renderSingleColumn(column: number) {
|
||||||
const renderBox = new RenderBox(this.root.config, {
|
const renderBox = new RenderBox(this.root.config, {
|
||||||
row: 0,
|
row: 0,
|
||||||
column: column
|
column: column,
|
||||||
})
|
});
|
||||||
|
|
||||||
this.renderRect(column, renderBox)
|
this.renderRect(column, renderBox);
|
||||||
this.renderText(column, renderBox)
|
this.renderText(column, renderBox);
|
||||||
}
|
}
|
||||||
|
|
||||||
public renderBar() {
|
public renderBar() {
|
||||||
|
|
@ -103,7 +107,7 @@ export class ColumnsBar {
|
||||||
const firstColIdx = this.root.viewport.firstCol;
|
const firstColIdx = this.root.viewport.firstCol;
|
||||||
|
|
||||||
this.ctx.beginPath();
|
this.ctx.beginPath();
|
||||||
this.ctx.strokeStyle = 'black';
|
this.ctx.strokeStyle = "black";
|
||||||
this.ctx.lineWidth = 1;
|
this.ctx.lineWidth = 1;
|
||||||
this.ctx.moveTo(0, 0);
|
this.ctx.moveTo(0, 0);
|
||||||
this.ctx.lineTo(0, this.height);
|
this.ctx.lineTo(0, this.height);
|
||||||
|
|
@ -111,8 +115,8 @@ export class ColumnsBar {
|
||||||
this.ctx.stroke();
|
this.ctx.stroke();
|
||||||
|
|
||||||
for (let col = firstColIdx; col <= lastColIdx; col++) {
|
for (let col = firstColIdx; col <= lastColIdx; col++) {
|
||||||
if(!this.root.config.columns[col]) break;
|
if (!this.root.config.columns[col]) break;
|
||||||
this.renderSingleColumn(col)
|
this.renderSingleColumn(col);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
import Spreadsheet, { CSS_PREFIX } from "../main";
|
import Spreadsheet, { CSS_PREFIX } from "../main";
|
||||||
import { Position } from "../modules/cell";
|
import { Position } from "../modules/cell";
|
||||||
|
import { EventTypes } from "../modules/events";
|
||||||
import { RenderBox } from "../modules/renderBox";
|
import { RenderBox } from "../modules/renderBox";
|
||||||
|
|
||||||
export class Editor {
|
export class Editor {
|
||||||
|
|
@ -28,8 +29,10 @@ export class Editor {
|
||||||
const cell = this.root.getCell(position);
|
const cell = this.root.getCell(position);
|
||||||
this.element.classList.remove("hide");
|
this.element.classList.remove("hide");
|
||||||
|
|
||||||
this.element.style.top = y - this.root.viewport.top + this.root.columnsBarHeight + "px";
|
this.element.style.top =
|
||||||
this.element.style.left = x - this.root.viewport.left + this.root.rowsBarWidth + "px";
|
y - this.root.viewport.top + this.root.columnsBarHeight + "px";
|
||||||
|
this.element.style.left =
|
||||||
|
x - this.root.viewport.left + this.root.rowsBarWidth + "px";
|
||||||
this.element.style.width = width + "px";
|
this.element.style.width = width + "px";
|
||||||
this.element.style.height = height + "px";
|
this.element.style.height = height + "px";
|
||||||
this.element.style.display = "block";
|
this.element.style.display = "block";
|
||||||
|
|
@ -50,11 +53,20 @@ export class Editor {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case "Enter": {
|
case "Enter": {
|
||||||
this.root.changeCellValues(this.root.selection.selectedCell!, {
|
if (!this.root.selection.selectedCell) return;
|
||||||
|
|
||||||
|
this.root.changeCellValues(this.root.selection.selectedCell, {
|
||||||
value: this.element.value,
|
value: this.element.value,
|
||||||
displayValue: this.element.value,
|
displayValue: this.element.value,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.root.events.dispatch({
|
||||||
|
type: EventTypes.CELL_CHANGE,
|
||||||
|
cell: this.root.getCell(this.root.selection.selectedCell),
|
||||||
|
});
|
||||||
|
|
||||||
this.hide();
|
this.hide();
|
||||||
|
this.root.renderSelection();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,91 +1,92 @@
|
||||||
import Spreadsheet, { RenderBox } from "../main";
|
import Spreadsheet, { RenderBox } from "../main";
|
||||||
|
|
||||||
export class RowsBar {
|
export class RowsBar {
|
||||||
element: HTMLCanvasElement
|
element: HTMLCanvasElement;
|
||||||
ctx: CanvasRenderingContext2D
|
ctx: CanvasRenderingContext2D;
|
||||||
root: Spreadsheet
|
root: Spreadsheet;
|
||||||
width: number = 30
|
width: number = 35;
|
||||||
height: number
|
height: number;
|
||||||
resizerHeight = 1
|
resizerHeight = 1;
|
||||||
constructor(root: Spreadsheet) {
|
constructor(root: Spreadsheet) {
|
||||||
this.root = root
|
this.root = root;
|
||||||
this.element = this.createElement()
|
this.element = this.createElement();
|
||||||
const ctx = this.element.getContext('2d')
|
const ctx = this.element.getContext("2d");
|
||||||
if (!ctx) throw new Error("Enable hardware acceleration");
|
if (!ctx) throw new Error("Enable hardware acceleration");
|
||||||
this.ctx = ctx
|
this.ctx = ctx;
|
||||||
|
|
||||||
this.height = this.root.viewProps.height
|
this.height = this.root.viewProps.height;
|
||||||
}
|
}
|
||||||
|
|
||||||
private createElement() {
|
private createElement() {
|
||||||
const element = document.createElement('canvas')
|
const element = document.createElement("canvas");
|
||||||
|
|
||||||
element.style.position = 'absolute'
|
element.style.position = "absolute";
|
||||||
element.style.height = this.root.viewProps.height + 'px'
|
element.style.height = this.root.viewProps.height + "px";
|
||||||
element.style.width = this.width + 'px'
|
element.style.width = this.width + "px";
|
||||||
element.style.display = 'block'
|
element.style.display = "block";
|
||||||
element.style.borderTop = '1px solid black'
|
element.style.borderTop = "1px solid black";
|
||||||
// element.style.boxSizing = 'border-box'
|
// element.style.boxSizing = 'border-box'
|
||||||
|
|
||||||
|
element.width = this.width;
|
||||||
element.width = this.width
|
element.height = this.root.viewProps.height;
|
||||||
element.height = this.root.viewProps.height
|
return element;
|
||||||
return element
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public setElementPosition(top: number, left: number) {
|
public setElementPosition(top: number, left: number) {
|
||||||
this.element.style.top = top + 'px'
|
this.element.style.top = top + "px";
|
||||||
this.element.style.left = left + 'px'
|
this.element.style.left = left + "px";
|
||||||
}
|
}
|
||||||
|
|
||||||
private isRowSelected(row: number): boolean {
|
private isRowSelected(row: number): boolean {
|
||||||
const { selectedCell, selectedRange } = this.root.selection
|
const { selectedCell, selectedRange } = this.root.selection;
|
||||||
if (selectedCell && selectedCell.row === row) return true
|
if (selectedCell && selectedCell.row === row) return true;
|
||||||
if (selectedRange) {
|
if (selectedRange) {
|
||||||
const inRange =
|
const inRange =
|
||||||
row >= Math.min(selectedRange.from.row, selectedRange.to.row) &&
|
row >= Math.min(selectedRange.from.row, selectedRange.to.row) &&
|
||||||
row <= Math.max(selectedRange.from.row, selectedRange.to.row)
|
row <= Math.max(selectedRange.from.row, selectedRange.to.row);
|
||||||
|
|
||||||
return inRange
|
return inRange;
|
||||||
}
|
}
|
||||||
return false
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private renderText(row: number, renderBox: RenderBox) {
|
private renderText(row: number, renderBox: RenderBox) {
|
||||||
const { y, height } = renderBox
|
const { y, height } = renderBox;
|
||||||
|
|
||||||
this.ctx.fillStyle = 'black'
|
this.ctx.fillStyle = "black";
|
||||||
this.ctx.textAlign = 'center'
|
this.ctx.textAlign = "center";
|
||||||
this.ctx.textBaseline = 'middle'
|
this.ctx.textBaseline = "middle";
|
||||||
this.ctx.font = '16px Arial'
|
this.ctx.font = "12px Arial";
|
||||||
this.ctx.fillText(this.root.config.rows[row].title, this.width / 2, y - this.root.viewport.top + height / 2)
|
this.ctx.fillText(
|
||||||
|
this.root.config.rows[row].title,
|
||||||
|
this.width / 2,
|
||||||
|
y - this.root.viewport.top + height / 2,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private renderRect(column: number, renderBox: RenderBox) {
|
private renderRect(column: number, renderBox: RenderBox) {
|
||||||
const { y, height } = renderBox
|
const { y, height } = renderBox;
|
||||||
|
|
||||||
|
const isRowSeleted = this.isRowSelected(column);
|
||||||
|
|
||||||
const isRowSeleted = this.isRowSelected(column)
|
this.ctx.fillStyle = isRowSeleted ? "#c7ebff" : "white";
|
||||||
|
this.ctx.strokeStyle = "black";
|
||||||
|
this.ctx.lineWidth = this.resizerHeight;
|
||||||
|
|
||||||
this.ctx.fillStyle = isRowSeleted ? this.root.styles.cells.selectedBackground : 'white'
|
const specialY = y - this.root.viewport.top;
|
||||||
this.ctx.strokeStyle = 'black'
|
|
||||||
this.ctx.lineWidth = this.resizerHeight
|
|
||||||
|
|
||||||
const specialY = y - this.root.viewport.top
|
this.ctx.fillRect(0, specialY - 1, this.width, height);
|
||||||
|
this.ctx.strokeRect(0, specialY - 1, this.width, height);
|
||||||
this.ctx.fillRect(0, specialY - 1, this.width, height )
|
|
||||||
this.ctx.strokeRect(0, specialY - 1, this.width, height )
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private renderSingleRow(row: number) {
|
private renderSingleRow(row: number) {
|
||||||
const renderBox = new RenderBox(this.root.config, {
|
const renderBox = new RenderBox(this.root.config, {
|
||||||
column: 0,
|
column: 0,
|
||||||
row: row
|
row: row,
|
||||||
})
|
});
|
||||||
|
|
||||||
this.renderRect(row, renderBox)
|
this.renderRect(row, renderBox);
|
||||||
this.renderText(row, renderBox)
|
this.renderText(row, renderBox);
|
||||||
}
|
}
|
||||||
|
|
||||||
public renderBar() {
|
public renderBar() {
|
||||||
|
|
@ -94,16 +95,15 @@ export class RowsBar {
|
||||||
|
|
||||||
this.ctx.beginPath();
|
this.ctx.beginPath();
|
||||||
this.ctx.moveTo(0, 0);
|
this.ctx.moveTo(0, 0);
|
||||||
this.ctx.strokeStyle = 'black';
|
this.ctx.strokeStyle = "black";
|
||||||
this.ctx.lineWidth = 16;
|
this.ctx.lineWidth = 16;
|
||||||
this.ctx.lineTo(35, 0);
|
this.ctx.lineTo(35, 0);
|
||||||
this.ctx.closePath();
|
this.ctx.closePath();
|
||||||
this.ctx.stroke();
|
this.ctx.stroke();
|
||||||
|
|
||||||
for (let row = firstRowIdx; row <= lastRowIdx; row++) {
|
for (let row = firstRowIdx; row <= lastRowIdx; row++) {
|
||||||
if(!this.root.config.rows[row]) break;
|
if (!this.root.config.rows[row]) break;
|
||||||
this.renderSingleRow(row)
|
this.renderSingleRow(row);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -1,4 +1,6 @@
|
||||||
import Spreadsheet, { CSS_PREFIX } from "../main";
|
import Spreadsheet, { CSS_PREFIX, Cell, Selection } from "../main";
|
||||||
|
import { EventTypes } from "../modules/events";
|
||||||
|
import { checkEqualCellSelections } from "../utils/position";
|
||||||
|
|
||||||
export interface ViewportRect {
|
export interface ViewportRect {
|
||||||
top: number;
|
top: number;
|
||||||
|
|
@ -12,7 +14,6 @@ export class Scroller {
|
||||||
private verticalScroller: HTMLDivElement;
|
private verticalScroller: HTMLDivElement;
|
||||||
private horizontalScroller: HTMLDivElement;
|
private horizontalScroller: HTMLDivElement;
|
||||||
private root: Spreadsheet;
|
private root: Spreadsheet;
|
||||||
|
|
||||||
private isSelecting = false;
|
private isSelecting = false;
|
||||||
|
|
||||||
constructor(root: Spreadsheet) {
|
constructor(root: Spreadsheet) {
|
||||||
|
|
@ -25,8 +26,8 @@ export class Scroller {
|
||||||
|
|
||||||
this.element.style.height = this.root.config.view.height + "px";
|
this.element.style.height = this.root.config.view.height + "px";
|
||||||
this.element.style.width = this.root.config.view.width + "px";
|
this.element.style.width = this.root.config.view.width + "px";
|
||||||
this.element.style.top = this.root.columnsBarHeight + 'px'
|
this.element.style.top = this.root.columnsBarHeight + "px";
|
||||||
this.element.style.left = this.root.rowsBarWidth + 'px'
|
this.element.style.left = this.root.rowsBarWidth + "px";
|
||||||
this.element.tabIndex = -1;
|
this.element.tabIndex = -1;
|
||||||
|
|
||||||
this.updateScrollerSize(); //* Init size set
|
this.updateScrollerSize(); //* Init size set
|
||||||
|
|
@ -39,31 +40,61 @@ export class Scroller {
|
||||||
this.element.addEventListener("dblclick", this.handleDoubleClick);
|
this.element.addEventListener("dblclick", this.handleDoubleClick);
|
||||||
|
|
||||||
this.element.addEventListener("keydown", this.handleKeydown);
|
this.element.addEventListener("keydown", this.handleKeydown);
|
||||||
|
this.element.addEventListener("paste", (event) => {
|
||||||
|
if (!this.root.selection.selectedCell) return;
|
||||||
|
this.root.clipboard.paste(
|
||||||
|
this.root,
|
||||||
|
this.root.selection.selectedCell,
|
||||||
|
event,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public setSelectingMode(mode: boolean) {
|
||||||
|
this.isSelecting = mode;
|
||||||
}
|
}
|
||||||
|
|
||||||
private handleMouseMove = (event: MouseEvent) => {
|
private handleMouseMove = (event: MouseEvent) => {
|
||||||
if (!this.isSelecting) return;
|
if (!this.isSelecting) return;
|
||||||
const { offsetX, offsetY } = event;
|
const { offsetX, offsetY } = event;
|
||||||
const lastSelectedCell = this.root.getCellByCoords(offsetX, offsetY);
|
const lastSelectedCell = this.root.getCellByCoords(offsetX, offsetY);
|
||||||
|
|
||||||
|
let isRangeChanged = false;
|
||||||
|
|
||||||
if (this.root.selection.selectedRange) {
|
if (this.root.selection.selectedRange) {
|
||||||
|
isRangeChanged = !checkEqualCellSelections(
|
||||||
|
this.root.selection.selectedRange.to,
|
||||||
|
lastSelectedCell,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (isRangeChanged) {
|
||||||
this.root.selection.selectedRange.to = lastSelectedCell;
|
this.root.selection.selectedRange.to = lastSelectedCell;
|
||||||
|
this.root.events.dispatch({
|
||||||
|
type: EventTypes.SELECTION_CHANGE,
|
||||||
|
selection: this.root.selection,
|
||||||
|
enableCallback: true,
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
this.root.renderSheet();
|
|
||||||
this.root.renderColumnsBar();
|
|
||||||
this.root.renderRowsBar();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
private handleMouseUp = () => {
|
private handleMouseUp = () => {
|
||||||
this.isSelecting = false;
|
this.isSelecting = false;
|
||||||
|
const newSelection = { ...this.root.selection };
|
||||||
|
|
||||||
if (this.root.selection.selectedRange) {
|
if (this.root.selection.selectedRange) {
|
||||||
if (
|
if (
|
||||||
this.root.selection.selectedRange.from.row ===
|
checkEqualCellSelections(
|
||||||
this.root.selection.selectedRange.to.row &&
|
this.root.selection.selectedRange.from,
|
||||||
this.root.selection.selectedRange.from.column ===
|
this.root.selection.selectedRange.to,
|
||||||
this.root.selection.selectedRange.to.column
|
)
|
||||||
) {
|
) {
|
||||||
this.root.selection.selectedRange = null;
|
newSelection.selectedRange = null;
|
||||||
|
this.root.events.dispatch({
|
||||||
|
type: EventTypes.SELECTION_CHANGE,
|
||||||
|
selection: newSelection,
|
||||||
|
enableCallback: false,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -79,8 +110,8 @@ export class Scroller {
|
||||||
};
|
};
|
||||||
|
|
||||||
private handleKeydown = (event: KeyboardEvent) => {
|
private handleKeydown = (event: KeyboardEvent) => {
|
||||||
console.log(event);
|
|
||||||
//* Navigation
|
//* Navigation
|
||||||
|
|
||||||
if (
|
if (
|
||||||
["ArrowLeft", "ArrowRight", "ArrowUp", "ArrowDown"].includes(event.key)
|
["ArrowLeft", "ArrowRight", "ArrowUp", "ArrowDown"].includes(event.key)
|
||||||
) {
|
) {
|
||||||
|
|
@ -92,9 +123,8 @@ export class Scroller {
|
||||||
this.root.selection.selectedCell &&
|
this.root.selection.selectedCell &&
|
||||||
this.root.selection.selectedCell.column > 0
|
this.root.selection.selectedCell.column > 0
|
||||||
) {
|
) {
|
||||||
console.log("tick");
|
|
||||||
this.root.selection.selectedCell.column -= 1;
|
this.root.selection.selectedCell.column -= 1;
|
||||||
this.root.renderSheet();
|
// this.root.renderSheet();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
@ -105,7 +135,7 @@ export class Scroller {
|
||||||
this.root.config.columns.length - 1
|
this.root.config.columns.length - 1
|
||||||
) {
|
) {
|
||||||
this.root.selection.selectedCell.column += 1;
|
this.root.selection.selectedCell.column += 1;
|
||||||
this.root.renderSheet();
|
// this.root.renderSheet();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
@ -115,7 +145,7 @@ export class Scroller {
|
||||||
this.root.selection.selectedCell.row > 0
|
this.root.selection.selectedCell.row > 0
|
||||||
) {
|
) {
|
||||||
this.root.selection.selectedCell.row -= 1;
|
this.root.selection.selectedCell.row -= 1;
|
||||||
this.root.renderSheet();
|
// this.root.renderSheet();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
@ -126,13 +156,20 @@ export class Scroller {
|
||||||
this.root.config.rows.length - 1
|
this.root.config.rows.length - 1
|
||||||
) {
|
) {
|
||||||
this.root.selection.selectedCell.row += 1;
|
this.root.selection.selectedCell.row += 1;
|
||||||
this.root.renderSheet();
|
// this.root.renderSheet();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
this.root.events.dispatch({
|
||||||
|
type: EventTypes.SELECTION_CHANGE,
|
||||||
|
selection: this.root.selection,
|
||||||
|
enableCallback: true,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
const keysRegex = /^([a-z]|[а-я])$/;
|
|
||||||
|
//* Start typings
|
||||||
|
const keysRegex = /^([a-z]|[а-я]|[0-9]|=)$/;
|
||||||
if (!event.metaKey && !event.ctrlKey) {
|
if (!event.metaKey && !event.ctrlKey) {
|
||||||
//* Prevent handle shortcutrs
|
//* Prevent handle shortcutrs
|
||||||
const isPressedLetterKey = keysRegex.test(event.key.toLowerCase());
|
const isPressedLetterKey = keysRegex.test(event.key.toLowerCase());
|
||||||
|
|
@ -154,32 +191,62 @@ export class Scroller {
|
||||||
this.root.deleteSelectedCellsValues();
|
this.root.deleteSelectedCellsValues();
|
||||||
this.root.renderSheet();
|
this.root.renderSheet();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (event.metaKey || event.ctrlKey) {
|
||||||
|
if (event.code === "KeyC") {
|
||||||
|
let cells: Cell[][] = undefined!;
|
||||||
|
const selection = new Selection();
|
||||||
|
|
||||||
|
if (this.root.selection.selectedRange) {
|
||||||
|
const { from, to } = this.root.selection.selectedRange;
|
||||||
|
|
||||||
|
selection.selectedRange = this.root.selection.selectedRange;
|
||||||
|
|
||||||
|
const subArrByRows = this.root.data.slice(from.row, to.row + 1);
|
||||||
|
|
||||||
|
const subArrByCols = subArrByRows.map((row) => {
|
||||||
|
return row.slice(from.column, to.column + 1);
|
||||||
|
});
|
||||||
|
|
||||||
|
cells = [...subArrByCols];
|
||||||
|
} else if (this.root.selection.selectedCell) {
|
||||||
|
const { column, row } = this.root.selection.selectedCell;
|
||||||
|
cells = [[this.root.data[row][column]]];
|
||||||
|
selection.selectedRange = {
|
||||||
|
from: this.root.selection.selectedCell,
|
||||||
|
to: this.root.selection.selectedCell,
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.root.clipboard.copy(cells, selection.selectedRange);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (event.code === "KeyV") {
|
||||||
|
// if (!this.root.selection.selectedCell) return;
|
||||||
|
// this.root.clipboard.paste(this.root, this.root.selection.selectedCell);
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
private handleClick = (event: MouseEvent) => {
|
private handleClick = (event: MouseEvent) => {
|
||||||
if (event.button !== 0) return; // Left mouse button
|
this.root.events.dispatch({
|
||||||
const { offsetX, offsetY } = event;
|
type: EventTypes.CELL_CLICK,
|
||||||
const clickedCell = this.root.getCellByCoords(offsetX, offsetY);
|
event,
|
||||||
this.isSelecting = true;
|
scroller: this,
|
||||||
this.root.selection.selectedRange = {
|
});
|
||||||
from: clickedCell,
|
|
||||||
to: clickedCell,
|
|
||||||
};
|
};
|
||||||
this.root.selection.selectedCell = clickedCell;
|
|
||||||
|
|
||||||
this.root.renderSheet();
|
|
||||||
this.root.renderColumnsBar()
|
|
||||||
|
|
||||||
this.root.renderRowsBar(); };
|
|
||||||
|
|
||||||
private handleScroll = () => {
|
private handleScroll = () => {
|
||||||
const rect = this.getViewportBoundlingRect();
|
const rect = this.getViewportBoundlingRect();
|
||||||
this.root.viewport.updateValues(rect);
|
this.root.viewport.updateValues(rect);
|
||||||
|
|
||||||
this.root.renderSheet();
|
this.root.renderSheet();
|
||||||
this.root.renderColumnsBar()
|
this.root.renderColumnsBar();
|
||||||
|
|
||||||
this.root.renderRowsBar(); };
|
this.root.renderRowsBar();
|
||||||
|
};
|
||||||
|
|
||||||
public getViewportBoundlingRect(): ViewportRect {
|
public getViewportBoundlingRect(): ViewportRect {
|
||||||
const { scrollTop, scrollLeft } = this.element;
|
const { scrollTop, scrollLeft } = this.element;
|
||||||
|
|
@ -215,6 +282,7 @@ this.root.renderRowsBar(); };
|
||||||
this.verticalScroller = verticalScroller;
|
this.verticalScroller = verticalScroller;
|
||||||
this.horizontalScroller = horizontalScroller;
|
this.horizontalScroller = horizontalScroller;
|
||||||
scroller.appendChild(groupScrollers);
|
scroller.appendChild(groupScrollers);
|
||||||
|
scroller.contentEditable = "false";
|
||||||
scroller.classList.add(CSS_PREFIX + "scroller");
|
scroller.classList.add(CSS_PREFIX + "scroller");
|
||||||
|
|
||||||
return { scroller, verticalScroller, horizontalScroller };
|
return { scroller, verticalScroller, horizontalScroller };
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import Spreadsheet, { CSS_PREFIX } from "../main";
|
import Spreadsheet, { CSS_PREFIX, RenderBox } from "../main";
|
||||||
import { Position } from "../modules/cell";
|
import { Position } from "../modules/cell";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -18,7 +18,7 @@ export class Sheet {
|
||||||
canvas.width = this.root.config.view.width;
|
canvas.width = this.root.config.view.width;
|
||||||
canvas.style.width = this.root.config.view.width + "px";
|
canvas.style.width = this.root.config.view.width + "px";
|
||||||
canvas.style.height = this.root.config.view.height + "px";
|
canvas.style.height = this.root.config.view.height + "px";
|
||||||
canvas.style.left = '0px'
|
canvas.style.left = "0px";
|
||||||
|
|
||||||
this.element = canvas;
|
this.element = canvas;
|
||||||
|
|
||||||
|
|
@ -52,12 +52,79 @@ export class Sheet {
|
||||||
this.root.data[row][column].render(this.root);
|
this.root.data[row][column].render(this.root);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private getSelectionRange() {
|
||||||
|
const { selectedCell, selectedRange } = this.root.selection;
|
||||||
|
|
||||||
|
if (!selectedCell && !selectedRange) return;
|
||||||
|
if (selectedRange) {
|
||||||
|
const startRow = Math.min(selectedRange.from.row, selectedRange.to.row);
|
||||||
|
const startCol = Math.min(
|
||||||
|
selectedRange.from.column,
|
||||||
|
selectedRange.to.column,
|
||||||
|
);
|
||||||
|
const lastRow = Math.max(selectedRange.from.row, selectedRange.to.row);
|
||||||
|
const lastCol = Math.max(
|
||||||
|
selectedRange.from.column,
|
||||||
|
selectedRange.to.column,
|
||||||
|
);
|
||||||
|
|
||||||
|
const startCellBox = new RenderBox(this.root.config, {
|
||||||
|
row: startRow,
|
||||||
|
column: startCol,
|
||||||
|
});
|
||||||
|
|
||||||
|
let width = 0;
|
||||||
|
for (let col = startCol; col <= lastCol; col++) {
|
||||||
|
width += this.root.config.columns[col].width;
|
||||||
|
}
|
||||||
|
|
||||||
|
let height = 0;
|
||||||
|
for (let row = startRow; row <= lastRow; row++) {
|
||||||
|
height += this.root.config.rows[row].height;
|
||||||
|
}
|
||||||
|
|
||||||
|
const x = startCellBox.x - this.root.viewport.left;
|
||||||
|
const y = startCellBox.y - this.root.viewport.top;
|
||||||
|
|
||||||
|
return { x, y, height, width };
|
||||||
|
}
|
||||||
|
if (!selectedRange && selectedCell) {
|
||||||
|
const box = new RenderBox(this.root.config, selectedCell);
|
||||||
|
box.x -= this.root.viewport.left;
|
||||||
|
box.y -= this.root.viewport.top;
|
||||||
|
return box;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private renderSelectionRange(
|
||||||
|
x: number,
|
||||||
|
y: number,
|
||||||
|
width: number,
|
||||||
|
height: number,
|
||||||
|
) {
|
||||||
|
this.ctx.save();
|
||||||
|
this.ctx.strokeStyle = "#7da8ff";
|
||||||
|
this.ctx.lineWidth = 3;
|
||||||
|
this.ctx.strokeRect(x, y, width, height);
|
||||||
|
this.ctx.fillStyle = "#7da8ff35";
|
||||||
|
this.ctx.fillRect(x, y, width, height);
|
||||||
|
this.ctx.restore();
|
||||||
|
}
|
||||||
|
|
||||||
|
renderSelection() {
|
||||||
|
const box = this.getSelectionRange();
|
||||||
|
if (!box) return;
|
||||||
|
const { height, width, x, y } = box;
|
||||||
|
this.renderSelectionRange(x, y, width, height);
|
||||||
|
}
|
||||||
|
|
||||||
renderSheet() {
|
renderSheet() {
|
||||||
const firstRowIdx = this.root.viewport.firstRow;
|
const firstRowIdx = this.root.viewport.firstRow;
|
||||||
const lastColIdx = this.root.viewport.lastCol + 3;
|
const lastColIdx = this.root.viewport.lastCol + 3;
|
||||||
const lastRowIdx = this.root.viewport.lastRow + 3;
|
const lastRowIdx = this.root.viewport.lastRow + 3;
|
||||||
const firstColIdx = this.root.viewport.firstCol;
|
const firstColIdx = this.root.viewport.firstCol;
|
||||||
|
|
||||||
|
|
||||||
for (let row = firstRowIdx; row <= lastRowIdx; row++) {
|
for (let row = firstRowIdx; row <= lastRowIdx; row++) {
|
||||||
for (let col = firstColIdx; col <= lastColIdx; col++) {
|
for (let col = firstColIdx; col <= lastColIdx; col++) {
|
||||||
if (!this.root.config.columns[col] || !this.root.config.rows[row])
|
if (!this.root.config.columns[col] || !this.root.config.rows[row])
|
||||||
|
|
@ -66,5 +133,6 @@ export class Sheet {
|
||||||
this.renderCell({ column: col, row });
|
this.renderCell({ column: col, row });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
this.renderSelection();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ import Spreadsheet, { CSS_PREFIX } from "../main";
|
||||||
export class Toolbar {
|
export class Toolbar {
|
||||||
element: HTMLDivElement;
|
element: HTMLDivElement;
|
||||||
root: Spreadsheet;
|
root: Spreadsheet;
|
||||||
height: number = 0
|
height: number = 0;
|
||||||
constructor(root: Spreadsheet) {
|
constructor(root: Spreadsheet) {
|
||||||
this.root = root;
|
this.root = root;
|
||||||
const toolbarElement = document.createElement("div");
|
const toolbarElement = document.createElement("div");
|
||||||
|
|
|
||||||
27
src/index.ts
27
src/index.ts
|
|
@ -1,11 +1,21 @@
|
||||||
import Spreadsheet from "./main";
|
import Spreadsheet, { SpreadsheetConstructorProperties } from "./main";
|
||||||
|
|
||||||
const saveButton = document.querySelector("#save_button");
|
const options: SpreadsheetConstructorProperties = {
|
||||||
const loadButton = document.querySelector("#load_button");
|
onCellClick: (event, cell) => {
|
||||||
|
console.log("Cell click", event, cell);
|
||||||
|
},
|
||||||
|
onSelectionChange: (selection) => {
|
||||||
|
console.log("Changed selection: ", selection);
|
||||||
|
},
|
||||||
|
onCellChange(cell) {
|
||||||
|
console.log("Cell changed: ", cell);
|
||||||
|
},
|
||||||
|
onCopy: (range, data, dataAsString) => {
|
||||||
|
console.log("Copy event: ", range, data, dataAsString)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
if (!saveButton || !loadButton) throw new Error("LOST");
|
const sheet = new Spreadsheet("#spreadsheet", options);
|
||||||
|
|
||||||
const sheet = new Spreadsheet("#spreadsheet");
|
|
||||||
|
|
||||||
function saveDataToLS() {
|
function saveDataToLS() {
|
||||||
const serializableData = sheet.serializeData();
|
const serializableData = sheet.serializeData();
|
||||||
|
|
@ -19,5 +29,10 @@ function loadDataFromLS() {
|
||||||
sheet.loadData(json);
|
sheet.loadData(json);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const saveButton = document.querySelector("#save_button");
|
||||||
|
const loadButton = document.querySelector("#load_button");
|
||||||
|
|
||||||
|
if (!saveButton || !loadButton) throw new Error("LOST");
|
||||||
|
|
||||||
saveButton.addEventListener("click", saveDataToLS);
|
saveButton.addEventListener("click", saveDataToLS);
|
||||||
loadButton.addEventListener("click", loadDataFromLS);
|
loadButton.addEventListener("click", loadDataFromLS);
|
||||||
109
src/main.ts
109
src/main.ts
|
|
@ -10,7 +10,14 @@ import {
|
||||||
Position,
|
Position,
|
||||||
SerializableCell,
|
SerializableCell,
|
||||||
} from "./modules/cell";
|
} from "./modules/cell";
|
||||||
import { Config, ViewProperties } from "./modules/config";
|
import {
|
||||||
|
CellChangeEvent,
|
||||||
|
CellClickEvent,
|
||||||
|
Config,
|
||||||
|
CopyEvent,
|
||||||
|
SelectionChangeEvent,
|
||||||
|
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";
|
||||||
import { Viewport } from "./modules/viewport";
|
import { Viewport } from "./modules/viewport";
|
||||||
|
|
@ -21,6 +28,9 @@ import { Row } from "./modules/row";
|
||||||
import { Column } from "./modules/column";
|
import { Column } from "./modules/column";
|
||||||
import { ColumnsBar } from "./components/columnsBar";
|
import { ColumnsBar } from "./components/columnsBar";
|
||||||
import { RowsBar } from "./components/rowsBar";
|
import { RowsBar } from "./components/rowsBar";
|
||||||
|
import { EventTypes, Events } from "./modules/events";
|
||||||
|
import { Clipboard } from "./modules/clipboard";
|
||||||
|
import { FormulaParser } from "./modules/formulaParser";
|
||||||
|
|
||||||
/*
|
/*
|
||||||
! Component structure
|
! Component structure
|
||||||
|
|
@ -34,9 +44,12 @@ import { RowsBar } from "./components/rowsBar";
|
||||||
</Table>
|
</Table>
|
||||||
*/
|
*/
|
||||||
|
|
||||||
interface SpreadsheetConstructorProperties {
|
export interface SpreadsheetConstructorProperties {
|
||||||
config?: Omit<Config, "view">; // Not optional.
|
|
||||||
view?: ViewProperties;
|
view?: ViewProperties;
|
||||||
|
onCellClick?: CellClickEvent | null;
|
||||||
|
onSelectionChange?: SelectionChangeEvent | null;
|
||||||
|
onCellChange?: CellChangeEvent | null;
|
||||||
|
onCopy?: CopyEvent | null
|
||||||
}
|
}
|
||||||
|
|
||||||
export const CSS_PREFIX = "modern_sc_";
|
export const CSS_PREFIX = "modern_sc_";
|
||||||
|
|
@ -45,8 +58,8 @@ export default class Spreadsheet {
|
||||||
private table: Table;
|
private table: Table;
|
||||||
private scroller: Scroller;
|
private scroller: Scroller;
|
||||||
private toolbar: Toolbar;
|
private toolbar: Toolbar;
|
||||||
private rowsBar: RowsBar
|
private rowsBar: RowsBar;
|
||||||
private columnsBar: ColumnsBar
|
private columnsBar: ColumnsBar;
|
||||||
private sheet: Sheet;
|
private sheet: Sheet;
|
||||||
private editor: Editor;
|
private editor: Editor;
|
||||||
public styles: Styles;
|
public styles: Styles;
|
||||||
|
|
@ -55,6 +68,9 @@ export default class Spreadsheet {
|
||||||
public viewport: Viewport;
|
public viewport: Viewport;
|
||||||
public selection: Selection;
|
public selection: Selection;
|
||||||
public cache: Cache;
|
public cache: Cache;
|
||||||
|
public events: Events;
|
||||||
|
public clipboard: Clipboard;
|
||||||
|
public formulaParser: FormulaParser
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
target: string | HTMLElement,
|
target: string | HTMLElement,
|
||||||
|
|
@ -71,8 +87,13 @@ export default class Spreadsheet {
|
||||||
|
|
||||||
this.config = new Config(config);
|
this.config = new Config(config);
|
||||||
|
|
||||||
this.rowsBar = new RowsBar(this)
|
this.config.onCellClick = props?.onCellClick ?? null;
|
||||||
this.columnsBar = new ColumnsBar(this)
|
this.config.onSelectonChange = props?.onSelectionChange ?? null;
|
||||||
|
this.config.onCellChange = props?.onCellChange ?? null;
|
||||||
|
this.config.onCopy = props?.onCopy ?? null
|
||||||
|
|
||||||
|
this.rowsBar = new RowsBar(this);
|
||||||
|
this.columnsBar = new ColumnsBar(this);
|
||||||
this.sheet = new Sheet(this);
|
this.sheet = new Sheet(this);
|
||||||
this.table = new Table(this);
|
this.table = new Table(this);
|
||||||
this.scroller = new Scroller(this);
|
this.scroller = new Scroller(this);
|
||||||
|
|
@ -84,32 +105,34 @@ export default class Spreadsheet {
|
||||||
this.scroller.getViewportBoundlingRect(),
|
this.scroller.getViewportBoundlingRect(),
|
||||||
);
|
);
|
||||||
this.selection = new Selection();
|
this.selection = new Selection();
|
||||||
|
this.events = new Events(this);
|
||||||
|
this.clipboard = new Clipboard(this);
|
||||||
|
this.formulaParser = new FormulaParser(this)
|
||||||
|
|
||||||
this.data = data;
|
this.data = data;
|
||||||
this.styles = new Styles();
|
this.styles = new Styles();
|
||||||
this.buildComponent();
|
this.buildComponent();
|
||||||
this.setElementsPositions()
|
this.setElementsPositions();
|
||||||
this.appendTableToTarget(target);
|
this.appendTableToTarget(target);
|
||||||
this.renderSheet();
|
this.renderSheet();
|
||||||
this.renderColumnsBar()
|
this.renderColumnsBar();
|
||||||
this.renderRowsBar()
|
this.renderRowsBar();
|
||||||
}
|
}
|
||||||
|
|
||||||
private setRowsBarPosition() {
|
private setRowsBarPosition() {
|
||||||
const top = this.columnsBar.height + this.toolbar.height
|
const top = this.columnsBar.height + this.toolbar.height;
|
||||||
const left = 0
|
const left = 0;
|
||||||
this.rowsBar.setElementPosition(top, left)
|
this.rowsBar.setElementPosition(top, left);
|
||||||
}
|
}
|
||||||
private setColumnsBarPosition() {
|
private setColumnsBarPosition() {
|
||||||
const top = this.toolbar.height
|
const top = this.toolbar.height;
|
||||||
const left = this.rowsBar.width
|
const left = this.rowsBar.width;
|
||||||
console.log(top,left)
|
this.columnsBar.setElementPosition(top, left);
|
||||||
this.columnsBar.setElementPosition(top, left)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private setElementsPositions() {
|
private setElementsPositions() {
|
||||||
this.setRowsBarPosition()
|
this.setRowsBarPosition();
|
||||||
this.setColumnsBarPosition()
|
this.setColumnsBarPosition();
|
||||||
}
|
}
|
||||||
|
|
||||||
private getInitialCache(): Cache {
|
private getInitialCache(): Cache {
|
||||||
|
|
@ -142,23 +165,21 @@ export default class Spreadsheet {
|
||||||
rows: cachedRows,
|
rows: cachedRows,
|
||||||
});
|
});
|
||||||
|
|
||||||
console.log("CACHE: ", cache);
|
|
||||||
console.log("CONFIG: ", this.config);
|
|
||||||
return cache;
|
return cache;
|
||||||
}
|
}
|
||||||
|
|
||||||
private buildComponent(): void {
|
private buildComponent(): void {
|
||||||
const content = document.createElement("div"); //* Abstract
|
const content = document.createElement("div"); //* Abstract
|
||||||
content.style.top = this.columnsBarHeight + 'px'
|
content.style.top = this.columnsBarHeight + "px";
|
||||||
content.style.left = this.rowsBarWidth + 'px'
|
content.style.left = this.rowsBarWidth + "px";
|
||||||
|
|
||||||
content.appendChild(this.sheet.element);
|
content.appendChild(this.sheet.element);
|
||||||
|
|
||||||
content.classList.add(CSS_PREFIX + "content");
|
content.classList.add(CSS_PREFIX + "content");
|
||||||
|
|
||||||
this.table.element.appendChild(this.toolbar.element);
|
this.table.element.appendChild(this.toolbar.element);
|
||||||
this.table.element.appendChild(this.rowsBar.element)
|
this.table.element.appendChild(this.rowsBar.element);
|
||||||
this.table.element.appendChild(this.columnsBar.element)
|
this.table.element.appendChild(this.columnsBar.element);
|
||||||
this.table.element.appendChild(content);
|
this.table.element.appendChild(content);
|
||||||
this.table.element.appendChild(this.scroller.element);
|
this.table.element.appendChild(this.scroller.element);
|
||||||
this.table.element.append(this.editor.element);
|
this.table.element.append(this.editor.element);
|
||||||
|
|
@ -199,15 +220,15 @@ export default class Spreadsheet {
|
||||||
}
|
}
|
||||||
|
|
||||||
get columnsBarHeight() {
|
get columnsBarHeight() {
|
||||||
return this.columnsBar.height
|
return this.columnsBar.height;
|
||||||
}
|
}
|
||||||
|
|
||||||
get rowsBarWidth() {
|
get rowsBarWidth() {
|
||||||
return this.rowsBar.width
|
return this.rowsBar.width;
|
||||||
}
|
}
|
||||||
|
|
||||||
get toolbarHeight() {
|
get toolbarHeight() {
|
||||||
return this.toolbar.height
|
return this.toolbar.height;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Focusing on interactive part of spreadsheet */
|
/** Focusing on interactive part of spreadsheet */
|
||||||
|
|
@ -227,10 +248,19 @@ export default class Spreadsheet {
|
||||||
changeCellValues(
|
changeCellValues(
|
||||||
position: Position,
|
position: Position,
|
||||||
values: Partial<Omit<CellConstructorProps, "position">>,
|
values: Partial<Omit<CellConstructorProps, "position">>,
|
||||||
|
enableCallback: boolean = true
|
||||||
) {
|
) {
|
||||||
const { column, row } = position;
|
const { column, row } = position;
|
||||||
|
|
||||||
|
|
||||||
this.data[row][column].changeValues(values);
|
this.data[row][column].changeValues(values);
|
||||||
|
|
||||||
|
this.events.dispatch({
|
||||||
|
type: EventTypes.CELL_CHANGE,
|
||||||
|
cell: this.data[row][column],
|
||||||
|
enableCallback: enableCallback
|
||||||
|
})
|
||||||
|
|
||||||
this.renderCell(row, column);
|
this.renderCell(row, column);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -285,12 +315,16 @@ export default class Spreadsheet {
|
||||||
this.sheet.renderSheet();
|
this.sheet.renderSheet();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
renderSelection() {
|
||||||
|
this.sheet.renderSelection();
|
||||||
|
}
|
||||||
|
|
||||||
renderColumnsBar() {
|
renderColumnsBar() {
|
||||||
this.columnsBar.renderBar()
|
this.columnsBar.renderBar();
|
||||||
}
|
}
|
||||||
|
|
||||||
renderRowsBar() {
|
renderRowsBar() {
|
||||||
this.rowsBar.renderBar()
|
this.rowsBar.renderBar();
|
||||||
}
|
}
|
||||||
|
|
||||||
renderCell(row: number, col: number) {
|
renderCell(row: number, col: number) {
|
||||||
|
|
@ -299,11 +333,11 @@ export default class Spreadsheet {
|
||||||
|
|
||||||
public loadData(data: Cell[][] | SerializableCell[][]): Spreadsheet {
|
public loadData(data: Cell[][] | SerializableCell[][]): Spreadsheet {
|
||||||
const rowsLength = data.length;
|
const rowsLength = data.length;
|
||||||
const colsLength = data[0] ? this.data[0].length : 0;
|
const colsLength = data[0] ? data[0].length : 0;
|
||||||
this.data = [];
|
this.data = [];
|
||||||
|
|
||||||
const formattedData: Cell[][] = [];
|
const formattedData: Cell[][] = [];
|
||||||
|
// Transform serialized objects to Cells
|
||||||
for (let row = 0; row < rowsLength; row++) {
|
for (let row = 0; row < rowsLength; row++) {
|
||||||
const innerRow: Cell[] = [];
|
const innerRow: Cell[] = [];
|
||||||
for (let col = 0; col < colsLength; col++) {
|
for (let col = 0; col < colsLength; col++) {
|
||||||
|
|
@ -321,11 +355,17 @@ export default class Spreadsheet {
|
||||||
formattedData.push(innerRow);
|
formattedData.push(innerRow);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.data = formattedData;
|
const config = this.makeConfigFromData(formattedData, this.config.view);
|
||||||
|
config.onCellChange = this.config.onCellChange
|
||||||
|
config.onCellClick = this.config.onCellClick
|
||||||
|
config.onCopy = this.config.onCopy
|
||||||
|
config.onSelectonChange = this.config.onSelectonChange
|
||||||
|
|
||||||
|
|
||||||
|
this.data = formattedData;
|
||||||
this.selection.selectedCell = null;
|
this.selection.selectedCell = null;
|
||||||
this.selection.selectedRange = null;
|
this.selection.selectedRange = null;
|
||||||
this.config = this.makeConfigFromData(formattedData, this.config.view);
|
this.config = config
|
||||||
this.cache = this.getInitialCache();
|
this.cache = this.getInitialCache();
|
||||||
this.scroller.updateScrollerSize();
|
this.scroller.updateScrollerSize();
|
||||||
this.viewport = new Viewport(
|
this.viewport = new Viewport(
|
||||||
|
|
@ -366,6 +406,7 @@ export default class Spreadsheet {
|
||||||
view,
|
view,
|
||||||
rows,
|
rows,
|
||||||
columns,
|
columns,
|
||||||
|
onCellClick: null,
|
||||||
});
|
});
|
||||||
|
|
||||||
return config;
|
return config;
|
||||||
|
|
|
||||||
|
|
@ -44,21 +44,21 @@ export class Cache {
|
||||||
public getRowByYCoord(y: number): number {
|
public getRowByYCoord(y: number): number {
|
||||||
let rowIdx = 0;
|
let rowIdx = 0;
|
||||||
for (let i = 0; i < this.rows.length; i++) {
|
for (let i = 0; i < this.rows.length; i++) {
|
||||||
if (y <= this.rows[i].yPos) {
|
rowIdx = i
|
||||||
//* Intersection detect
|
if (y <= this.rows[i].yPos) { //* Intersection detect
|
||||||
rowIdx = i;
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return rowIdx;
|
return rowIdx;
|
||||||
}
|
}
|
||||||
|
|
||||||
public getColumnByXCoord(x: number): number {
|
public getColumnByXCoord(x: number): number {
|
||||||
let colIdx = 0;
|
let colIdx = 0;
|
||||||
for (let i = 0; i < this.columns.length; i++) {
|
for (let i = 0; i < this.columns.length; i++) {
|
||||||
if (x <= this.columns[i].xPos) {
|
colIdx = i
|
||||||
//* Intersection detect
|
if (x <= this.columns[i].xPos) { //* Intersection detect
|
||||||
colIdx = i;
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
import Spreadsheet from "../main";
|
import Spreadsheet from "../main";
|
||||||
|
import { FormulaParser } from "./formulaParser";
|
||||||
import { RenderBox } from "./renderBox";
|
import { RenderBox } from "./renderBox";
|
||||||
|
|
||||||
export type CellConstructorProps = {
|
export type CellConstructorProps = {
|
||||||
|
|
@ -69,6 +70,9 @@ export class Cell {
|
||||||
position: Position;
|
position: Position;
|
||||||
style: CellStyles | null = null;
|
style: CellStyles | null = null;
|
||||||
|
|
||||||
|
cellsDependsOnThisCell: Position[] = []
|
||||||
|
dependedFromCells: Position[] = []
|
||||||
|
|
||||||
constructor(props: CellConstructorProps) {
|
constructor(props: CellConstructorProps) {
|
||||||
this.value = props.value;
|
this.value = props.value;
|
||||||
this.displayValue = props.displayValue;
|
this.displayValue = props.displayValue;
|
||||||
|
|
@ -96,50 +100,53 @@ export class Cell {
|
||||||
Object.assign(this, values);
|
Object.assign(this, values);
|
||||||
}
|
}
|
||||||
|
|
||||||
private isCellInRange(root: Spreadsheet): boolean {
|
evalFormula(parser: FormulaParser) {
|
||||||
const { column, row } = this.position;
|
if (this.value.substring(0, 1) !== '=') return;
|
||||||
const { selectedRange } = root.selection;
|
|
||||||
|
|
||||||
if (!selectedRange) return false;
|
this.resultValue = parser.parser.parse(this.value.slice(1), {
|
||||||
|
col: this.position.column,
|
||||||
const isCellInRow =
|
row: this.position.row
|
||||||
row >= Math.min(selectedRange.from.row, selectedRange.to.row) &&
|
})
|
||||||
row <= Math.max(selectedRange.to.row, selectedRange.from.row);
|
|
||||||
const isCellInCol =
|
|
||||||
column >= Math.min(selectedRange.from.column, selectedRange.to.column) &&
|
|
||||||
column <= Math.max(selectedRange.to.column, selectedRange.from.column);
|
|
||||||
|
|
||||||
return isCellInCol && isCellInRow;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// private isCellInRange(root: Spreadsheet): boolean {
|
||||||
|
// const { column, row } = this.position;
|
||||||
|
// const { selectedRange } = root.selection;
|
||||||
|
|
||||||
|
// if (!selectedRange) return false;
|
||||||
|
|
||||||
|
// const isCellInRow =
|
||||||
|
// row >= Math.min(selectedRange.from.row, selectedRange.to.row) &&
|
||||||
|
// row <= Math.max(selectedRange.to.row, selectedRange.from.row);
|
||||||
|
// const isCellInCol =
|
||||||
|
// column >= Math.min(selectedRange.from.column, selectedRange.to.column) &&
|
||||||
|
// column <= Math.max(selectedRange.to.column, selectedRange.from.column);
|
||||||
|
|
||||||
|
// return isCellInCol && isCellInRow;
|
||||||
|
// }
|
||||||
|
|
||||||
render(root: Spreadsheet) {
|
render(root: Spreadsheet) {
|
||||||
const renderBox = new RenderBox(root.config, this.position);
|
const renderBox = new RenderBox(root.config, this.position);
|
||||||
let {x, y} = renderBox
|
let { x, y } = renderBox;
|
||||||
const {height, width} = renderBox
|
const { height, width } = renderBox;
|
||||||
const { ctx } = root;
|
const { ctx } = root;
|
||||||
|
|
||||||
const isCellSelected =
|
// const isCellSelected =
|
||||||
root.selection.selectedCell?.row === this.position.row &&
|
// root.selection.selectedCell?.row === this.position.row &&
|
||||||
root.selection.selectedCell.column === this.position.column;
|
// root.selection.selectedCell.column === this.position.column;
|
||||||
const isCellInRange = this.isCellInRange(root);
|
// const isCellInRange = this.isCellInRange(root);
|
||||||
y -= root.viewport.top;
|
y -= root.viewport.top;
|
||||||
x -= root.viewport.left;
|
x -= root.viewport.left;
|
||||||
|
|
||||||
const styles = this.style ?? root.styles.cells;
|
const styles = this.style ?? root.styles.cells;
|
||||||
|
|
||||||
ctx.clearRect(x, y, width, height);
|
ctx.clearRect(x, y, width, height);
|
||||||
ctx.fillStyle =
|
ctx.fillStyle = styles.background;
|
||||||
isCellSelected || isCellInRange
|
|
||||||
? styles.selectedBackground
|
|
||||||
: styles.background;
|
|
||||||
ctx.strokeStyle = "black";
|
ctx.strokeStyle = "black";
|
||||||
ctx.fillRect(x, y, width - 1, height - 1);
|
ctx.fillRect(x, y, width - 1, height - 1);
|
||||||
ctx.strokeRect(x, y, width, height);
|
ctx.strokeRect(x, y, width, height);
|
||||||
|
|
||||||
ctx.fillStyle =
|
ctx.fillStyle = styles.fontColor;
|
||||||
isCellSelected || isCellInRange
|
|
||||||
? styles.selectedFontColor
|
|
||||||
: styles.fontColor;
|
|
||||||
ctx.textAlign = "left";
|
ctx.textAlign = "left";
|
||||||
ctx.font = `${styles.fontSize}px Arial`;
|
ctx.font = `${styles.fontSize}px Arial`;
|
||||||
ctx.textBaseline = "middle";
|
ctx.textBaseline = "middle";
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,106 @@
|
||||||
|
import Spreadsheet, { RangeSelectionType } from "../main";
|
||||||
|
import { Cell, CellConstructorProps, CellStyles, Position } from "./cell";
|
||||||
|
import { EventTypes } from "./events";
|
||||||
|
|
||||||
|
export class Clipboard {
|
||||||
|
saved: Cell[][] | null = null;
|
||||||
|
root: Spreadsheet;
|
||||||
|
constructor(root: Spreadsheet) {
|
||||||
|
this.root = root;
|
||||||
|
}
|
||||||
|
|
||||||
|
copy(data: Cell[][], range: RangeSelectionType) {
|
||||||
|
const mapedData = data
|
||||||
|
.map((row) => {
|
||||||
|
return row
|
||||||
|
.map((item) => {
|
||||||
|
return item.displayValue;
|
||||||
|
})
|
||||||
|
.join("\t");
|
||||||
|
})
|
||||||
|
.join("\n");
|
||||||
|
|
||||||
|
this.saved = data;
|
||||||
|
navigator.clipboard.writeText(mapedData);
|
||||||
|
|
||||||
|
this.root.events.dispatch({
|
||||||
|
type: EventTypes.COPY_CELLS,
|
||||||
|
data,
|
||||||
|
dataAsString: mapedData,
|
||||||
|
range,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
paste(root: Spreadsheet, { column, row }: Position, event: ClipboardEvent) {
|
||||||
|
if (!this.saved) {
|
||||||
|
if (!event.clipboardData) return;
|
||||||
|
const data = event.clipboardData.getData("text");
|
||||||
|
try {
|
||||||
|
const arr = data.split("\n").map((item) => item.split("\t"));
|
||||||
|
const arrayOfCells = arr.map((innerRow) => {
|
||||||
|
return innerRow.map((item) => {
|
||||||
|
const cellProps: CellConstructorProps = {
|
||||||
|
displayValue: item,
|
||||||
|
position: {
|
||||||
|
column,
|
||||||
|
row,
|
||||||
|
},
|
||||||
|
resultValue: item,
|
||||||
|
style: new CellStyles(),
|
||||||
|
value: item,
|
||||||
|
};
|
||||||
|
return new Cell(cellProps);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
const rowsLength = arrayOfCells.length;
|
||||||
|
const colsLength = arrayOfCells[0] ? arrayOfCells[0].length : 0;
|
||||||
|
|
||||||
|
for (let i = 0; i < rowsLength; i++) {
|
||||||
|
for (let j = 0; j < colsLength; j++) {
|
||||||
|
const savedCell = arrayOfCells[i][j];
|
||||||
|
|
||||||
|
const position = {
|
||||||
|
column: column + j,
|
||||||
|
row: row + i,
|
||||||
|
};
|
||||||
|
|
||||||
|
const values = {
|
||||||
|
displayValue: savedCell.displayValue,
|
||||||
|
value: savedCell.value,
|
||||||
|
style: savedCell.style,
|
||||||
|
};
|
||||||
|
|
||||||
|
root.changeCellValues(position, values, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.error("Cannot read clipboard. ", err);
|
||||||
|
}
|
||||||
|
root.renderSheet();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const rowsLength = this.saved.length;
|
||||||
|
const colsLength = this.saved[0] ? this.saved[0].length : 0;
|
||||||
|
|
||||||
|
for (let i = 0; i < rowsLength; i++) {
|
||||||
|
for (let j = 0; j < colsLength; j++) {
|
||||||
|
const savedCell = this.saved[i][j];
|
||||||
|
|
||||||
|
const position = {
|
||||||
|
column: column + j,
|
||||||
|
row: row + i,
|
||||||
|
};
|
||||||
|
|
||||||
|
const values = {
|
||||||
|
displayValue: savedCell.displayValue,
|
||||||
|
value: savedCell.value,
|
||||||
|
style: savedCell.style,
|
||||||
|
};
|
||||||
|
|
||||||
|
root.changeCellValues(position, values, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,10 +1,20 @@
|
||||||
|
import { Cell } from "./cell";
|
||||||
import { Column } from "./column";
|
import { Column } from "./column";
|
||||||
import { Row } from "./row";
|
import { Row } from "./row";
|
||||||
|
import { RangeSelectionType, Selection } from "./selection";
|
||||||
|
|
||||||
export interface ViewProperties {
|
export interface ViewProperties {
|
||||||
width: number;
|
width: number;
|
||||||
height: number;
|
height: number;
|
||||||
}
|
}
|
||||||
|
export type CellClickEvent = (event: MouseEvent, cell: Cell) => void;
|
||||||
|
export type SelectionChangeEvent = (selection: Selection) => void;
|
||||||
|
export type CellChangeEvent = (cell: Cell) => void;
|
||||||
|
export type CopyEvent = (
|
||||||
|
range: RangeSelectionType,
|
||||||
|
data: Cell[][],
|
||||||
|
dataAsString: string,
|
||||||
|
) => void;
|
||||||
|
|
||||||
export type ConfigProperties = {
|
export type ConfigProperties = {
|
||||||
/** Please, end it with '_' symbol.
|
/** Please, end it with '_' symbol.
|
||||||
|
|
@ -16,6 +26,10 @@ export type ConfigProperties = {
|
||||||
rows: Row[];
|
rows: Row[];
|
||||||
columns: Column[];
|
columns: Column[];
|
||||||
view: ViewProperties;
|
view: ViewProperties;
|
||||||
|
onCellClick?: CellClickEvent | null;
|
||||||
|
onSelectionChange?: SelectionChangeEvent | null;
|
||||||
|
onCellChange?: CellChangeEvent | null;
|
||||||
|
onCopy?: CopyEvent | null;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type SheetConfigConstructorProps = {
|
export type SheetConfigConstructorProps = {
|
||||||
|
|
@ -30,9 +44,20 @@ export class Config {
|
||||||
width: 800,
|
width: 800,
|
||||||
height: 600,
|
height: 600,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
onCellClick: CellClickEvent | null = null;
|
||||||
|
onSelectonChange: SelectionChangeEvent | null = null;
|
||||||
|
onCellChange: CellChangeEvent | null = null;
|
||||||
|
onCopy: CopyEvent | null;
|
||||||
|
|
||||||
constructor(props: ConfigProperties) {
|
constructor(props: ConfigProperties) {
|
||||||
this.columns = props.columns;
|
this.columns = props.columns;
|
||||||
this.rows = props.rows;
|
this.rows = props.rows;
|
||||||
this.view = props.view;
|
this.view = props.view;
|
||||||
|
|
||||||
|
this.onCellClick = props.onCellClick ?? null;
|
||||||
|
this.onSelectonChange = props.onSelectionChange ?? null;
|
||||||
|
this.onCellChange = props.onCellChange ?? null;
|
||||||
|
this.onCopy = props.onCopy ?? null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,142 @@
|
||||||
|
import { Scroller } from "../components/scroller";
|
||||||
|
import Spreadsheet, { Cell, RangeSelectionType, Selection } from "../main";
|
||||||
|
|
||||||
|
export enum EventTypes {
|
||||||
|
CELL_CLICK = "CELL_CLICK",
|
||||||
|
SELECTION_CHANGE = "CHANGE_SELECTION",
|
||||||
|
CELL_CHANGE = "CELL_CHANGE",
|
||||||
|
COPY_CELLS = "COPY_CELLS",
|
||||||
|
}
|
||||||
|
|
||||||
|
export type CellClickEvent = {
|
||||||
|
type: EventTypes.CELL_CLICK;
|
||||||
|
event: MouseEvent;
|
||||||
|
scroller: Scroller;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type ChangeSelectionEvent = {
|
||||||
|
type: EventTypes.SELECTION_CHANGE;
|
||||||
|
selection: Selection;
|
||||||
|
enableCallback?: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type ChangeCellEvent = {
|
||||||
|
type: EventTypes.CELL_CHANGE;
|
||||||
|
cell: Cell;
|
||||||
|
enableCallback?: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type CopyAction = {
|
||||||
|
type: EventTypes.COPY_CELLS;
|
||||||
|
range: RangeSelectionType;
|
||||||
|
data: Cell[][];
|
||||||
|
dataAsString: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type ActionTypes =
|
||||||
|
| CellClickEvent
|
||||||
|
| ChangeSelectionEvent
|
||||||
|
| ChangeCellEvent
|
||||||
|
| CopyAction;
|
||||||
|
|
||||||
|
|
||||||
|
export class Events {
|
||||||
|
root: Spreadsheet;
|
||||||
|
|
||||||
|
constructor(root: Spreadsheet) {
|
||||||
|
this.root = root;
|
||||||
|
}
|
||||||
|
|
||||||
|
async dispatch(action: ActionTypes) {
|
||||||
|
switch (action.type) {
|
||||||
|
case EventTypes.CELL_CLICK: {
|
||||||
|
const { event, scroller } = action;
|
||||||
|
//
|
||||||
|
//* Here may be side effects
|
||||||
|
//
|
||||||
|
this.cellClick(event, scroller);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case EventTypes.SELECTION_CHANGE: {
|
||||||
|
const { selection, enableCallback } = action;
|
||||||
|
//
|
||||||
|
//* Here may be side effects
|
||||||
|
//
|
||||||
|
this.changeSelection(selection, enableCallback);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case EventTypes.CELL_CHANGE: {
|
||||||
|
const { cell, enableCallback } = action;
|
||||||
|
if (cell.value.substring(0, 1).startsWith('=')) {
|
||||||
|
try {
|
||||||
|
await cell.evalFormula(this.root.formulaParser)
|
||||||
|
cell.displayValue = cell.resultValue
|
||||||
|
this.root.renderCell(cell.position.row, cell.position.column)
|
||||||
|
this.changeCellValues(cell, enableCallback);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
console.error(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.root.renderCell(cell.position.row, cell.position.column)
|
||||||
|
this.changeCellValues(cell, enableCallback);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case EventTypes.COPY_CELLS: {
|
||||||
|
const { data, dataAsString, range } = action;
|
||||||
|
this.copy(range, data, dataAsString);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
default: {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private cellClick = (event: MouseEvent, scroller: Scroller) => {
|
||||||
|
if (event.button !== 0) return; // Left mouse button
|
||||||
|
const { offsetX, offsetY } = event;
|
||||||
|
const clickedCell = this.root.getCellByCoords(offsetX, offsetY);
|
||||||
|
const cell = this.root.getCell(clickedCell);
|
||||||
|
|
||||||
|
const selection = new Selection();
|
||||||
|
selection.selectedCell = clickedCell;
|
||||||
|
selection.selectedRange = {
|
||||||
|
from: clickedCell,
|
||||||
|
to: clickedCell,
|
||||||
|
};
|
||||||
|
|
||||||
|
scroller.setSelectingMode(true);
|
||||||
|
|
||||||
|
this.changeSelection(selection, true);
|
||||||
|
|
||||||
|
this.root.config.onCellClick?.(event, cell);
|
||||||
|
};
|
||||||
|
|
||||||
|
private changeSelection = (selection: Selection, enableCallback = false) => {
|
||||||
|
this.root.selection = selection;
|
||||||
|
|
||||||
|
if (enableCallback) this.root.config.onSelectonChange?.(selection);
|
||||||
|
this.root.renderSheet();
|
||||||
|
this.root.renderColumnsBar();
|
||||||
|
this.root.renderRowsBar();
|
||||||
|
};
|
||||||
|
|
||||||
|
private changeCellValues(cell: Cell, enableCallback: boolean = true) {
|
||||||
|
if (enableCallback) this.root.config.onCellChange?.(cell);
|
||||||
|
}
|
||||||
|
|
||||||
|
private copy = (
|
||||||
|
range: RangeSelectionType,
|
||||||
|
data: Cell[][],
|
||||||
|
dataAsString: string,
|
||||||
|
) => {
|
||||||
|
this.root.config.onCopy?.(range, data, dataAsString);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,23 @@
|
||||||
|
import Parser, { DepParser } from 'fast-formula-parser'
|
||||||
|
import Spreadsheet from '../main'
|
||||||
|
|
||||||
|
export class FormulaParser {
|
||||||
|
parser: Parser
|
||||||
|
depParser: DepParser
|
||||||
|
root: Spreadsheet
|
||||||
|
constructor(root: Spreadsheet) {
|
||||||
|
this.root = root
|
||||||
|
|
||||||
|
this.parser = new Parser({
|
||||||
|
onCell: ({col, row}) => {
|
||||||
|
const cell = this.root.data[row - 1][col - 1]
|
||||||
|
const cellValue = cell.resultValue.length > 0 ? cell.resultValue : cell.value
|
||||||
|
if( cellValue && isNaN(Number(cellValue)) === false) return Number(cellValue)
|
||||||
|
return this.root.data[row - 1][col - 1].resultValue ?? ''
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
this.depParser = new DepParser({})
|
||||||
|
this.depParser
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,15 +1,15 @@
|
||||||
$css_prefix: "modern_sc_";
|
$css_prefix: "modern_sc_";
|
||||||
|
|
||||||
.#{$css_prefix}content {
|
|
||||||
position: absolute;
|
|
||||||
}
|
|
||||||
|
|
||||||
.#{$css_prefix}spreadsheet_container {
|
.#{$css_prefix}spreadsheet_container {
|
||||||
position: relative;
|
position: relative;
|
||||||
isolation: isolate;
|
isolation: isolate;
|
||||||
border: 2px solid black;
|
border: 2px solid black;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.#{$css_prefix}content {
|
||||||
|
position: absolute;
|
||||||
|
}
|
||||||
|
|
||||||
.#{$css_prefix}sheet {
|
.#{$css_prefix}sheet {
|
||||||
display: block;
|
display: block;
|
||||||
contain: strict;
|
contain: strict;
|
||||||
|
|
@ -20,6 +20,7 @@ $css_prefix: "modern_sc_";
|
||||||
overflow: scroll;
|
overflow: scroll;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
transform: translateZ(0);
|
transform: translateZ(0);
|
||||||
|
|
||||||
&:focus {
|
&:focus {
|
||||||
outline: none;
|
outline: none;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,68 @@
|
||||||
|
declare module 'fast-formula-parser' {
|
||||||
|
export type PositionWithSheet = {
|
||||||
|
sheet?: string
|
||||||
|
row: number
|
||||||
|
col: number
|
||||||
|
}
|
||||||
|
|
||||||
|
export type FunctionArgument = {
|
||||||
|
isArray: boolean
|
||||||
|
isCellRef: boolean
|
||||||
|
isRangeRef: boolean
|
||||||
|
value: string | number
|
||||||
|
}
|
||||||
|
|
||||||
|
export type Position = {
|
||||||
|
col: number
|
||||||
|
row: number
|
||||||
|
}
|
||||||
|
|
||||||
|
export type RangeReference = {
|
||||||
|
sheet?: string
|
||||||
|
from: Position,
|
||||||
|
to: Position
|
||||||
|
}
|
||||||
|
|
||||||
|
export type Config = {
|
||||||
|
functions?: Record<string, (...args: FunctionArgument[]) => string>
|
||||||
|
functionsNeedContext?: (context: Parser, ...args: FunctionArgument[]) => string
|
||||||
|
onCell?: (position: PositionWithSheet) => number | string
|
||||||
|
onRange?: (ref) => Array<string|number>[]
|
||||||
|
onVariable?: (name: string, sheetName: string) => RangeReference
|
||||||
|
}
|
||||||
|
|
||||||
|
export const Types = {
|
||||||
|
NUMBER: 0,
|
||||||
|
ARRAY: 1,
|
||||||
|
BOOLEAN: 2,
|
||||||
|
STRING: 3,
|
||||||
|
RANGE_REF: 4, // can be 'A:C' or '1:4', not only 'A1:C3'
|
||||||
|
CELL_REF: 5,
|
||||||
|
COLLECTIONS: 6, // Unions of references
|
||||||
|
NUMBER_NO_BOOLEAN: 10,
|
||||||
|
};
|
||||||
|
|
||||||
|
export const Factorials: number[]
|
||||||
|
|
||||||
|
export default class Parser {
|
||||||
|
constructor(config: Config)
|
||||||
|
parse: (expression: string, position: PositionWithSheet) => string
|
||||||
|
parseAsync: (expression: string, position: PositionWithSheet) => Promise<string>
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
type FormulaHelpersType = {
|
||||||
|
accept: (param: FunctionArgument, type?: number, defValue?: number | string, flat?: boolean, allowSingleValue?: boolean) => number | string
|
||||||
|
type: (variable) => number
|
||||||
|
isRangeRef: (param) => boolean
|
||||||
|
isCellRef: (param) => boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
export class DepParser {
|
||||||
|
constructor(config?: {onVariable?: (name: string, sheetName: string) => RangeReference})
|
||||||
|
parse(expression: string, position: PositionWithSheet): PositionWithSheet[]
|
||||||
|
}
|
||||||
|
|
||||||
|
export const FormulaHelpers: FormulaHelpersType
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,20 @@
|
||||||
|
import { BaseSelectionType, RangeSelectionType } from "../main";
|
||||||
|
|
||||||
|
export function checkEqualRanges(
|
||||||
|
range1: RangeSelectionType,
|
||||||
|
range2: RangeSelectionType,
|
||||||
|
) {
|
||||||
|
const equalRows = range1.from.row === range2.to.row;
|
||||||
|
const equalColumns = range1.from.column === range2.to.column;
|
||||||
|
|
||||||
|
return equalRows && equalColumns;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function checkEqualCellSelections(
|
||||||
|
selection1: BaseSelectionType,
|
||||||
|
selection2: BaseSelectionType,
|
||||||
|
) {
|
||||||
|
return (
|
||||||
|
selection1.column === selection2.column && selection1.row === selection2.row
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
@ -5,7 +5,7 @@
|
||||||
"types": ["vite/client", "node"],
|
"types": ["vite/client", "node"],
|
||||||
"allowJs": false,
|
"allowJs": false,
|
||||||
"skipLibCheck": true,
|
"skipLibCheck": true,
|
||||||
"esModuleInterop": false,
|
"esModuleInterop": true,
|
||||||
"allowSyntheticDefaultImports": true,
|
"allowSyntheticDefaultImports": true,
|
||||||
"strict": true,
|
"strict": true,
|
||||||
"forceConsistentCasingInFileNames": true,
|
"forceConsistentCasingInFileNames": true,
|
||||||
|
|
|
||||||
|
|
@ -2,14 +2,16 @@ import { defineConfig } from "vite";
|
||||||
import path from "path";
|
import path from "path";
|
||||||
import typescript from "@rollup/plugin-typescript";
|
import typescript from "@rollup/plugin-typescript";
|
||||||
import { typescriptPaths } from "rollup-plugin-typescript-paths";
|
import { typescriptPaths } from "rollup-plugin-typescript-paths";
|
||||||
import { fileURLToPath } from "node:url";
|
|
||||||
|
|
||||||
export default defineConfig({
|
const BROWSER_MODE = process.env.BUILD_BROWSER === 'true';
|
||||||
|
console.log({ BROWSER_MODE });
|
||||||
|
|
||||||
|
const libConfig = defineConfig({
|
||||||
base: "/modern_spreadsheet/",
|
base: "/modern_spreadsheet/",
|
||||||
plugins: [],
|
plugins: [],
|
||||||
resolve: {},
|
resolve: {},
|
||||||
server: {
|
server: {
|
||||||
port: 3000,
|
port: 5179,
|
||||||
open: true,
|
open: true,
|
||||||
},
|
},
|
||||||
build: {
|
build: {
|
||||||
|
|
@ -37,3 +39,23 @@ export default defineConfig({
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const browserConfig = defineConfig({
|
||||||
|
base: "/modern_spreadsheet/",
|
||||||
|
resolve: {},
|
||||||
|
build: {
|
||||||
|
manifest: true,
|
||||||
|
minify: true,
|
||||||
|
reportCompressedSize: true,
|
||||||
|
sourcemap: true,
|
||||||
|
lib: {
|
||||||
|
entry: path.resolve(__dirname, 'index.html'),
|
||||||
|
fileName: 'demo',
|
||||||
|
formats: ['es']
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const config = BROWSER_MODE ? browserConfig : libConfig;
|
||||||
|
|
||||||
|
export default config;
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue