Compare commits

..

4 Commits

Author SHA1 Message Date
Eugene 75e929f0d5 Fixed view of col&row bars 2023-07-25 23:50:34 +03:00
Eugene c4c8ff7a8f Fixed rects 2023-07-25 17:34:36 +03:00
Eugene c68a88c907 Added rows bar 2023-07-25 20:04:11 +03:00
Eugene ad69c4e01c Added new component ColumnsBar
Added render of ColumnsBar methods
Added selection condition render styling
2023-07-25 18:42:00 +03:00
42 changed files with 564 additions and 2061 deletions

View File

@ -4,11 +4,7 @@ module.exports = {
es2021: true, es2021: true,
node: true, node: true,
}, },
extends: [ extends: ["eslint:recommended", "plugin:@typescript-eslint/recommended", "prettier"],
"eslint:recommended",
"plugin:@typescript-eslint/recommended",
"prettier",
],
overrides: [ overrides: [
{ {
env: { env: {

View File

@ -1,15 +1,9 @@
# 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";
@ -20,7 +14,7 @@ const sheet = new Spreadsheet(target);
//... //...
``` ```
### Save and load data ## Save and load data
```ts ```ts
function saveData() { function saveData() {
@ -36,39 +30,10 @@ function loadData() {
} }
``` ```
#### Supported events ## Roadmap
- onCellClick
- onSelectionChange
- onCellChange
- onCopy
### Using events examples - Custom event functions (ex.: onSelectionChange, onCellEdit...). Full list of supported events will available on this page
```ts - Rows number and columns heading render
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
@ -76,3 +41,4 @@ const sheet = new Spreadsheet("#spreadsheet", options);
- Selected cell depends cells highlight - Selected cell depends cells highlight
- Async formulas support - Async formulas support
- Mutlisheets (?) - Mutlisheets (?)
- Copy & Paste support

View File

@ -1,16 +0,0 @@
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;
}

6
dist/components/header.d.ts vendored Normal file
View File

@ -0,0 +1,6 @@
import Spreadsheet from "../main";
export declare class Header {
element: HTMLHeadElement;
root: Spreadsheet;
constructor(root: Spreadsheet);
}

View File

@ -1,17 +0,0 @@
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;
}

View File

@ -12,7 +12,6 @@ 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;

View File

@ -10,8 +10,5 @@ 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;
} }

View File

@ -2,6 +2,5 @@ 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);
} }

8
dist/main.cjs vendored

File diff suppressed because one or more lines are too long

2
dist/main.cjs.map vendored

File diff suppressed because one or more lines are too long

51
dist/main.d.ts vendored
View File

@ -1,26 +1,20 @@
import { Cell, CellConstructorProps, CellStyles, Position, SerializableCell } from "./modules/cell"; import { Cell, CellConstructorProps, CellStyles, Position, SerializableCell } from "./modules/cell";
import { CellChangeEvent, CellClickEvent, Config, CopyEvent, SelectionChangeEvent, ViewProperties } from "./modules/config"; import { Config, ViewProperties } from "./modules/config";
import { RangeSelectionType, Selection } from "./modules/selection"; import { RangeSelectionType, Selection } from "./modules/selection";
import { Styles } from "./modules/styles"; import { Styles } from "./modules/styles";
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";
import { Events } from "./modules/events"; interface SpreadsheetConstructorProperties {
import { Clipboard } from "./modules/clipboard"; config?: Omit<Config, 'view'>;
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 rowsBar; private header;
private columnsBar;
private sheet; private sheet;
private editor; private editor;
styles: Styles; styles: Styles;
@ -29,12 +23,7 @@ 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.
@ -49,34 +38,28 @@ 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">>, enableCallback?: boolean): void; changeCellValues(position: Position, values: Partial<Omit<CellConstructorProps, 'position'>>): void;
changeCellStyles(position: Position, styles: CellStyles): void; changeCellStyles(position: Position, styles: CellStyles): void;
applyActionToRange(range: RangeSelectionType, callback: (cell: Cell) => void): void; applyActionToRange(range: RangeSelectionType, callback: (cell: Cell) => any): 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';

719
dist/main.js vendored

File diff suppressed because it is too large Load Diff

2
dist/main.js.map vendored

File diff suppressed because one or more lines are too long

View File

@ -41,14 +41,15 @@ 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 {};

View File

@ -1,9 +0,0 @@
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;
}

View File

@ -1,15 +1,9 @@
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.
* *
@ -20,10 +14,6 @@ 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[];
@ -33,9 +23,5 @@ 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);
} }

View File

@ -1,39 +0,0 @@
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;
}

2
dist/style.css vendored
View File

@ -1 +1 @@
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} 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}

View File

@ -1,3 +0,0 @@
import { BaseSelectionType, RangeSelectionType } from "../main";
export declare function checkEqualRanges(range1: RangeSelectionType, range2: RangeSelectionType): boolean;
export declare function checkEqualCellSelections(selection1: BaseSelectionType, selection2: BaseSelectionType): boolean;

View File

@ -1,7 +1,7 @@
{ {
"name": "modern_spreadsheet", "name": "modern_spreadsheet",
"private": false, "private": false,
"version": "0.0.33", "version": "0.0.27",
"exports": { "exports": {
".": { ".": {
"import": "./dist/main.js", "import": "./dist/main.js",
@ -33,21 +33,18 @@
"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",
@ -56,10 +53,6 @@
"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"
} }
} }

View File

@ -4,11 +4,6 @@ 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
@ -22,9 +17,6 @@ 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
@ -52,9 +44,6 @@ 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:
@ -318,43 +307,6 @@ 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'}
@ -395,14 +347,6 @@ 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'}
@ -633,28 +577,15 @@ 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:
@ -662,12 +593,6 @@ 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'}
@ -675,22 +600,11 @@ 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'}
@ -699,12 +613,6 @@ 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'}
@ -720,13 +628,6 @@ 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'}
@ -738,19 +639,10 @@ 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
@ -759,23 +651,6 @@ 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'}
@ -785,21 +660,6 @@ 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'}
@ -830,66 +690,10 @@ 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'}
@ -1039,15 +843,6 @@ 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'}
@ -1080,12 +875,6 @@ 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'}
@ -1144,15 +933,6 @@ 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'}
@ -1267,25 +1047,6 @@ 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'}
@ -1358,17 +1119,6 @@ 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
@ -1390,18 +1140,6 @@ 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'}
@ -1428,12 +1166,6 @@ 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'}
@ -1467,13 +1199,6 @@ 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
@ -1492,31 +1217,11 @@ 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'}
@ -1573,13 +1278,6 @@ 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'}
@ -1587,13 +1285,6 @@ 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'}
@ -1618,10 +1309,6 @@ 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
@ -1691,15 +1378,6 @@ 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'}
@ -1793,18 +1471,6 @@ 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'}
@ -1836,17 +1502,6 @@ 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
@ -1901,37 +1556,12 @@ 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.

Before

Width:  |  Height:  |  Size: 36 KiB

View File

@ -1,122 +1,118 @@
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 = 35; public height: number = 32
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 {
const element = document.createElement("canvas");
element.style.position = "absolute";
element.style.height = this.height + "px";
element.style.width = this.root.viewProps.width + "px";
element.style.display = "block";
element.style.borderLeft = "1px solid black";
// element.style.boxSizing = 'border-box'
element.width = this.root.viewProps.width;
element.height = this.height;
return element;
}
public setElementPosition(top: number, left: number) {
this.element.style.top = top + "px";
this.element.style.left = left + "px";
}
private isColumnSelected(column: number): boolean {
const { selectedCell, selectedRange } = this.root.selection;
if (selectedCell && selectedCell.column === column) return true;
if (selectedRange) {
const inRange =
column >=
Math.min(selectedRange.from.column, selectedRange.to.column) &&
column <= Math.max(selectedRange.from.column, selectedRange.to.column);
return inRange;
} }
return false;
}
// private getYCoordWithOffset(renderBox: RenderBox): number { private createElement(): HTMLCanvasElement {
// const {y} = renderBox const element = document.createElement('canvas')
element.style.position = 'absolute'
element.style.height = this.height + 'px'
element.style.width = this.root.viewProps.width + 'px'
element.style.display = 'block'
element.style.borderLeft = '1px solid black'
// element.style.boxSizing = 'border-box'
// return y + this.root.toolbarHeight
// }
// private getXCoordWithOffset(renderBox: RenderBox): number { element.width = this.root.viewProps.width
// const {x} = renderBox element.height = this.height
return element
// return x
// }
private renderText(column: number, renderBox: RenderBox) {
const { width, x } = renderBox;
this.ctx.fillStyle = "black";
this.ctx.textAlign = "center";
this.ctx.textBaseline = "middle";
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,
);
}
private renderRect(column: number, renderBox: RenderBox) {
const { width, x } = renderBox;
const isColSelected = this.isColumnSelected(column);
this.ctx.fillStyle = isColSelected ? "#c7ebff" : "white";
this.ctx.strokeStyle = "black";
this.ctx.lineWidth = 1;
const specialX = x - this.root.viewport.left;
this.ctx.fillRect(specialX - 1, 0, width, this.height);
this.ctx.strokeRect(specialX - 1, 0, width, this.height);
}
private renderSingleColumn(column: number) {
const renderBox = new RenderBox(this.root.config, {
row: 0,
column: column,
});
this.renderRect(column, renderBox);
this.renderText(column, renderBox);
}
public renderBar() {
const lastColIdx = this.root.viewport.lastCol + 3;
const firstColIdx = this.root.viewport.firstCol;
this.ctx.beginPath();
this.ctx.strokeStyle = "black";
this.ctx.lineWidth = 1;
this.ctx.moveTo(0, 0);
this.ctx.lineTo(0, this.height);
this.ctx.closePath();
this.ctx.stroke();
for (let col = firstColIdx; col <= lastColIdx; col++) {
if (!this.root.config.columns[col]) break;
this.renderSingleColumn(col);
} }
}
} public setElementPosition(top: number, left: number) {
this.element.style.top = top + 'px'
this.element.style.left = left + 'px'
}
private isColumnSelected(column: number): boolean {
const { selectedCell, selectedRange } = this.root.selection
if (selectedCell && selectedCell.column === column) return true
if (selectedRange) {
const inRange =
column >= Math.min(selectedRange.from.column, selectedRange.to.column) &&
column <= Math.max(selectedRange.from.column, selectedRange.to.column)
return inRange
}
return false
}
// private getYCoordWithOffset(renderBox: RenderBox): number {
// const {y} = renderBox
// return y + this.root.toolbarHeight
// }
// private getXCoordWithOffset(renderBox: RenderBox): number {
// const {x} = renderBox
// return x
// }
private renderText(column: number, renderBox: RenderBox) {
const { width, x } = renderBox
this.ctx.fillStyle = 'black'
this.ctx.textAlign = 'center'
this.ctx.textBaseline = 'middle'
this.ctx.font = '16px Arial'
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) {
const { width, x } = renderBox
const isColSelected = this.isColumnSelected(column)
this.ctx.fillStyle = isColSelected ? this.root.styles.cells.selectedBackground : 'white'
this.ctx.strokeStyle = 'black'
this.ctx.lineWidth = 1
const specialX = x - this.root.viewport.left
this.ctx.fillRect(specialX - 1 , 0, width, this.height)
this.ctx.strokeRect(specialX - 1, 0, width, this.height)
}
private renderSingleColumn(column: number) {
const renderBox = new RenderBox(this.root.config, {
row: 0,
column: column
})
this.renderRect(column, renderBox)
this.renderText(column, renderBox)
}
public renderBar() {
const lastColIdx = this.root.viewport.lastCol + 3;
const firstColIdx = this.root.viewport.firstCol;
this.ctx.beginPath();
this.ctx.strokeStyle = 'black';
this.ctx.lineWidth = 1;
this.ctx.moveTo(0, 0);
this.ctx.lineTo(0, this.height);
this.ctx.closePath();
this.ctx.stroke();
for (let col = firstColIdx; col <= lastColIdx; col++) {
if(!this.root.config.columns[col]) break;
this.renderSingleColumn(col)
}
}
}

View File

@ -1,6 +1,5 @@
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 {
@ -29,10 +28,8 @@ 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 = this.element.style.top = y - this.root.viewport.top + this.root.columnsBarHeight + "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.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";
@ -53,20 +50,11 @@ export class Editor {
break; break;
} }
case "Enter": { case "Enter": {
if (!this.root.selection.selectedCell) return; this.root.changeCellValues(this.root.selection.selectedCell!, {
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();
} }
} }
}; };

View File

@ -1,109 +1,109 @@
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 = 35; width: number = 30
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() {
const element = document.createElement("canvas");
element.style.position = "absolute";
element.style.height = this.root.viewProps.height + "px";
element.style.width = this.width + "px";
element.style.display = "block";
element.style.borderTop = "1px solid black";
// element.style.boxSizing = 'border-box'
element.width = this.width;
element.height = this.root.viewProps.height;
return element;
}
public setElementPosition(top: number, left: number) {
this.element.style.top = top + "px";
this.element.style.left = left + "px";
}
private isRowSelected(row: number): boolean {
const { selectedCell, selectedRange } = this.root.selection;
if (selectedCell && selectedCell.row === row) return true;
if (selectedRange) {
const inRange =
row >= Math.min(selectedRange.from.row, selectedRange.to.row) &&
row <= Math.max(selectedRange.from.row, selectedRange.to.row);
return inRange;
} }
return false;
}
private renderText(row: number, renderBox: RenderBox) { private createElement() {
const { y, height } = renderBox; const element = document.createElement('canvas')
this.ctx.fillStyle = "black"; element.style.position = 'absolute'
this.ctx.textAlign = "center"; element.style.height = this.root.viewProps.height + 'px'
this.ctx.textBaseline = "middle"; element.style.width = this.width + 'px'
this.ctx.font = "12px Arial"; element.style.display = 'block'
this.ctx.fillText( element.style.borderTop = '1px solid black'
this.root.config.rows[row].title, // element.style.boxSizing = 'border-box'
this.width / 2,
y - this.root.viewport.top + height / 2,
);
}
private renderRect(column: number, renderBox: RenderBox) {
const { y, height } = renderBox;
const isRowSeleted = this.isRowSelected(column); element.width = this.width
element.height = this.root.viewProps.height
this.ctx.fillStyle = isRowSeleted ? "#c7ebff" : "white"; return element
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);
}
private renderSingleRow(row: number) {
const renderBox = new RenderBox(this.root.config, {
column: 0,
row: row,
});
this.renderRect(row, renderBox);
this.renderText(row, renderBox);
}
public renderBar() {
const lastRowIdx = this.root.viewport.lastRow + 3;
const firstRowIdx = this.root.viewport.firstRow;
this.ctx.beginPath();
this.ctx.moveTo(0, 0);
this.ctx.strokeStyle = "black";
this.ctx.lineWidth = 16;
this.ctx.lineTo(35, 0);
this.ctx.closePath();
this.ctx.stroke();
for (let row = firstRowIdx; row <= lastRowIdx; row++) {
if (!this.root.config.rows[row]) break;
this.renderSingleRow(row);
} }
}
} public setElementPosition(top: number, left: number) {
this.element.style.top = top + 'px'
this.element.style.left = left + 'px'
}
private isRowSelected(row: number): boolean {
const { selectedCell, selectedRange } = this.root.selection
if (selectedCell && selectedCell.row === row) return true
if (selectedRange) {
const inRange =
row >= Math.min(selectedRange.from.row, selectedRange.to.row) &&
row <= Math.max(selectedRange.from.row, selectedRange.to.row)
return inRange
}
return false
}
private renderText(row: number, renderBox: RenderBox) {
const { y, height } = renderBox
this.ctx.fillStyle = 'black'
this.ctx.textAlign = 'center'
this.ctx.textBaseline = 'middle'
this.ctx.font = '16px Arial'
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) {
const { y, height } = renderBox
const isRowSeleted = this.isRowSelected(column)
this.ctx.fillStyle = isRowSeleted ? this.root.styles.cells.selectedBackground : 'white'
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 )
}
private renderSingleRow(row: number) {
const renderBox = new RenderBox(this.root.config, {
column: 0,
row: row
})
this.renderRect(row, renderBox)
this.renderText(row, renderBox)
}
public renderBar() {
const lastRowIdx = this.root.viewport.lastRow + 3;
const firstRowIdx = this.root.viewport.firstRow;
this.ctx.beginPath();
this.ctx.moveTo(0, 0);
this.ctx.strokeStyle = 'black';
this.ctx.lineWidth = 16;
this.ctx.lineTo(35, 0);
this.ctx.closePath();
this.ctx.stroke();
for (let row = firstRowIdx; row <= lastRowIdx; row++) {
if(!this.root.config.rows[row]) break;
this.renderSingleRow(row)
}
}
}

View File

@ -1,6 +1,4 @@
import Spreadsheet, { CSS_PREFIX, Cell, Selection } from "../main"; import Spreadsheet, { CSS_PREFIX } from "../main";
import { EventTypes } from "../modules/events";
import { checkEqualCellSelections } from "../utils/position";
export interface ViewportRect { export interface ViewportRect {
top: number; top: number;
@ -14,6 +12,7 @@ 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) {
@ -26,8 +25,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
@ -40,61 +39,31 @@ 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;
this.root.selection.selectedRange.to,
lastSelectedCell,
);
if (isRangeChanged) {
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 (
checkEqualCellSelections( this.root.selection.selectedRange.from.row ===
this.root.selection.selectedRange.from, this.root.selection.selectedRange.to.row &&
this.root.selection.selectedRange.to, this.root.selection.selectedRange.from.column ===
) this.root.selection.selectedRange.to.column
) { ) {
newSelection.selectedRange = null; this.root.selection.selectedRange = null;
this.root.events.dispatch({
type: EventTypes.SELECTION_CHANGE,
selection: newSelection,
enableCallback: false,
});
} }
} }
@ -110,8 +79,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)
) { ) {
@ -123,8 +92,9 @@ 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;
} }
@ -132,10 +102,10 @@ export class Scroller {
if ( if (
this.root.selection.selectedCell && this.root.selection.selectedCell &&
this.root.selection.selectedCell.column < this.root.selection.selectedCell.column <
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;
} }
@ -145,7 +115,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;
} }
@ -153,23 +123,16 @@ export class Scroller {
if ( if (
this.root.selection.selectedCell && this.root.selection.selectedCell &&
this.root.selection.selectedCell.row < this.root.selection.selectedCell.row <
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());
@ -191,62 +154,32 @@ 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) => {
this.root.events.dispatch({ if (event.button !== 0) return; // Left mouse button
type: EventTypes.CELL_CLICK, const { offsetX, offsetY } = event;
event, const clickedCell = this.root.getCellByCoords(offsetX, offsetY);
scroller: this, this.isSelecting = true;
}); 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;
@ -282,7 +215,6 @@ export class Scroller {
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 };

View File

@ -1,4 +1,4 @@
import Spreadsheet, { CSS_PREFIX, RenderBox } from "../main"; import Spreadsheet, { CSS_PREFIX } 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,79 +52,12 @@ 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])
@ -133,6 +66,5 @@ export class Sheet {
this.renderCell({ column: col, row }); this.renderCell({ column: col, row });
} }
} }
this.renderSelection();
} }
} }

View File

@ -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");

View File

@ -1,21 +1,11 @@
import Spreadsheet, { SpreadsheetConstructorProperties } from "./main"; import Spreadsheet from "./main";
const options: SpreadsheetConstructorProperties = { const saveButton = document.querySelector("#save_button");
onCellClick: (event, cell) => { const loadButton = document.querySelector("#load_button");
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); if (!saveButton || !loadButton) throw new Error("LOST");
const sheet = new Spreadsheet("#spreadsheet");
function saveDataToLS() { function saveDataToLS() {
const serializableData = sheet.serializeData(); const serializableData = sheet.serializeData();
@ -29,10 +19,5 @@ 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);

View File

@ -10,14 +10,7 @@ import {
Position, Position,
SerializableCell, SerializableCell,
} from "./modules/cell"; } from "./modules/cell";
import { import { Config, ViewProperties } from "./modules/config";
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";
@ -28,9 +21,6 @@ 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
@ -44,12 +34,9 @@ import { FormulaParser } from "./modules/formulaParser";
</Table> </Table>
*/ */
export interface SpreadsheetConstructorProperties { 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_";
@ -58,8 +45,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;
@ -68,9 +55,6 @@ 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,
@ -87,13 +71,8 @@ export default class Spreadsheet {
this.config = new Config(config); this.config = new Config(config);
this.config.onCellClick = props?.onCellClick ?? null; this.rowsBar = new RowsBar(this)
this.config.onSelectonChange = props?.onSelectionChange ?? null; this.columnsBar = new ColumnsBar(this)
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);
@ -105,34 +84,32 @@ 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
this.columnsBar.setElementPosition(top, left); console.log(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 {
@ -165,21 +142,23 @@ 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);
@ -220,15 +199,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 */
@ -248,19 +227,10 @@ 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);
} }
@ -315,16 +285,12 @@ 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) {
@ -333,11 +299,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] ? data[0].length : 0; const colsLength = data[0] ? this.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++) {
@ -355,17 +321,11 @@ export default class Spreadsheet {
formattedData.push(innerRow); formattedData.push(innerRow);
} }
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.data = formattedData;
this.selection.selectedCell = null; this.selection.selectedCell = null;
this.selection.selectedRange = null; this.selection.selectedRange = null;
this.config = config this.config = this.makeConfigFromData(formattedData, this.config.view);
this.cache = this.getInitialCache(); this.cache = this.getInitialCache();
this.scroller.updateScrollerSize(); this.scroller.updateScrollerSize();
this.viewport = new Viewport( this.viewport = new Viewport(
@ -406,7 +366,6 @@ export default class Spreadsheet {
view, view,
rows, rows,
columns, columns,
onCellClick: null,
}); });
return config; return config;

View File

@ -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++) {
rowIdx = i if (y <= this.rows[i].yPos) {
if (y <= this.rows[i].yPos) { //* Intersection detect //* 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++) {
colIdx = i if (x <= this.columns[i].xPos) {
if (x <= this.columns[i].xPos) { //* Intersection detect //* Intersection detect
colIdx = i;
break; break;
} }
} }

View File

@ -1,5 +1,4 @@
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 = {
@ -70,9 +69,6 @@ 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;
@ -100,53 +96,50 @@ export class Cell {
Object.assign(this, values); Object.assign(this, values);
} }
evalFormula(parser: FormulaParser) { private isCellInRange(root: Spreadsheet): boolean {
if (this.value.substring(0, 1) !== '=') return; const { column, row } = this.position;
const { selectedRange } = root.selection;
this.resultValue = parser.parser.parse(this.value.slice(1), { if (!selectedRange) return false;
col: this.position.column,
row: this.position.row 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;
} }
// 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 = styles.background; ctx.fillStyle =
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 = styles.fontColor; ctx.fillStyle =
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";

View File

@ -1,106 +0,0 @@
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);
}
}
}
}

View File

@ -1,20 +1,10 @@
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.
@ -26,10 +16,6 @@ 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 = {
@ -44,20 +30,9 @@ 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;
} }
} }

View File

@ -1,142 +0,0 @@
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);
};
}

View File

@ -1,23 +0,0 @@
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
}
}

View File

@ -6,7 +6,7 @@ export class RenderBox {
y: number; y: number;
width: number; width: number;
height: number; height: number;
constructor(config: Config, cellPosition: Position) { constructor(config: Config, cellPosition: Position) {
this.x = this.getXCoord(cellPosition.column, config); this.x = this.getXCoord(cellPosition.column, config);
this.y = this.getYCoord(cellPosition.row, config); this.y = this.getYCoord(cellPosition.row, config);

View File

@ -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,7 +20,6 @@ $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;
} }

View File

@ -1,68 +0,0 @@
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
}

View File

@ -1,20 +0,0 @@
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
);
}

View File

@ -5,7 +5,7 @@
"types": ["vite/client", "node"], "types": ["vite/client", "node"],
"allowJs": false, "allowJs": false,
"skipLibCheck": true, "skipLibCheck": true,
"esModuleInterop": true, "esModuleInterop": false,
"allowSyntheticDefaultImports": true, "allowSyntheticDefaultImports": true,
"strict": true, "strict": true,
"forceConsistentCasingInFileNames": true, "forceConsistentCasingInFileNames": true,

View File

@ -2,16 +2,14 @@ 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";
const BROWSER_MODE = process.env.BUILD_BROWSER === 'true'; export default defineConfig({
console.log({ BROWSER_MODE });
const libConfig = defineConfig({
base: "/modern_spreadsheet/", base: "/modern_spreadsheet/",
plugins: [], plugins: [],
resolve: {}, resolve: {},
server: { server: {
port: 5179, port: 3000,
open: true, open: true,
}, },
build: { build: {
@ -39,23 +37,3 @@ const libConfig = 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;